Skip to main content

Generation lifecycle

Generations usually complete within a few hours. Duration depends on the asset’s complexity, the supplied references, and the selected effort level; a generation may run for up to nine hours before timing out. Poll an individual asset every 30–60 seconds; polling has no rate limit. The account owner receives an email when a generation completes or fails. created_at is the time a generation was submitted. status_reason contains a human-readable detail about its current state. On failure, it begins with a stable code followed by : and detail. The possible codes are REFERENCE_FETCH_FAILED, IMAGE_UNSUPPORTED_TYPE, TIMEOUT, DISPATCH_FAILED, MISSING_CREDENTIALS, NO_DELIVERABLE, THUMBNAIL_CREATE_FAILED, and INTERNAL.

Downloads

download_url values are presigned links that expire after seven days. Fetch GET /api/v1/assets/{asset_id} again to receive new links. Anyone holding a link can download the file until it expires, so treat it as a secret and copy artifacts into your own storage for long-term retention.

Concurrency and credits

POST /api/v1/assets quotes by default: in its default mode: quote it returns 200 with a price quote and creates nothing. Only mode: generate consumes concurrency or credits. Quoting — POST /api/v1/assets/quote and quote-mode POST /api/v1/assets together — is limited to 20 requests per minute per account, and returns 429 beyond that. See Quote credits before creating an asset. Up to 100 generations per account can be active (pending or processing). Creating another one returns 429 until a generation finishes. Submitting reserves the generation’s fixed price up front; if your balance can’t cover it, submission returns 402. Once submitted, that price is what you pay: credits_charged shows the reserved price on the create, retrieve, and cancel responses, and is null whenever nothing is currently charged — a failed generation, or one that was never charged. List entries always omit it.

Idempotency

An Idempotency-Key is scoped to your account and never expires. The same key and body always return the original generation. Reusing a key with a different body returns 409. A 422 validation failure binds no key, so fix the request and retry with the same key. A submission that can’t start — for example a 402 for insufficient credits or a 502 dispatch failure — also frees the key, so you can retry with the same key. Once a generation has started, its key is bound: a run that later fails keeps its key, so retry a failed run with a new key. POST /api/v1/projects accepts the header too, but for grouping rather than retry safety, and it differs on two points. A reused key returns the existing project with 200 instead of 201, which tells you whether this call created it. And because a project can be renamed at any time, a reused key with a different name returns the existing project unchanged instead of 409 — the key identifies the project, the name does not. Deleting a project releases its key for reuse. See Projects and batches.

Cancellation

POST /api/v1/assets/{asset_id}/cancel cancels pending and processing generations. It is idempotent for an already cancelled generation. Cancelling a completed or failed generation returns 409.
Cancelling does not refund credits. A cancelled generation stays charged the fixed price it reserved at submission. Cancel to stop a generation whose result you no longer want — it frees a concurrency slot and ends the work — not to avoid its cost. Quote first with mode: quote if you are unsure about the price.
Cancellation is best-effort: if the generation completes first, the completed result wins and is charged.

Listing assets

GET /api/v1/assets returns generations newest-first. Its list entries omit result and credits_charged; retrieve an individual asset for those fields. Filter with status= and project_id= and page with the opaque cursor in next_cursor. Filtering by an unknown or unowned project returns an empty page.

HTTP errors