---
title: "Images"
subtitle: "Generate and edit images with OpenAI, Gemini, Together, xAI and OpenRouter models through one API."
---

The gateway serves OpenAI's Images API. Call `/v1/images/generations` to draw an image from a prompt, or `/v1/images/edits` to change one you upload. The model routes to any provider that draws images, on your own key or Respan's credits, and each call is logged with its cost.

**Pick a model:** filter by output type `image` on the [model catalog](https://www.respan.ai/models), or read [Models](/docs/ai-gateway/models/overview) for how model ids work.

## Quickstart

<Tabs>
<Tab title="cURL">
```bash
curl https://gateway.respan.ai/v1/images/generations \
  -H "Authorization: Bearer $RESPAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-1",
    "prompt": "A lighthouse at dusk, watercolor",
    "size": "1024x1024"
  }'
```
</Tab>
<Tab title="Python">
```python
import base64
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://gateway.respan.ai/v1",
    api_key=os.environ["RESPAN_API_KEY"],
)

result = client.images.generate(
    model="gpt-image-1",
    prompt="A lighthouse at dusk, watercolor",
    size="1024x1024",
)

with open("lighthouse.png", "wb") as f:
    f.write(base64.b64decode(result.data[0].b64_json))
```
</Tab>
<Tab title="TypeScript">
```typescript
import fs from "node:fs";
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://gateway.respan.ai/v1",
  apiKey: process.env.RESPAN_API_KEY,
});

const result = await client.images.generate({
  model: "gpt-image-1",
  prompt: "A lighthouse at dusk, watercolor",
  size: "1024x1024",
});

fs.writeFileSync("lighthouse.png", Buffer.from(result.data[0].b64_json!, "base64"));
```
</Tab>
</Tabs>

## Options

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `model` | string | required | The image model, such as `gpt-image-1`. |
| `prompt` | string | required | What to draw, or what to change in an edit. |
| `n` | integer | `1` | How many images to make. |
| [`size`](#sizes) | string | provider default | Image size, such as `1024x1024`. |
| [`image`](#edit-an-image) | file | none | Edits only: the image to change, sent as multipart form data. |
| [`mask`](#edit-an-image) | file | none | Edits only: the area to change. Not taken by Gemini. |

Other OpenAI Images fields pass through when the target provider takes them.

## Edit an image

```python
result = client.images.edit(
    model="gpt-image-1",
    image=open("lighthouse.png", "rb"),
    prompt="Add a full moon above the lighthouse",
)
```

Edits are multipart, so put Respan parameters in the `X-Data-Respan-Params` header (see [Respan parameters](#respan-parameters)).

## Sizes

OpenAI, Together and xAI take `size` as is. Gemini draws only certain aspect ratios, so a size is mapped to the nearest one within 5%, or refused with `400`.

## Output

The response follows OpenAI's Images shape. Each image in `data` comes back as `b64_json` or as a `url`, depending on what the provider returns.

## Providers

| Provider | Generations | Edits |
| --- | --- | --- |
| OpenAI | Yes | Yes |
| Gemini | Yes | Yes, without masks |
| Together | Yes | Yes |
| xAI | Yes | Yes |
| OpenRouter | Yes | Yes |

{/* REVIEW: OpenRouter images landed Oct 28 (#11381); confirm it answers edits too. Azure, Bedrock (Stability), Novita, DeepInfra and EmpirioLabs image models are still not on gateway-next. */}

## Respan parameters

Respan parameters work here as on chat: `customer_identifier`, `metadata`, `thread_identifier`, `disable_log` and the rest. Put them in the JSON body (or `extra_body` in the OpenAI SDK) for generations, and in the `X-Data-Respan-Params` header (base64 JSON) for multipart edits. See [Respan parameters](/docs/ai-gateway/customers/respan-params).

Image requests are not redacted, so an organization with [PII redaction](/docs/ai-gateway/features/pii-redaction) on is refused.

## Pricing and logs

- Each call is one trace with one `image` row. The row keeps each image as a URL: the provider's, or a `data:` URL of its bytes. An edit's input image bytes are not kept.
- The row is priced from the provider's usage: image output at the model's image output rate, and an edit's input images at its image input rate where the catalog states one. OpenRouter and xAI report their own charge, which is used as is.
- Before a call on Respan's credits is sent, the gateway holds credits for the prompt plus 8,192 output tokens per image at the model's image rate.
- On Respan's credits, a model with no catalog price whose provider reports no cost is refused with `400`. A model priced at $0 is served free.
- An answer on Respan's credits that reports no billable usage is refused with `502 no_billable_usage` and charged nothing.
- A non-streamed image call runs to its end even if your client disconnects, so it is charged its reported usage.
- Image calls pass the same checks as chat (limit policies, RPM, key usage cap, customer budgets), with no TPM limit and no customer rate limit. See [Budgets and limits](/docs/ai-gateway/spend/budgets-and-limits).

## Next steps

- [Vision and files](/docs/ai-gateway/modalities/vision-and-files): send images to chat models instead.
- [Video](/docs/ai-gateway/modalities/video): generate video.
- [Logs](/docs/ai-gateway/observability/logs-and-traces): find each image row and its cost.
- [Pricing](/docs/ai-gateway/spend/credits-vs-byok): how media is priced.
