# Images

> Generate images from a prompt with an image model.

**POST** `https://deference.si/v1/images`

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`](https://deference.si/models?type=image).

## Send a request

**curl**

```bash
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.png
```

**Python**

```python
import 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"]))
```

**TypeScript**

```typescript
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

* `model` (string, required): An image model id.
* `prompt` (string, required): What to draw.
* `n` (integer): How many images, a whole number from 1 to 10. Defaults to 1. Any other value returns `400 invalid_parameter`.
* `aspect_ratio` (string): For instance `16:9`. Accepted values depend on the model.
* `resolution` (string): For instance `1K` or `2K`, on models that offer sizes.
* `size` (string): Pixel size, such as `1024x1024`, on models that take one.
* `quality` (string): Quality level, on models that offer it.
* `output_format` (string): `png`, `jpeg` or `webp`, on models that offer a choice.
* `input_references` (array): Reference images, on models that accept them.
* `stream` (boolean): 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

```json
{
  "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](https://deference.si/docs/concepts/pricing-and-metering). 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_generation.partial_image` events as the image forms, then `image_generation.completed` 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 figure `GET /v1/models` does not return — plus the per-request price. If the hold does not fit, the request returns `402`: ask for fewer images or add credit.
* A success is charged the provider's `usage.cost` plus 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](https://deference.si/docs/api-reference/chat-completions). Image-only models, such as Recraft and FLUX, need this endpoint.
