# Image generation

> Generate and edit images with the OpenAI-compatible /v1/images/generations and /v1/images/edits endpoints, billed per image, and send images into chat models for vision input.

MirAPI offers two OpenAI-compatible endpoints for working with images:

- `POST /v1/images/generations` — text-to-image.
- `POST /v1/images/edits` — image editing (send an image plus an edit instruction).

Both endpoints accept a trailing slash (`/v1/images/generations/`, `/v1/images/edits/`) and are billed **per generated image** — one charge per image in the response, never per token. They use standard `Authorization: Bearer` authentication; there is no separate Anthropic or Gemini image-generation endpoint.

:::note
The trailing-slash variants are the OpenAI-native formats: they take the standard OpenAI fields (`prompt`, `n`, `size`, …) instead of the `input.messages` shape, and the edits variant additionally accepts `multipart/form-data` with `image` and `mask`. The examples below use the primary endpoints.
:::

:::tip
**Pick the right model.** Image endpoints only work with models that support image generation. Replace `deepseek-chat` in the examples below with an image model from your catalogue — filter by output type in the console, or check the model card's capability flags.
:::

## Generate an image

`/v1/images/generations` takes a chat-style `input.messages` payload with a `parameters` object:

```bash
curl https://api.mirapi.ai/v1/images/generations \
  -H "Authorization: Bearer $MIRAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-chat",
    "input": {
      "messages": [
        {
          "role": "user",
          "content": [
            {"text": "A neon-lit street in the rain, cinematic"}
          ]
        }
      ]
    },
    "parameters": {
      "size": "1024*1024",
      "negative_prompt": "blurry, low quality",
      "prompt_extend": true,
      "watermark": false
    }
  }'
```

```python
import os
import requests

resp = requests.post(
    "https://api.mirapi.ai/v1/images/generations",
    headers={"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"},
    json={
        "model": "deepseek-chat",
        "input": {
            "messages": [
                {
                    "role": "user",
                    "content": [{"text": "A neon-lit street in the rain, cinematic"}],
                }
            ]
        },
        "parameters": {"size": "1024*1024", "prompt_extend": True, "watermark": False},
    },
)
print(resp.json()["data"][0])
```

## Edit an image

`/v1/images/edits` sends the source image and the instruction in the same message-style shape:

```bash
curl https://api.mirapi.ai/v1/images/edits \
  -H "Authorization: Bearer $MIRAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-chat",
    "input": {
      "messages": [
        {
          "role": "user",
          "content": [
            {"image": "https://example.com/photo.png"},
            {"text": "Turn the sky into a starry night"}
          ]
        }
      ]
    },
    "parameters": {"n": 2, "prompt_extend": true, "watermark": false}
  }'
```

The `image` field accepts a public HTTP(S) URL or a base64 data URI (`data:image/png;base64,…`).

## Parameters

| Parameter | Purpose |
|---|---|
| `model` | Model name exactly as shown in the catalogue |
| `input.messages[].content[].text` | The generation or edit instruction |
| `input.messages[].content[].image` | Source image for edits (URL or base64 data URI) |
| `parameters.size` | Output dimensions as a string, e.g. `1024*1024` |
| `parameters.negative_prompt` | Things to avoid in the output |
| `parameters.prompt_extend` | Let the model expand the prompt (`true` / `false`) |
| `parameters.watermark` | Request a visible watermark (`true` / `false`) |
| `parameters.n` | Number of images to return (where supported) |

Not every model accepts every parameter, and many models only support `n: 1` — check the model card in the catalogue for supported values.

## Retrieving the image

The response contains an array of images:

```json
{
  "created": 1780000000,
  "data": [
    {
      "url": "https://…",
      "b64_json": null,
      "revised_prompt": "…"
    }
  ]
}
```

- **`url`** — a temporary URL to download the image.
- **`b64_json`** — the image as a base64 string.
- **`revised_prompt`** — the prompt actually used when the model rewrites yours.

To save the image to disk, prefer `b64_json` (no second request) or download `url` immediately — it is temporary.

## Send images to chat models

Image **generation** is separate from **vision input**: to have a model *read* an image (caption it, answer questions, extract text), send it through a chat endpoint, where it is billed as **tokens**, not per image.

With the OpenAI-compatible protocol, add an `image_url` part to the `content` array:

```python
import os
from openai import OpenAI

client = OpenAI(base_url="https://api.mirapi.ai/v1", api_key=os.environ["MIRAPI_API_KEY"])

response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "What is in this photo?"},
            {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
        ],
    }],
)
print(response.choices[0].message.content)
```

The `url` can be an HTTP(S) URL or a base64 data URI. The same capability is available on the Anthropic (`/v1/messages`) and Gemini (`/v1beta/models/{model}:generateContent`) chat protocols — see [Vision inputs](/docs/guides/vision) for the full formats.

## Errors and billing

**Billing.** Generation and editing are charged per image, deducted from your prepaid balance. When the balance is exhausted, requests return `403`; topping up restores the same key immediately. Reconcile spend in the console billing log or via `GET /api/usage/token` (account summary) and `GET /api/log/token` (per-request detail).

**Errors.** The gateway returns standard status codes: `401` (fix your key), `403` (balance, model whitelist, quota, or IP allowlist), `413` (request too large), `429` (rate limited — back off exponentially with jitter; there is no `Retry-After` header), and `500` (upstream failure; idempotent requests are safe to retry). Error responses use the OpenAI envelope, and each message ends with a request ID — include it in any support ticket.

## Troubleshooting

- **Wrong model.** Image endpoints reject models without image-generation capability. Filter the catalogue by output type and use the model's exact `id`.
- **`403` on a valid key.** Check your balance first, then the model whitelist, quota, and IP allowlist on the key.
- **`url` no longer works.** Download it immediately after the request, or use `b64_json`.
- **`413` with base64.** Large images exceed the size limit — downscale before encoding.
- **`n` ignored or rejected.** Some models return a single image regardless; check the model card.

## Related links

- [Video generation](/docs/guides/video) — video output
- [Vision inputs](/docs/guides/vision) — images into chat models on all three protocols
- [Image generation tutorial](/docs/tutorials/image-generation) — a complete script
- [Models & pricing](/docs/models) — find image-capable models
- [Billing & top-ups](/docs/billing) — per-image and per-token pricing