Images
Generate images from a prompt with an image model.
Takes a model and a prompt and returns images, each data item carrying b64_json or a url. The format is OpenRouter's Images API, not OpenAI's images/generations. Send the key as Authorization: Bearer sk-df-... or x-api-key.
Find image models at /models?type=image.
Send a request
curl https://deference.si/v1/images \
-H "Authorization: Bearer $DEFERENCE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "recraft/recraft-v4.1-flash",
"prompt": "A lighthouse on a cliff at dawn, flat illustration",
"n": 1
}' | jq -r '.data[0].b64_json' | base64 --decode > lighthouse.pngimport base64
import os
import requests
response = requests.post(
"https://deference.si/v1/images",
headers={"Authorization": f"Bearer {os.environ['DEFERENCE_API_KEY']}"},
json={
"model": "recraft/recraft-v4.1-flash",
"prompt": "A lighthouse on a cliff at dawn, flat illustration",
"n": 1,
},
timeout=180,
)
response.raise_for_status()
for index, image in enumerate(response.json()["data"]):
with open(f"image-{index}.png", "wb") as file:
file.write(base64.b64decode(image["b64_json"]))import { writeFile } from "node:fs/promises";
const response = await fetch("https://deference.si/v1/images", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DEFERENCE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "recraft/recraft-v4.1-flash",
prompt: "A lighthouse on a cliff at dawn, flat illustration",
n: 1,
}),
signal: AbortSignal.timeout(180_000),
});
if (!response.ok) throw new Error(await response.text());
const { data } = await response.json();
for (const [index, image] of data.entries()) {
await writeFile(`image-${index}.png`, Buffer.from(image.b64_json, "base64"));
}Request body
- modelstringRequired
- An image model id.
- promptstringRequired
- What to draw.
- ninteger
- How many images, a whole number from 1 to 10. Defaults to 1. Any other value returns
400 invalid_parameter. - aspect_ratiostring
- For instance
16:9. Accepted values depend on the model. - resolutionstring
- For instance
1Kor2K, on models that offer sizes. - sizestring
- Pixel size, such as
1024x1024, on models that take one. - qualitystring
- Quality level, on models that offer it.
- output_formatstring
png,jpegorwebp, on models that offer a choice.- input_referencesarray
- Reference images, on models that accept them.
- streamboolean
- Return server-sent events. Only OpenAI image models stream.
Other fields pass through to the provider, and fields a model does not support are ignored. models, fallbacks, route and provider return 400 unsupported_parameter, as does a plugins entry that is not response-healing, a file-parser on cloudflare-ai or native, or context-compression with enabled: false. Deference replaces user with an account tag.
Response
{
"created": 1760000000,
"data": [{ "b64_json": "iVBORw0KGgo...", "media_type": "image/png" }],
"usage": {
"prompt_tokens": 0,
"completion_tokens": 0,
"total_tokens": 0,
"cost": 0.007
}
}Each data item carries b64_json or a url; b64_json is shortened here. media_type tells you the file type to save. usage.cost is the provider's cost in dollars, before OpenRouter's 5% platform fee. What you are charged is in Activity, and on a successful non-streamed response in x-deference-cost, in micro-dollars (1,000,000 is $1).
Streaming
On models that stream, stream: true returns image_ events as the image forms, then image_ with the final image and usage, then [DONE]. An error event can replace either.
Billing
- Each requested image holds the greatest of $0.20, the catalog's per-input-image price (
pricing.image), and 8,192 tokens at the model's highest image-output token price — a figureGET /v1/modelsdoes not return — plus the per-request price. If the hold does not fit, the request returns402: ask for fewer images or add credit. - A success is charged the provider's
usage.costplus OpenRouter's 5% platform fee. Deference adds no markup. That charge is final, because the response has no generation id. A success that reports no cost stays unresolved: nothing is charged yet, and the hold stays reserved. - A failed generation is charged the provider's reported cost plus OpenRouter's 5% platform fee. Deference adds no markup. Usually nothing is charged. A non-streamed timeout charges nothing yet and keeps its hold reserved, because the provider may still have generated the image.
- Free credit never pays for images. An account with only free credit gets
402 free_.credit_ not_ eligible
Limits
| Limit | Value | When exceeded |
|---|---|---|
| Request body | 64 MB | 413 request_too_large |
| Time to generate | 120 seconds once the request is forwarded, including streams | Non-streamed calls return 504 upstream_timeout and keep the hold reserved. Streamed timeouts follow the rules below |
| Images per request | 10 | 400 invalid_parameter |
Generation can take over a minute, so set a client timeout of at least 180 seconds.
A streamed timeout before response headers returns 504 upstream_timeout and costs zero. After headers, a timeout interrupts the HTTP 200 stream. A reported cost is charged plus OpenRouter's 5% platform fee. Deference adds no markup. Token counts with a generation id are a provisional estimate. Anything else does not charge the hold.
Also from chat
Some chat models, such as Gemini and GPT image models, generate images inside a chat reply. Send modalities: ["image", "text"] to Chat Completions. Image-only models, such as Recraft and FLUX, need this endpoint.