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.

POST/v1/images/generations

Authorization

Authorizationstringheaderrequired

Bearer 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

ModelEndpointsSize envelopeGridDefault sizeMax nReference imagesFormatsTransparent
seedream-4.0generate + edit921,600–16,777,216 px, up to 16:1—2048x20481014jpeg—
seedream-4.5generate + edit3,686,400–16,777,216 px, up to 16:1—2048x2048614jpeg—
seedream-5.0generate + edit3,686,400–16,777,216 px, up to 16:1—2048x2048614jpeg, png—
seedream-5.0-progenerate + edit921,600–4,624,220 px, up to 16:1—2048x2048410jpeg, pngyes
qwen-image-2.0generate + edit262,144–4,194,304 px, up to 4:1—2048x204863png—
qwen-image-3.0generate + edit262,144–6,553,600 px, up to 8:1—model decides13png—
qwen-image-3.0-progenerate + edit262,144–6,553,600 px, up to 8:1—model decides13png—
qwen-image-edit-plusediteach side 512–2,04816 pxmodel decides63png—
z-image-turbogenerate262,144–4,194,304 px, up to 4:116 px1536x10241—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.5 and seedream-5.0, whose pixel floor sits above all three: ask for 1024x1024 and the delivered image is 2048x2048; 720x1280 delivers 1440x2560.
  • qwen-image-edit-plus and z-image-turbo render on a 16-pixel grid. Each side rounds to the nearest multiple of 16, ties to the even multiple — 1000x700 arrives 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":

ParameterWhy
styleNo model here exposes a style control
qualityOnly "auto" is legal; no model here exposes a quality control
output_compressionThis surface never transcodes a delivered image
input_fidelityNot supported by any model here
moderationContent policy is always applied and is not configurable
maskNo 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.

StatustypeWhat it means
400invalid_request_errorA 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
400image_generation_user_errorThe prompt or a source image was refused by the model's content policy. code is moderation_blocked
401authentication_errorMissing, malformed or unrecognised credential
402insufficient_creditYour organization is out of credit
403permission_errorThe key is inactive or expired, or the model is not enabled in your region
413invalid_request_errorThe request body is over the endpoint's size limit
429rate_limit_errorOver your request rate, or the model is not priced on your tier or in your billing currency
429quota_exceededOver a spend budget: the API key's, its budget for this model, your member budget or your organization's. code names which
500api_errorSomething on our side failed, including a gate that could not reach its own dependency and refused rather than guess
502api_errorThe model produced a response we could not interpret
503api_errorThe model is temporarily unavailable or over capacity — retry with backoff
504api_errorThe 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-data
modelstringrequired

The image model to use (e.g. "seedream-4.0"). See the model table below for sizes, batch limits and formats.

promptstringrequired

Text 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.

ninteger

How 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.

sizestring

Output 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_jsonurl
output_formatstring

Delivered 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.

transparentopaqueauto
quality"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.

userstring

An opaque identifier for your end user. Recorded in your logs for correlation and never passed to the model.

Response

createdintegerrequired

Unix timestamp of when the images were returned.

dataobject[]required

One 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);

Response

{
  "created": 1755820800,
  "data": [
    {
      "b64_json": "iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAA...(truncated)"
    }
  ]
}