IMAGES
Create an Image
Generate one or more images from a text prompt. Synchronous — the images come back in the response, either inline as base64 or as authenticated URLs.
Authorization
AuthorizationstringheaderrequiredBearer token — your API key. Example: Bearer sk-...
Image generation is synchronous: there is no job to poll and no webhook to subscribe. The request stays open until the images are ready, which on the slower models can be a minute or more for a single image — set a generous client timeout rather than a default one.
Models
| Model | Endpoints | Size envelope | Grid | Default size | Max n | Reference images | Formats | Transparent |
|---|---|---|---|---|---|---|---|---|
seedream-4.0 | generate + edit | 921,600–16,777,216 px, up to 16:1 | — | 2048x2048 | 10 | 14 | jpeg | — |
seedream-4.5 | generate + edit | 3,686,400–16,777,216 px, up to 16:1 | — | 2048x2048 | 6 | 14 | jpeg | — |
seedream-5.0 | generate + edit | 3,686,400–16,777,216 px, up to 16:1 | — | 2048x2048 | 6 | 14 | jpeg, png | — |
seedream-5.0-pro | generate + edit | 921,600–4,624,220 px, up to 16:1 | — | 2048x2048 | 4 | 10 | jpeg, png | yes |
qwen-image-2.0 | generate + edit | 262,144–4,194,304 px, up to 4:1 | — | 2048x2048 | 6 | 3 | png | — |
qwen-image-3.0 | generate + edit | 262,144–6,553,600 px, up to 8:1 | — | model decides | 1 | 3 | png | — |
qwen-image-3.0-pro | generate + edit | 262,144–6,553,600 px, up to 8:1 | — | model decides | 1 | 3 | png | — |
qwen-image-edit-plus | edit | each side 512–2,048 | 16 px | model decides | 6 | 3 | png | — |
z-image-turbo | generate | 262,144–4,194,304 px, up to 4:1 | 16 px | 1536x1024 | 1 | — | png | — |
size takes any WIDTHxHEIGHT inside the model's envelope — the
envelope column is the whole surface, not a list of presets, and within it the
image arrives at exactly the size you name. Two carve-outs:
- A size below the model's minimum scales up. You get the smallest size
the model renders at your exact aspect ratio — never fewer pixels than you
asked for, and never a changed shape. This is how the OpenAI canonical sizes
work on
seedream-4.5andseedream-5.0, whose pixel floor sits above all three: ask for1024x1024and the delivered image is 2048x2048;720x1280delivers 1440x2560. qwen-image-edit-plusandz-image-turborender on a 16-pixel grid. Each side rounds to the nearest multiple of 16, ties to the even multiple —1000x700arrives as 992x704. Sides already divisible by 16 arrive exactly.
A size outside the envelope — over its pixel ceiling, or at an aspect ratio beyond its limit — is a 400 naming the envelope. A below-minimum size is never an error.
Every model additionally accepts size: "auto", and a model whose default
size reads model decides has no fixed output shape: on an edit it follows your
source image, and on a generation the model picks for itself.
The Transparent column marks the models that can render an alpha channel —
seedream-5.0-pro — and it is an edits-only capability. Transparency
needs something to be transparent around, so on this endpoint
background: "transparent" fails for every model without exception: we refuse
it outright on the models with no alpha support, and on the one that has it the
request is refused for having no source image. Either way you get a 400. On
edits, where it does work, it carries further
conditions worth reading before you rely on it.
The response carries the image, not a size field — measure the bytes if the exact shape matters to you.
Prompt handling
Your prompt is used exactly as you wrote it — no rewriting, no prefix, not even a trim, because leading and trailing whitespace is yours and models do respond to it. Prompts run to 32,000 characters, counted as JavaScript string length, so anything outside the basic multilingual plane — most emoji — counts as two.
seedream-4.0, seedream-4.5, seedream-5.0 and seedream-5.0-pro expand the prompt before rendering it, and there is
no switch to turn that off, so a terse prompt can produce a more elaborate image
than you specified. The
remaining models render what you wrote. Nothing in the response reports the
expanded text, so if reproducibility matters, prefer a model that does not
expand and write the detail yourself.
Delivery
response_format decides where the pixels go, and the two options have
different privacy properties:
"b64_json"(default) inlines the bytes in the JSON response. The image is not stored — no object, no row, no URL, and nothing to expire. Your request is still recorded in your usage history like any other, to the depth your organization's logging level sets."url"stores each image for 24 hours and returns an authenticated URL. The URL is not public: fetching it needs your API key, the same as any other endpoint. After 24 hours it returns 404. There is no delete endpoint — expiry is the only removal path.
Batching
n is bounded per model rather than at OpenAI's flat 10 — see the Max n
column. The ceilings are not one rule: some models have no documented n at
all and are pinned to 1, and on the slowest models a full fan-out would not
finish inside the request wall.
However it is served, n means n separate answers to your whole
prompt. A prompt asking for "six variations" still returns exactly the n
you requested, never a set the model decided on for itself, and never one
picture divided across several files.
Underneath, qwen-image-2.0 and qwen-image-edit-plus render the whole batch in one pass, while
the remaining models render n single images concurrently. Nothing in the
response distinguishes them, and the only place it surfaces is the bill: see
Billing for what n does to a per-source charge.
Unknown parameters are refused
We do not silently drop parameters we do not recognise. Any key outside the documented set is a 400 naming it —
unknown parameter 'foo' — so a typo fails loudly instead of appearing to
work while changing nothing.
Several parameters from OpenAI's images API are recognised and then refused by
name, because no model here implements them. The refusal says
which one and why, so you can tell whether to drop the parameter or change
model. quality is the one partial case — it is accepted, but only as
"auto":
| Parameter | Why |
|---|---|
style | No model here exposes a style control |
quality | Only "auto" is legal; no model here exposes a quality control |
output_compression | This surface never transcodes a delivered image |
input_fidelity | Not supported by any model here |
moderation | Content policy is always applied and is not configurable |
mask | No model here offers masked editing |
If you are porting OpenAI code, strip these before your first call.
Errors
Every failure is the same { "error": { "message", "type", … } } envelope.
Read the status and type together — the 5xx family all report api_error,
so the status is what separates a transient failure worth retrying from one
that is not.
| Status | type | What it means |
|---|---|---|
| 400 | invalid_request_error | A parameter is missing, unknown, or not valid for this model. param names it when one parameter is at fault; a refusal from the model itself carries a message but no param |
| 400 | image_generation_user_error | The prompt or a source image was refused by the model's content policy. code is moderation_blocked |
| 401 | authentication_error | Missing, malformed or unrecognised credential |
| 402 | insufficient_credit | Your organization is out of credit |
| 403 | permission_error | The key is inactive or expired, or the model is not enabled in your region |
| 413 | invalid_request_error | The request body is over the endpoint's size limit |
| 429 | rate_limit_error | Over your request rate, or the model is not priced on your tier or in your billing currency |
| 429 | quota_exceeded | Over a spend budget: the API key's, its budget for this model, your member budget or your organization's. code names which |
| 500 | api_error | Something on our side failed, including a gate that could not reach its own dependency and refused rather than guess |
| 502 | api_error | The model produced a response we could not interpret |
| 503 | api_error | The model is temporarily unavailable or over capacity — retry with backoff |
| 504 | api_error | The model did not finish in time |
Every response carries X-RateLimit-Limit-RPM, -TPM and -TPD with
their -Remaining counterparts, which is where to look when a 429 is not
obviously yours.
Two of these are worth planning for specifically. Capacity pressure reaches you as a 503, never a 429 — a retry policy keyed only on 429 will not fire for it, and it is the most common transient failure on this surface. And a 429 is not always about rate: an unpriced model on your tier, or one with no price in your billing currency, is refused the same way.
Billing
Images are billed per delivered image. Most models carry a single rate for
every size they render; on seedream-5.0-pro and qwen-image-3.0-pro the rate steps up once the
delivered image's pixel count crosses the model's large-size threshold —
it is the delivered pixels that pick the rate, so naming a size pins it.
A model whose default reads model decides can come back at a shape you did
not pick, and you are billed for the shape that arrived. Your usage history
itemizes each request into per-meter line items, one per rate applied, so what
you were charged for is always visible there. Your tier's price list is the
authority on the rates themselves and on where each model's threshold sits.
seedream-5.0-pro, qwen-image-3.0 and qwen-image-3.0-pro additionally bill per source image on an edit, at a
flat rate that does not vary with size. That line counts source images per
delivered image, not once per request — so on a model that accepts an n
above 1, three source images with n: 2 bills six rather than three. Where
the ceiling is 1 the two numbers are the same and the distinction never
appears.
A request that returns no images is not billed at all, and a partly successful batch is billed only for the images it delivered — source images included.
Images carry no token meter. They report zero tokens and add nothing to your tokens-per-minute or tokens-per-day counters. They are still checked against those limits, though: an image request refused with a token-limit 429 is being turned away on allowance your other traffic has already spent, not on anything the image itself consumed.
Request format
application/json is the normal form and what every example here uses. The
body is capped at 1 MB, which a 32,000-character prompt sits far inside;
over that is a 413.
multipart/form-data is also accepted with the same field names, worth
knowing only if you already have a form encoder to hand. Form fields arrive as
text, so n is read back as the number it spells — but that widening belongs
to the form transport alone: {"n": "2"} in a JSON body is still a 400.
There are no file parts to send here. A request carrying an image part
belongs on edits, and one sent here is refused as
an unknown parameter.
A model that only edits — the table's Endpoints column says which — is a
400 on this endpoint naming model, not a silent redirect.
Request body
multipart/form-datamodelstringrequiredThe image model to use (e.g. "seedream-4.0"). See the model table below for sizes, batch limits and formats.
promptstringrequiredText description of the image to generate (1–32,000 characters). Used verbatim — never rewritten, prefixed or trimmed. Some models expand it before rendering; see Prompt handling below.
nintegerHow many images to generate (defaults to 1). Each model has its own ceiling — see the `Max n` column in the model table; asking for more is a 400 rather than a silent truncation. Every image answers your whole prompt: `n` asks for variations, never for one picture split across several files.
sizestringOutput size as WIDTHxHEIGHT — any size inside the model's envelope (see the model table), not an enum. A size below the model's minimum delivers the smallest size it renders at your exact aspect ratio, and the grid models round each side to the nearest multiple of 16; everything else arrives at exactly the size you name. Omitting it, or sending `"auto"`, falls back to that model's `Default size` — a fixed shape on most models, and genuinely the model's own choice only where the table reads *model decides*.
response_format"b64_json" | "url"How to deliver the images. `"b64_json"` (the default) inlines the bytes in the response and stores nothing. `"url"` stores each image for 24 hours and returns an authenticated URL.
b64_jsonurloutput_formatstringDelivered image format, from the model's supported list (see the model table). Omit to let the model use its native format — this surface never transcodes, so an unsupported value is a 400 rather than a conversion.
background"transparent" | "opaque" | "auto"Background handling. On this endpoint only `"auto"` (the default) and `"opaque"` are usable, and both are no-ops — a text-only render has no background to control. `"transparent"` requires a source image and therefore belongs on [edits](/api-reference/images-edits); sending it here is always a 400.
transparentopaqueautoquality"auto"Accepted for OpenAI compatibility, and only as `"auto"` — no model here exposes a quality control, so any other value is a named 400 rather than a silently ignored parameter.
userstringAn opaque identifier for your end user. Recorded in your logs for correlation and never passed to the model.
Response
createdintegerrequiredUnix timestamp of when the images were returned.
dataobject[]requiredOne entry per image the model actually delivered, in delivery order. A partly-successful batch returns only its survivors, with a `200` and no error field — compare `data.length` against the `n` you asked for. You are billed for only the images you received.
Request
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "your-scx-api-key",
baseURL: "https://api.scx.ai/v1",
});
const result = await client.images.generate({
model: "seedream-4.0",
prompt: "A red kite over turquoise waves at golden hour, cinematic",
size: "1024x1024",
});
// Default delivery is base64 — write the first image to disk.
const bytes = Buffer.from(result.data[0].b64_json, "base64");
await Bun.write("kite.jpg", bytes);import OpenAI from "openai";
const client = new OpenAI({
apiKey: "your-scx-api-key",
baseURL: "https://api.scx.ai/v1",
});
const result = await client.images.generate({
model: "seedream-4.0",
prompt: "A red kite over turquoise waves at golden hour, cinematic",
size: "1024x1024",
});
// Default delivery is base64 — write the first image to disk.
const bytes = Buffer.from(result.data[0].b64_json, "base64");
await Bun.write("kite.jpg", bytes);Response
{
"created": 1755820800,
"data": [
{
"b64_json": "iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAA...(truncated)"
}
]
}{
"created": 1755820800,
"data": [
{
"b64_json": "iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAA...(truncated)"
}
]
}