# Image generation & editing

Synchronous image generation and editing in the OpenAI format. Each call
returns when the image is ready.

Two endpoints share the same models:

- `POST /v1/images/generations` — draw a new image from a prompt.
- `POST /v1/images/edits` — change an existing image, optionally through a mask.

## Generate an image

`POST /v1/images/generations`

```bash
curl "https://sharellm.net/v1/images/generations" \
  -H "Authorization: Bearer $SHARE_LLM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model-id>",
    "prompt": "a paper boat on a rainy street, cinematic",
    "n": 1,
    "size": "1024x1024"
  }'
```

### Request fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | yes | Image model ID from the market. |
| `prompt` | string | yes | What to draw. |
| `n` | integer | no | Number of images. The platform rejects counts above 128; individual models may accept fewer. |
| `size` | string | no | Output size as `WIDTHxHEIGHT`, for example `1024x1024` or `1024x1536`. Omitted means `auto`: the model picks the frame from the prompt. GPT image models take any compliant frame — see [GPT image sizes](#gpt-image-sizes). Models that bill by resolution class use it to pick the class. |
| `response_format` | string | no | `url` (default) returns a link; `b64_json` inlines the bytes. GPT image models always return `b64_json` and do not take this parameter. |

### Response

A URL-style model:

```json
{
  "created": 1789827220,
  "data": [{"url": "https://.../image.png"}],
  "usage": {"total_tokens": 0}
}
```

A GPT image model:

```json
{
  "created": 1789827220,
  "data": [{"b64_json": "iVBORw0KGgo..."}],
  "usage": {
    "input_tokens": 24,
    "input_tokens_details": {"text_tokens": 24},
    "output_tokens": 4160,
    "total_tokens": 4184
  }
}
```

## Edit an existing image

Editing sends the source image and the instruction to the same models over a
multipart request. GPT image models are edited by sending the image they should
change, not by describing it again from scratch.

`POST /v1/images/edits` with `Content-Type: multipart/form-data`

```bash
curl "https://sharellm.net/v1/images/edits" \
  -H "Authorization: Bearer $SHARE_LLM_TOKEN" \
  -F "model=gpt-image-2" \
  -F "image=@room.png" \
  -F "mask=@room-mask.png" \
  -F "prompt=replace the sofa with a green velvet one, keep the room unchanged" \
  -F "size=1024x1024" \
  -F "n=1"
```

Send several reference images by repeating the file field, or by naming the
fields `image[]`:

```bash
curl "https://sharellm.net/v1/images/edits" \
  -H "Authorization: Bearer $SHARE_LLM_TOKEN" \
  -F "model=gpt-image-2" \
  -F "image[]=@subject.png" \
  -F "image[]=@style.png" \
  -F "prompt=put the subject of the first image into the scene of the second" \
  -F "size=1536x1024"
```

### Request fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `image` | file | yes | Source image (PNG, JPEG or WebP). Repeat the field, or use `image[]` names, to send several references at once; GPT image models accept up to 16. |
| `mask` | file | no | PNG whose transparent pixels mark the area to repaint. It applies to the first source image and should share its dimensions. |
| `prompt` | string | yes | Instruction describing the change. |
| `model` | string | yes | Image model ID from the market, for example `gpt-image-2`. |
| `n` | integer | no | Number of edited images. Bounded like `n` on generation — at most 128, fewer on most models. |
| `size` | string | no | Output size: `1024x1024`, `1536x1024` or `1024x1536` on the common class, `auto` (the default) to let the model choose. The newest models also take any `WIDTHxHEIGHT` inside their limits — see [GPT image sizes](#gpt-image-sizes). |
| `quality` | string | no | `low`, `medium`, `high` or `auto`, on the values the chosen model renders. |
| `input_fidelity` | string | no | How strongly the source is preserved on the models that take it (`gpt-image-1.5`: `high` or `low`). Omit it for `gpt-image-2`, which does not accept the parameter. |
| `output_format` | string | no | `png` (default), `jpeg` or `webp` on GPT image models; a transparent background requires `png` or `webp`. |
| `background` | string | no | `transparent`, `opaque` or `auto`, on the models that render transparency. |
| `stream` | boolean | no | `true` emits partial images while the edit renders; the final event carries usage. |
| `partial_images` | integer | no | How many previews arrive while streaming (`0`–`3`). |

### Response

GPT image models always answer with base64 in `data[].b64_json`, and do not
return a URL or a revised prompt.

```json
{
  "created": 1789827220,
  "data": [{"b64_json": "iVBORw0KGgo..."}],
  "usage": {
    "input_tokens": 1520,
    "input_tokens_details": {"text_tokens": 24, "image_tokens": 1496},
    "output_tokens": 4160,
    "total_tokens": 5680
  }
}
```

With `"stream": true` the edits endpoint answers `text/event-stream`:
`image_edit.partial_image` events carry a `partial_image_index`, and the final
`image_edit.completed` event carries `usage` and the finished image.

## GPT image sizes

`gpt-image-2` and both `gpt-image-2.5` models (`sunburst` for quality, `flare`
for speed) accept any size, not only the three presets. `size` is a
`WIDTHxHEIGHT` string with a lower-case `x`; omit it — or send `auto` — and the
model picks the frame from the prompt. Frames are judged by the model, not by
this gateway: a JSON request that spells the separator with the multiplication
sign `×` is refused here, everything else is forwarded, so a frame the model
rejects comes back as the provider's own `400`.

A frame is accepted when all four limits hold:

| Rule | Limit |
| --- | --- |
| 16 px alignment | both edges are multiples of 16 |
| Long edge | at most 3840 px |
| Aspect ratio | between 1:3 and 3:1 (long side ÷ short side ≤ 3) |
| Total pixels | 655,360 to 8,294,400 |

Frames above `2560x1440` (3,686,400 pixels) are **experimental**: the call
succeeds, but quality and stability vary more between runs. `2560x1440` itself
is the reliable upper bound, and `3840x2160` (or `2160x3840` upright) is the
largest accepted frame.

`gpt-image-1`, `gpt-image-1.5` and `gpt-image-1-mini` are not in this class:
they take only `1024x1024`, `1536x1024`, `1024x1536` or `auto`.

### Sizes that need a nudge

The usual offenders are `1080`, `1350` and `630`, none of which is a multiple of
16. Round each edge to the nearest multiple of 16, then check the pixel range:

| Size you want | Send instead |
| --- | --- |
| `1920x1080` | `1920x1088` |
| `1080x1920` | `1088x1920` |
| `1080x1080` | `1088x1088` |
| `1080x1350` | `1088x1360` (exactly 4:5) |
| `1200x630` | `1200x640` |
| `2560x1080` | `2560x1088` |
| `4096x2160` | `3840x2160` (the long edge is capped) |
| `2480x3508` (A4 at 300 dpi) | `2416x3408` (8,233,728 pixels) |
| `512x512`, `768x768` | `816x816` (below the pixel floor otherwise) |

Sizes that already comply can be sent as they are, for example `1280x720`,
`1024x768`, `2048x2048`, `2560x1440` and `2880x2880`.

### The rendered frame can differ

Close to the cap the provider may answer a `3840x2160` request with
`3840x2161`, which is neither 16-aligned nor inside the pixel ceiling. Read the
frame from the response (`width` / `height`, or the PNG header) instead of
assuming the size you asked for, and crop or pad downstream when a layout needs
an exact grid.

## Which models take an edit

Editing is a per-model capability, not a property of the images endpoint: the
market lists generation and editing models side by side, and a model that
cannot take a source image refuses the edit instead of inventing one. The model
page in the Model Market is the authoritative list for the model you call.

| Family | Edit through | What to send |
| --- | --- | --- |
| GPT image (`gpt-image-2`, `gpt-image-2.5`, `gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`) | `POST /v1/images/edits` (multipart) | `image` (repeatable, up to 16), optional `mask`, `size`, `quality`, `output_format`. `gpt-image-2` does not take `input_fidelity`. |
| Gemini image (`gemini-3-pro-image`, `gemini-3.1-flash-image`, `gemini-nano-banana-2.1`) | `POST /v1/chat/completions` with an image part | A text part plus `image_url` parts (up to 14 references, model-dependent); the reply carries the image as a data URL in `message.content`. |
| Doubao Seedream (`doubao-seedream-4-*`, `-5-0-*`) | `/v1/images/edits` or `/v1/images/generations` | `image` or `image[]` (up to 14 references); `n` must be 1, groups via `sequential_image_generation`; no `mask`, no streaming. |
| Alibaba Wan / Qwen (`wan2.5-i2i-preview`, `wan2.6-image`, `wan2.7-image`, `wanx2.1-imageedit`, `qwen-image-edit*`, `qwen-image-2.0`/`3.0`) | `/v1/images/edits` or `/v1/images/generations` | `images` (1–9 inputs by model); `wanx2.1-imageedit` selects an operation with `function` and also takes `mask`. |
| xAI Grok Imagine (`grok-imagine-image*`) | generation only through this gateway | The edits endpoint is not wired to forward the source image, so only generation is served. |

These are the fields of the OpenAI surface. A model only accepts the subset it
implements: Seedream rejects `mask` and streaming, `wanx2.1-imageedit` rejects
`size`, and a model that takes no reference image refuses the request before
anything is charged.

### Gemini image models use chat

Gemini image models take the source image as a content part of a chat message.
The reply carries the edited image as a data URL in `message.content`; a request
in the native Gemini format (`parts` with `inline_data`) is forwarded unchanged
as well.

```bash
curl "https://sharellm.net/v1/chat/completions" \
  -H "Authorization: Bearer $SHARE_LLM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.1-flash-image",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "text", "text": "replace the sky with a sunset, keep the buildings"},
        {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
      ]
    }]
  }'
```

A successful reply looks like:

```json
{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "![image](data:image/png;base64,...)"
    }
  }]
}
```

### Doubao Seedream

Seedream models take reference images on either images endpoint. `n` must stay
1 — several images come from `sequential_image_generation` — `mask` and
streaming are refused, and `size` is one of the model presets (`1K`, `1.5K`,
`2K`, `3K`, `4K`) or a pixel pair inside its limits.

```bash
curl "https://sharellm.net/v1/images/edits" \
  -H "Authorization: Bearer $SHARE_LLM_TOKEN" \
  -F "model=doubao-seedream-5-0-pro-260628" \
  -F "image=@subject.png" \
  -F "image=@scene.png" \
  -F "prompt=put the subject into that scene, keep both unchanged" \
  -F "size=2K"
```

### Alibaba Wan and Qwen

Wan and Qwen image models take `images` (1–9 by model): the edit-only Qwen
models require at least one, the general models accept up to three, and
`wan2.7-image` up to nine. `wanx2.1-imageedit` selects its operation with
`function` (`description_edit`, `description_edit_with_mask`,
`stylization_all`, `expand`, `super_resolution` and the rest) and takes a mask
only with `description_edit_with_mask`.

```json
{
  "model": "wanx2.1-imageedit",
  "prompt": "turn the photo into a watercolour",
  "image": "https://.../room.png",
  "function": "description_edit",
  "n": 1
}
```

## Billing

A generation bills per call, or per output resolution class when the platform
prices the model that way. The class comes from the long edge of the requested
frame — up to 1536 px is 1K, up to 3072 px is 2K, anything larger is 4K — and,
when the provider reports the frame it rendered, the charge settles on that
frame. `auto`, or an omitted `size`, names no class: the request starts at the
flat per-request price and moves to the rendered class once the answer reports
one.

An edit bills the prompt, the input image tokens (cached image tokens at the
cached rate) and the output image tokens. The cost breakdown on a usage-log row
names each part, so an edit is comparable with a generation of the same size.

## Upstream support

An edit is served only by an upstream that implements `/v1/images/edits`. A
channel that reaches GPT image models through a generation-only tool refuses the
edit (a retryable `503` unsupported-capability error) instead of quietly drawing
a new image, so the request can fail over to a channel that serves the endpoint.
When no attempt succeeds, nothing is charged for an image that was not produced.

`input_fidelity` is a per-model parameter: send it only where the model page
names it. `gpt-image-2` does not accept it; `gpt-image-1.5` takes `high` or
`low`.