Edit images

Modifies input images according to a prompt, or generates a new image using one or more images as references. It is compatible with images.edit in the OpenAI Images API, and the request body can be written in two ways: multipart/form-data with the image files uploaded (what the official OpenAI SDKs send), or JSON with the images as Base64 in the body (a LYRouter extension). The GPT-Image and Gemini Image families support editing, and both accept either form; Seedream does not support this endpoint, so for Seedream image-to-image use the image parameter of Create images.

POST/v1/images/edits

Authentication

Send your API key in a request header:

Authorization: Bearer <API Key>

Bearer is case-sensitive. You can use x-api-key: <API Key> instead; when both are present, only Authorization is read. Create API keys on the workspace's API Keys page. Your API key is used only to authenticate with LYRouter and is never forwarded to the upstream provider. See Base URLs and authentication.

Headers

Header Required Description
Content-Type Yes multipart/form-data; boundary=... or application/json. The multipart boundary is generated automatically by curl -F or the SDKs; if you build the request yourself, do not send a bare multipart/form-data
X-Gateway-Strict-Parameters No When set to true (lowercase only), a request containing any parameter the gateway would drop returns 400 unsupported_parameters immediately, without calling the upstream and without charge. On this endpoint only Gemini drops parameters; GPT-Image parameters are all forwarded unchanged and are not affected by this header

Request body

Both forms use the same field names; pick whichever is convenient. GPT-Image and Gemini support both.

multipart/form-data

