---
title: "Create image"
description: "Generate images (OpenAI Images protocol)."
canonical: "https://pirouter.ai/docs/api/images"
last-updated: "2026-10-13"
lang: "en"
---

# Create image

Generate images (OpenAI Images protocol).

`POST https://api.pirouter.ai/v1/images/generations`

OpenAI-compatible image generation. Image models are served on this endpoint and on the Gemini `generateContent` endpoint, and on no chat endpoint: calling one there is `validation_failed` (422), with a message listing the endpoints the model is served on. The endpoint does not stream. Only the fields listed in the request schema are read by the platform; every other field (`size`, `n`, ...) is forwarded to the upstream provider as-is.

## Authorization

Send your API key in one of these headers.

| Header | Value | Description |
| --- | --- | --- |
| `Authorization` | `Bearer sk-pi-…` | Platform-issued API key with the `sk-pi-` prefix, sent as a Bearer token. |
| `X-Api-Key` | `sk-pi-…` | The same platform-issued `sk-pi-` key, sent in `X-Api-Key`. Read only when `Authorization` is absent or empty; when both are sent, `Authorization` wins. A bare key without the `Bearer ` prefix is accepted in either header. |

## Body

`application/json`

OpenAI Images generation request. Only the fields the platform reads or requires are listed; the rest of the protocol's fields are forwarded upstream unchanged.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | Model slug in "vendor/model" form. Must be an image model registered for this endpoint. |
| `prompt` | string | Yes | Text description of the image to generate. Forwarded unchanged. |

## Response

`200` `application/json`

The generated images, in the shape the OpenAI Images protocol defines; the body is the upstream's, passed through.

## Errors

| Status | Code | Description |
| --- | --- | --- |
| `400` | `invalid_request` | The request is missing a required field (`invalid_request`): a body with neither `model` nor `models` answers `model is required`. |
| `401` | `invalid_api_key`, `api_key_not_found`, `api_key_revoked`, `api_key_expired` | API key is missing or malformed (`invalid_api_key`), unknown (`api_key_not_found`), revoked (`api_key_revoked`), or expired (`api_key_expired`). |
| `402` | `insufficient_balance` | Wallet balance cannot cover the requested call (`insufficient_balance`). |
| `404` | `model_not_found`, `no_eligible_provider` | Requested model slug is unknown or not enabled (`model_not_found`), or the request's `provider` preferences, merged with the workspace routing settings, leave no provider that serves it (`no_eligible_provider`, with `error.metadata` carrying the routing funnel). |
| `422` | `validation_failed` | The request failed validation (`validation_failed`): the model is not served on this endpoint (the message lists the endpoints it is served on), or a routing field (`provider`, `models`, `route`, `preset`) breaks its rules. The message names the cause. |
| `429` | `rate_limited` | Request rejected by rate limiting (`rate_limited`). |
| `500` | `internal_error` | Other failures: `provider_unavailable` (503) when no healthy upstream can serve the request, `internal_error` (500) for unexpected platform-side failures. |
| `503` | `provider_unavailable` | Other failures: `provider_unavailable` (503) when no healthy upstream can serve the request, `internal_error` (500) for unexpected platform-side failures. |

Error body

```json
{
  "error": {
    "message": "No such model. Model names are vendor-qualified (vendor/model), so a name copied from a provider's own docs needs its vendor prefix added. Call GET /v1/models for the slugs this gateway serves.",
    "type": "not_found_error",
    "code": "model_not_found"
  }
}
```

## Response headers

Each header is listed with the response statuses it is sent with.

| Header | Sent with | Description |
| --- | --- | --- |
| `X-Pi-Trace-Id` | `200`, `400`, `401`, `402`, `404`, `422`, `429`, `default` | Platform request id, minted by the gateway. Present on every response of the inference endpoints; quote it when reporting a problem. |
| `Access-Control-Allow-Origin` | `200`, `400`, `401`, `402`, `404`, `422`, `429`, `default` | Always `*`. Credentials are the API key header, never a cookie. |
| `Access-Control-Expose-Headers` | `200`, `400`, `401`, `402`, `404`, `422`, `429`, `default` | Lists every `X-Pi-*` response header plus `Retry-After` and `Content-Encoding`, so browser code can read them. |
| `X-Pi-Provider` | `200`, `default` | Provider that served this response, in the same vocabulary as `provider.order` and `GET /v1/models/{vendor}/{model}/endpoints`. Present only when the upstream provider answered (a completion or an upstream error); after a fallback it names the provider that took over. |
| `X-Pi-Model` | `200`, `default` | Model slug that served this response - the same value as the body's `model`. When a fallback model took over, it is that model. Present only when the upstream provider answered. |
| `X-Pi-Upstream-Model` | `200`, `default` | The full model id the upstream provider itself reported in its response, passed on verbatim (on a stream, from the first data event). Absent when the upstream response named no model; never filled from the platform's own records. |
| `X-Pi-Upstream-Request-Id` | `200`, `default` | The upstream provider's own request id, passed on verbatim, for looking the call up on the provider's side. Absent when the provider returned none. |

## Request samples

cURL

```bash
curl https://api.pirouter.ai/v1/images/generations \
  -H "Authorization: Bearer $PIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemini-3.1-flash-image",
    "prompt": "A shiba inu drinking coffee on the moon, film grain"
  }'
```

Python

```python
import os
import requests

response = requests.post(
    "https://api.pirouter.ai/v1/images/generations",
    headers={"Authorization": f"Bearer {os.environ['PIROUTER_API_KEY']}"},
    json={
        "model": "google/gemini-3.1-flash-image",
        "prompt": "A shiba inu drinking coffee on the moon, film grain",
    },
)
print(response.json())
```

TypeScript

```ts
const response = await fetch('https://api.pirouter.ai/v1/images/generations', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.PIROUTER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'google/gemini-3.1-flash-image',
    prompt: 'A shiba inu drinking coffee on the moon, film grain',
  }),
})
console.log(await response.json())
```

## Response sample

`200` `application/json`

```json
{
  "created": 1791795980,
  "data": [
    {
      "b64_json": "iVBORw0KGgoAAAANSUhEUg..."
    }
  ]
}
```
