API Reference

Image

Generate an image from a prompt, or edit existing ones with up to four reference images.

POST/v1/images/generations

Two routes over the same model: one starts from a prompt alone, the other starts from images you supply. Both return the image as base64 in the response body.

POST/v1/images/generations
POST/v1/images/edits

Generation

application/json. A prompt in, a PNG out.

curl -X POST "https://api.trystac.com/v1/images/generations" \
  -H "Authorization: Bearer $STAC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a white dress shirt folded on a wooden table",
    "size": "1024x1024",
    "seed": 42
  }'

This route takes no input image. Sending one returns 400 wrong_route_for_reference_image — use /v1/images/edits instead.

Editing

multipart/form-data. One to four reference images, plus a prompt describing the change.

curl -X POST "https://api.trystac.com/v1/images/edits" \
  -H "Authorization: Bearer $STAC_API_KEY" \
  -F "prompt=make it black and white" \
  -F "size=1024x1024" \
  -F "image[]=@photo.png"

The file field accepts both image and image[] — the second is what OpenAI's own examples use for multiple files. Up to 4 images, 5 MiB each, in PNG, JPEG or WEBP. The format is read from the file's contents, not from its extension or the part's Content-Type. The whole multipart body is capped at 21 MiB.

Reference images are converted to RGB on arrival, which drops the alpha channel, colour profiles, and EXIF metadata — including any location data the original file carried.

mask is not supported and returns 400. The pod loads a single pipeline, and applying the edit to the whole image while pretending to honour a mask would be worse than refusing.

Parameters

The same fields on both routes — as JSON on /generations, as form fields on /edits.

promptstringRequired

What to generate, or what to change about the images you sent. It can be omitted if your API key has a prompt configured — see Prompt on the key. Read Prompt length before writing a long one.

sizestringOptionalDefault: 1024x1024

Output dimensions — square, landscape, or portrait. A closed list; anything else returns 400.

1024x10241536x10241024x1536
stepsintegerOptionalDefault: 4

Denoising steps, from 1 to 8. The checkpoint is distilled, so it is tuned to produce a finished image in very few steps — this is why the ceiling is 8 and not 50.

guidance_scalenumberOptionalDefault: 1.0

How strictly to follow the prompt, from 0 to 20.

seedintegerOptional

From 0 to 2^64−1. Omit it and the server draws one, returning it in meta.seed — that value is what reproduces the same image later.

nintegerOptionalDefault: 1

Images per request. 1 is currently the only accepted value.

response_formatstringOptionalDefault: b64_json

Only b64_json. url is rejected — the image comes back in the response body.

Response

Identical on both routes.

200 OKjson
{
  "created": 1767225600,
  "data": [{ "b64_json": "iVBORw0KGgo..." }],
  "meta": {
    "prompt": "a white dress shirt folded on a wooden table",
    "width": 1024,
    "height": 1024,
    "steps": 4,
    "guidance_scale": 1.0,
    "seed": 8151234567890123456,
    "n": 1,
    "model": "flux2-klein-4b",
    "timings": {
      "queue_wait_s": 0.0100,
      "decode_s": null,
      "gpu_s": 4.1000,
      "encode_s": 0.1800,
      "worker_s": 4.2800
    }
  }
}

meta reports the effective parameters — what the server actually used, after defaults and after drawing a seed if you didn't send one.

data[].b64_jsonstringOptional

The PNG, base64-encoded.

meta.seedintegerOptional

The seed that produced this result. It belongs to the batch, not to an individual image: reproducing image i takes the same seed and the same n.

meta.timingsobjectOptional

Where the time went, in seconds, to four decimal places. queue_wait_s is time spent waiting for a free slot; decode_s is reading your reference images (null on /generations, which has none); gpu_s is the diffusion itself; encode_s is producing the PNG. worker_s is the sum of the last three and excludes the queue wait.

Prompt length

This is the most common cause of "the image ignored half of what I asked for". A long, detailed prompt is not just wasted past the cutoff; it also dilutes the part that does fit. Lead with the subject and the details that matter most.

Prompt on the key

The prompt can live on your API key, the way the system prompt does on the text endpoints — and the same precedence applies.

In the request bodyOn the keyWhat the model receives
prompt with textanythingthe request's
absent or blankconfiguredthe key's
absent or blanknothing400 missing_prompt

That is what lets you send only the URL, the key and the image:

curl -X POST "https://api.trystac.com/v1/images/edits" \
  -H "Authorization: Bearer $STAC_API_KEY" \
  -F "image[]=@photo.png"

Throughput

Image generation has no daily quota — diffusion produces no tokens, so the token budget that applies to the chat endpoints doesn't apply here. What limits you is the GPU, which renders one image at a time.

LimitWhat it means
Submissions10 per minuteShared between generations and edits
In flight3 at once1 rendering plus up to 2 waiting
Queue wait60 secondsPast that the request returns 504

Both limits belong to the stack, not to the key. Issuing more keys doesn't raise them, because the same GPU does the work either way.

Observed end to end: roughly 6–9s for text to image, and 7–21s for an edit, varying with the number, dimensions and weight of the reference images. "10 per minute" is a submission ceiling, not a promise of ten finished images per minute.

Storage and retention

Every generated image is stored, linked to the account, stack and key that created it. This happens server-side and changes nothing in your code — the response is still b64_json.

Two consequences worth knowing:

  • A 200 means the image was stored. The write happens before the response is sent, and adds a few hundred milliseconds to a call that already takes seconds.
  • A 502 can happen with the image already generated. If storage fails we return the error rather than hand you an image while claiming it was saved. Retrying is safe — but it generates again, with a new seed unless you pinned one.

Files are kept for 30 days. If you need an image for longer, save the b64_json on your side: this storage exists for traceability and for viewing the image in the platform, not as permanent hosting.

Errors

Two error shapes reach you on these routes. The gateway's own rejections use the API-wide { "detail": "..." } shape; anything the image server rejects comes back in OpenAI's shape, with a stable code you can branch on:

400 Bad Requestjson
{
  "error": {
    "message": "size must be one of: 1024x1024, 1536x1024, 1024x1536",
    "type": "invalid_request_error",
    "code": "invalid_size"
  }
}
StatuscodeMeaning
400invalid_size, invalid_steps, invalid_n, invalid_guidance_scale, invalid_seedField outside its accepted range, or the wrong type
400missing_promptprompt absent or empty, and no prompt configured on the key
400mask_not_supportedmask isn't supported in this version
400wrong_route_for_reference_imageSent an image to /generations — use /edits
400missing_image/edits called without a file
400too_many_reference_imagesMore than 4 references
400unsupported_image_formatA reference isn't PNG, JPEG or WEBP
403The key isn't from a stack on the image plan
404model_not_foundmodel other than the served one (/edits only)
413image_too_largeA reference over 5 MiB, or a body over the route's cap
429queue_fullAlready 3 in flight — respect Retry-After
429Over 10 submissions per minute — respect Retry-After
502The image was generated but couldn't be stored
503model_not_readyInfrastructure still starting, or degraded
504queue_timeoutWaited more than 60s for a free slot

Retry 429, 503 and 504 after the delay in Retry-After. A 502 is also worth one retry, keeping in mind it renders again from scratch.