Field Type Required Description
model string Yes Model ID, such as openai/gpt-image-2. Only concrete model IDs work: a virtual model @vmodel/<name> or lyrouter/auto returns 403
image file Yes The image file to edit. On Gemini, repeat the image field to send several images; they are used in the order they appear
prompt string Yes Editing instruction
mask file No Mask image, used by GPT-Image; dropped for Gemini
quality string See description Used by GPT-Image and required for gpt-image-2; dropped for Gemini
n integer No Number of images, used by GPT-Image (up to 10); dropped for Gemini
size string No Output size; see the model sections for how to write it
  • GPT-Image: the gateway rewrites only the model field; files and other fields are forwarded to the upstream byte for byte.
  • Gemini: the gateway parses the form, converts the files to Base64 (the format comes from the part's Content-Type, or from the file header when there is none), then translates it the same way as the JSON form. All files together must not exceed 30 MB, or the request returns 400.

JSON

The official OpenAI /images/edits accepts only multipart; the JSON form is a LYRouter extension, with the images as Base64 in image.

Parameter Type Required Description
model string Yes Model ID, as above
prompt string Yes Editing instruction; must not be empty
image string | array Yes One or more input images, written as data:image/png;base64,<data>; use an array for several images. Gemini also accepts Base64 without the prefix; see below
quality, n, size and others — No Same as in the multipart form; see the model sections

How each model is handled

Model family Model IDs Handling Parameters dropped by the gateway
GPT-Image openai/gpt-image-2
openai/gpt-image-2.5-flare
openai/gpt-image-2.5-sunburst
Pass-through None; everything is forwarded unchanged
Gemini Image google/gemini-3-pro-image
google/gemini-3.1-flash-image
Contract translation Every parameter except model, prompt, image and size; size is also dropped when it cannot be matched

Dropped parameters are listed in the X-Gateway-Unsupported-Parameters response header and the request still runs; with X-Gateway-Strict-Parameters: true it returns 400 instead. For a comparison of model capabilities and use cases, see Multimodal capabilities.

GPT-Image (2 / 2.5-flare / 2.5-sunburst)

Pass-through: the body is forwarded unchanged with only the model name rewritten. Parameters match the OpenAI image API, and the result is always b64_json.

Parameter Type Description
quality string Values differ by model: gpt-image-2 takes low, medium, high or auto (auto behaves as low in measurements) and requires it, returning 400 intermittently when it is omitted (INVALID_REQUEST_BODY); the 2.5 family takes low, medium, high, xhigh, max or auto, defaults to low, and xhigh and max may be rejected upstream (if you get Invalid value: 'xhigh', use high). See Create images
size string widthxheight only; tier names are not accepted. Must meet four hard constraints: width and height both divisible by 16, longest edge no more than 3840, and between about 1 M and about 8.3 M pixels in total; see Create images. When it is omitted, the upstream picks the output size: in measurements, gpt-image-2 returned 1254x1254 for a 1024×1024 input (not divisible by 16, so requesting this size on the generation endpoint is rejected), and gpt-image-2.5-flare returned 1024x1024 for a 1536×1024 input. Pass size explicitly when you need a fixed size
mask file Mask image, uploaded in the multipart form
n, background, output_format, moderation, user — Same as in Create images
response_format, stream — Do not send them. On this endpoint the gateway does not drop them: response_format is forwarded to the upstream unchanged, and this family always returns b64_json; when stream is true, the gateway currently adds stream_options itself, and the upstream returns 400 Unknown parameter: 'stream_options'.
  • Latency: the two forms cost the same on the gateway side (about 0.1 s); nearly all the time is spent upstream, anywhere from 20 s to more than 4 minutes, and the same request can take several times longer on a second attempt. Set the client timeout to at least 300 s.
  • Image validation: the upstream validates the image itself and rejects images that are too small or corrupted (for example with Invalid image file or mode); retry with a normal image.

Billing: the same as for generation: per token, with input text, input image and output priced separately. Editing adds the cost of the input image: images are converted to tokens in 32×32-pixel blocks and reported in usage.input_tokens_details.image_tokens: an image whose long edge is under 1024 is first enlarged to twice its long edge (at most 1024), an image over about 1.52 million pixels is first scaled down proportionally, and the count is ceil(width/32) × ceil(height/32). Measured: 1024 tokens for a 1024×1024 image and 1 token for an 8×8 image. A mask is counted into image_tokens by the same rule: a 1024×1024 image plus a mask of the same size measured 2048 image_tokens. With the same quality and size, editing produces the same output tokens as generation; for how output tokens vary with quality, see Create images.

Gemini Image (3 Pro / 3.1 Flash)

Contract translation: the gateway translates the request into Gemini's native generateContent format, uses only the parameters marked as applied below, and drops the rest, listing them in the response header. The result is always b64_json.

Parameter Applied Description
prompt Applied Required; omitting it or sending it empty returns 400 prompt is required for this model
image Applied One or more images. In the JSON form, when the value has a data:<format>;base64, prefix, the declared format is used; without the prefix, the format is detected from the PNG, JPEG, WEBP or GIF file header; if neither works, the request returns 400 naming which image failed. Image URLs are not accepted
size Applied Same as for generation: a tier name (1K, 2K, 4K) controls the resolution, and the provider rejects the 512 tier in production, so do not use it; widthxheight matches the aspect ratio and picks a tier by the long edge, and when the ratio is more than 2% off, the whole size is dropped while the request still returns 200 and is billed. Output tokens per tier differ between the two models; see Create images
n, quality, mask, response_format, user and others Dropped —
  • In the multipart form, all files together must not exceed 30 MB. The official OpenAI SDKs' images.edit() always sends multipart and can be used with Gemini directly.

Billing: per token, with input, output and thinking priced separately. Input images count toward "input" (usage.input_tokens), and each input image counts as a fixed number of tokens regardless of its size: production measurements give 560 per image for gemini-3-pro-image and 1120 per image for gemini-3.1-flash-image, twice as many. For thinking tokens, see Create images.

Request examples

multipart/form-data

Using openai/gpt-image-2 as an example, upload cat.png and save the result to a file. The Python example needs a recent version of the openai SDK: with 1.58.1, images.edit() does not accept quality and raises TypeError: edit() got an unexpected keyword argument 'quality'.

curl https://api.lyrouter.com/v1/images/edits \
  --max-time 300 \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -F model=openai/gpt-image-2 \
  -F quality=medium \
  -F image=@cat.png \
  -F prompt="Replace the background with snowy mountains"

JSON

Using google/gemini-3-pro-image as an example, encode the image as Base64 and put it in the body:

printf '{"model":"google/gemini-3-pro-image","prompt":"Replace the background with snowy mountains","image":"data:image/png;base64,%s"}' \
  "$(base64 < cat.png | tr -d '\n')" > body.json
curl https://api.lyrouter.com/v1/images/edits \
  --max-time 300 \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @body.json

Response

The response has the same format as Create images: on success the images are in the top-level data array as b64_json; an error returns an error object. The 200 below is a GPT-Image edit (the multipart example above, without size); the 400 is Gemini receiving quality, which it does not support, with strict mode on; the 403 means the model is not enabled, or a virtual model or Auto was used.

{
  "created": 1791950400,
  "data": [
    { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA…" }
  ],
  "size": "1254x1254",
  "quality": "medium",
  "output_format": "png",
  "background": "opaque",
  "usage": {
    "input_tokens": 1036,
    "input_tokens_details": { "text_tokens": 12, "image_tokens": 1024 },
    "output_tokens": 2058,
    "total_tokens": 3094
  }
}
Field Description
created Unix timestamp (seconds). Not returned by Gemini
data Array of results; it is an array even for a single image
data[].b64_json The image encoded in Base64
size, quality, output_format, background The generation settings actually used (returned by GPT-Image)
usage Metering information. GPT-Image reports text and image tokens separately in input_tokens_details; for Gemini, input images count toward input_tokens, plus output_tokens_details.reasoning_tokens when there are thinking tokens. For each field, see Create images

Prices are those shown in the model marketplace in the console; the actual charge for each call is in the request logs.

Response headers

Header Description
X-Gateway-Unsupported-Parameters Present when the gateway dropped parameters (which happens only on Gemini); the value is a comma-separated list of parameter names, such as n,quality. The request still ran, and the listed parameters did not take effect

When quotas are configured, the response also carries the x-aigw-quota-* headers; see Gateway headers and extensions.

Gateway behavior

Compared with calling the upstream directly, calling through LYRouter differs in these ways:

  • Two request body forms: for GPT-Image, a multipart request has only its model field rewritten and the files are forwarded unchanged; a JSON request is forwarded unchanged. For Gemini, both forms are first normalized into the same set of fields and then translated into Gemini's format, so they behave identically.
  • Only the requested model is used: image endpoints never switch to another model. When the same model has several channels, the gateway tries the other channels in turn after a failure; virtual models, Auto smart routing and fallback to the workspace's Default Model do not apply to image endpoints. See Retries and fallback.
  • Dropped parameters and strict mode: only Gemini drops parameters, listing them in the X-Gateway-Unsupported-Parameters response header; with X-Gateway-Strict-Parameters: true the request returns 400 immediately instead, without calling the upstream and without charge. GPT-Image parameters are all forwarded unchanged, and the upstream decides whether to accept them.
  • Errors declared in a successful upstream response: when Gemini's upstream rejects a request, it may return HTTP 200 with no image. The gateway rewrites this into an error response: when the upstream rejected the request, error.code is upstream_rejected and error.message is the upstream's original text; when no image was produced, the code is upstream_no_artifact with upstream_finish_reason (such as NO_IMAGE or SAFETY). upstream_status keeps the upstream's original 200. These errors are not retried on another channel; change the prompt or image as indicated.
  • Upstream errors are returned unchanged: parameter and image errors for GPT-Image come from the upstream, with the upstream's format and error codes. When the upstream returns an error status with no body, the gateway fills in an error body with upstream_status and generated_by: "ai-gateway", plus upstream_request_id when the upstream supplied a request ID; give that ID to the provider when reporting an issue.
  • Images are not stored in logs: Base64 images in requests and responses are replaced in the logs with a [base64 <bytes>B #<CRC32>] summary. Not seeing the image in the log details does not mean the request failed; the image is in the response body.
  • Timeouts: editing is synchronous too, and the GPT-Image family often takes several minutes. Set the client timeout to at least 300 s; after a client timeout, do not resubmit right away; first check the result of the previous request in the request logs.

Common errors

Errors returned by the gateway take the form {"error":{"message":"...","type":"...","code":"..."}}; parameter errors for GPT-Image come from the upstream in the upstream's format.

Status code error.message error.code Cause and fix
400 unsupported parameters for this model: <names> unsupported_parameters X-Gateway-Strict-Parameters: true was sent and Gemini received parameters it does not support. Remove the listed parameters
400 prompt is required for this model invalid_parameter No prompt, or an empty one, was sent to Gemini
400 image[<index>]: cannot determine the image MIME type; send a data URI such as `data:image/png;base64,<payload>` invalid_parameter Gemini could not recognize the format of the numbered image, for example because an image URL was sent. Send it as a data URI
400 inline file parts exceed the limit of 31457280 bytes invalid_parameter The files in a multipart request to Gemini exceed 30 MB in total. Compress the images and retry
400 you must provide a model parameter missing_model No model was given (a multipart request without a model field counts too) and the workspace has no Default Model. Always specify the model ID on image endpoints
400 This model does not support the images_edits operation unsupported_model_operation The selected model does not support image editing. Use GPT-Image or Gemini; for Seedream image-to-image, use Create images
400 This model does not support the images API unsupported_model_api The interfaces the model declares (api_types in List models) do not include the image endpoints, for example a chat model used on this endpoint. A chat model that declares no api_types does not trigger this error: the request is forwarded upstream and may fail with an upstream error such as 404 upstream_not_found. Use only image models on image endpoints
400 The upstream's text, such as Request contains an invalid argument. upstream_rejected Gemini's upstream rejected the request; fix the parameters or image as the text says
400 upstream produced no image (finishReason: <reason>) upstream_no_artifact Gemini produced no image. Change the prompt or image according to upstream_finish_reason and retry
400 The upstream's text, such as Invalid size '<width>x<height>'. Width and height must both be divisible by 16. or Invalid size '<width>x<height>'. Requested resolution is below the current minimum pixel budget. — The GPT-Image size breaks one of the four hard constraints. Adjust it against the constraint table in Create images
400 The upstream's text INVALID_REQUEST_BODY The GPT-Image request body is invalid; first check that quality is set: omitting it on gpt-image-2 returns 400 intermittently
403 Model is not available in the model plaza model_not_available The workspace has not enabled this model, or @vmodel/<name> or lyrouter/auto was used. Ask an administrator to enable it in the model marketplace, or use a concrete model ID that is enabled