Images
llm.buzz serves image generation models on OpenRouter's Image API, POST /api/v1/images, and on the OpenAI Images API, POST /api/v1/images/generations, so the OpenRouter and OpenAI SDKs reach them with the base URL alone. An answer is never streamed, and every image comes back as base64 in b64_json.
Both routes serve the same models, each with the same sizes and settings, and take your key as a bearer token (Authentication). Chat Completions, Responses and Messages refuse an image model with a 400 on model that names POST /api/v1/images. A chat model that draws returns its images in a Chat Completions answer instead, as the API reference shows; Responses and Messages refuse such a model.
Find an image model
GET https://api.llm.buzz/api/v1/models?output_modalities=image lists the models that make images: the image generation models these routes serve, and chat models that return images in a chat answer. An image generation model lists as text->image, with a context_length of 32,000, the most characters its prompt takes. The Models page's Output filter picks out the models that make images, and an image model's page shows its price: per image, or per million input and output tokens.
In this list, pricing.completion of an image model is "0", and its image price is in other keys. A model priced per token lists its input price as pricing.prompt and its output price as pricing.image_output, in US dollars per token. A model priced per image lists pricing.image_token and pricing.image_output, its price per image token; the price of one image is the cost_usd of its endpoints listing, below.
With your key, GET https://api.llm.buzz/api/v1/images/models lists exactly the image models your key can call, in OpenRouter's image model shape. Each one's supported_parameters names every field the model takes with its values: its sizes, aspect ratios, resolutions, qualities, backgrounds and formats, and the range of n up to its most images per request. size lists the model's sizes, where OpenRouter lists a boolean.
curl https://api.llm.buzz/api/v1/images/models \
-H "Authorization: Bearer $LLM_BUZZ_KEY"GET https://api.llm.buzz/api/v1/images/models/{author}/{slug}/endpoints, or /images/models/{name}/endpoints for a name with no /, lists the providers that may serve you the model, each with its provider_tag, its supported_parameters and its pricing at your rate. A model priced per image has one line, the price of one image:
{"billable": "output_image", "unit": "image", "cost_usd": 0.03}A model priced per token has an input_text and an output_image line, in US dollars per token. A model that makes no images answers 404, naming GET /api/v1/images/models. Both image model routes need a key: without one, they answer 401.
Generate an image
curl https://api.llm.buzz/api/v1/images \
-H "Authorization: Bearer $LLM_BUZZ_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "qwen/qwen-image-3", "prompt": "A line drawing of a beehive", "aspect_ratio": "16:9"}'With the OpenAI SDK, call the OpenAI route. model and prompt are enough: the model's first size is its default.
import base64
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.llm.buzz/api/v1",
api_key=os.environ["LLM_BUZZ_KEY"],
)
result = client.images.generate(
model="qwen/qwen-image-3",
prompt="A line drawing of a beehive",
)
# PNG, JPEG or WebP: output_format names which when the answer carries it; name the file to match.
with open("beehive.png", "wb") as file:
file.write(base64.b64decode(result.data[0].b64_json))The fields of POST https://api.llm.buzz/api/v1/images:
| Field | Rule |
|---|---|
model | Required: an image model. Another model is a 400 on model that names GET /api/v1/images/models and the route you called. :floor and :nitro choose the provider order as on chat. |
prompt | Required: 1 to 32,000 characters. |
n | 1 to 10 images, 1 when omitted, and at most the model's most images per request. |
size | One of the model's sizes, WIDTHxHEIGHT or auto, or a resolution tier such as 1K or 2K, read as resolution. |
aspect_ratio, resolution | Choose one of the model's sizes, as below. |
quality, background, output_format | One of the values the model takes. A model that takes none ignores the field and never sends it. output_format is png, jpeg or webp. |
output_compression (0 to 100), seed | Sent only to a model that takes them, else ignored. |
provider | Steers the request as on chat: order, only, ignore, allow_fallbacks, sort, zdr and data_collection; see Provider preferences. |
user, session_id, trace | Accepted. user is recorded as the end-user label; none of them is sent to the provider. |
POST https://api.llm.buzz/api/v1/images/generations takes the OpenAI SDK's fields under the same rules: model, prompt, n, size, quality, background, output_format, output_compression, moderation, user, and provider for routing. moderation is sent only to a model that takes it, and style is accepted and never sent. The OpenAI SDK's standard and hd qualities match none of a model's qualities, so send one that GET /api/v1/images/models lists. Both routes drop fields they do not know.
How a size is chosen
A request resolves to exactly one of the model's sizes:
- An explicit
sizemust be one of the model's sizes, and anaspect_ratioorresolutionbeside it must be that size's own. - Otherwise
resolutionkeeps the sizes of that tier, andaspect_ratiopicks the first of them with that ratio (autopicks the model'sautosize, else its first). - With neither, the model's first size, its default.
A size, ratio or tier the model does not take is a 400 that names the field and the values it takes, such as "qwen/qwen-image-3 serves aspect_ratio 1:1, 4:3, 3:4, 16:9, 9:16, 3:2, 2:3; resend with one of them." Only what the model takes reaches the provider: model, prompt, n, the size (left out for auto), and the quality, background, output_format, output_compression, seed and moderation the model takes.
The answer
{"created": 1792900000, "data": [{"b64_json": "iVBORw0...", "media_type": "image/png"}],
"usage": {"prompt_tokens": 0, "completion_tokens": 4175, "total_tokens": 4175, "cost": 0.03}}Every image is PNG, JPEG or WebP, in base64 in b64_json, and media_type names its format from its bytes. usage.cost is what the call billed, in US dollars at your rate. A model priced per image counts each image as a fixed number of output tokens, so its completion_tokens is that count per image and its prompt_tokens is 0: 4,175 tokens an image on most models, the count OpenRouter's Image API reports for one image, and 1,120 on the Gemini image models. The OpenAI route answers in OpenAI's shape: data with b64_json and any revised_prompt, the background, output_format, quality and size the provider reports, and usage in OpenAI's names (input_tokens, output_tokens, total_tokens, input_tokens_details and cost). Both carry an X-Generation-Id header, which names the call's generation record: GET /api/v1/generation?id= answers what it cost (Cost and the generation record).
A provider may return fewer images than n, as on OpenRouter: you get and pay for the images returned. A high-quality image can take minutes, and each provider gets at least 300 seconds to answer, so give your client a long timeout.
A thinking image model, such as google/gemini-3-pro-image, can draw up to two interim images before its final one, and a provider may send them ahead of it. llm.buzz then serves the last n images of the answer, and bills only those.
Refusals
Each is a 400 before any provider is called, with error.metadata.param naming the field, and bills nothing:
| Request | Answer |
|---|---|
| A chat or embeddings model on either image route | unsupported_capability on model, naming the route you called: "The model 'deepseek/deepseek-v4.1-flash' does not generate images. Choose an image model from GET /api/v1/images/models and resend the request to POST /api/v1/images." |
| An image model on Chat Completions, Responses or Messages | model: "The model 'qwen/qwen-image-3' generates images and serves no chat request; send it to POST /api/v1/images." |
A size, aspect_ratio or resolution the model does not take | The field and the values the model takes. |
A quality, background or output_format value outside the ones the model takes | The field and the values the model takes. A model that takes none of a field ignores it. |
n above the model's most images | n, with the most: "qwen/qwen-image-3 makes at most 6 images per request; resend with n at most 6." |
input_references, stream: true, output_format: "svg", response_format: "url" | The field and what to send: a text prompt only, no stream, png, jpeg or webp, and no response_format, since every image is b64_json. |
Billing
An image model bills in one of two ways, which its price shows:
- Per image: each image the call returns, at the model's price per image and your rate. The model's pages show the price of one image, such as "$0.03/image".
- Per token: the input and output tokens the provider reports, at the model's input and output prices per million tokens and your rate.
Your credit, budgets and key limits apply as on chat; Errors lists the 402 and 403 answers.
- A call that fails over to another provider bills only the provider that answered.
- A call bills nothing when it is refused, when its provider answered an error (a refusal of your prompt by the provider's moderation is a 400 you receive), when its provider never received the request, or when what the provider answered cannot be delivered, such as no image, more images than you asked for (apart from a thinking model's interim images, above), bytes that are not a PNG, JPEG or WebP image, an image over 16 MiB, or an answer over 48 MiB. Such an answer moves the call to the next provider.
- A timeout, a network fault after the request was sent, or your client disconnecting bills what llm.buzz reserved for the call, since the provider may have made and billed images nobody received: for a model priced per image, the
nimages you asked for; for a model priced per token, the most your prompt andnimages can cost. A client that gives up on a slow image can be charged. - Neither route reads an
Idempotency-Key: a retry is a new request, served and billed again.
Not supported
Image-to-image (input_references), image edits and variations, streaming partial images, SVG output, image URLs in the answer (every image is b64_json), separate prices per resolution or megapixel (a model's sizes share one price per image), the image model routes without a key, and image models in the console's Playground.