A run is one generation request. Submit it once, keep its ID, then receive its result by webhook or read it from the API. The same run is returned through either channel.
Create a run
curl 'https://fusara.ai/api/v3/run' \
-H "Authorization: Bearer $FUSARA_API_KEY" \
-H 'Content-Type: application/json' \
--data-raw '{
"model": "qwen-image21-create",
"prompt": "A white ceramic lamp on a pale lavender background, soft studio light."
}'Put model settings at the top level alongside model. Use the exact ID and fields from the model catalog; do not wrap inputs in input, parameters or options.
The response is 202 while processing and 200 if already terminal. Keep id immediately. The Location header points to the same run resource.
Understand the lifecycle
| Status | Meaning | Next step |
|---|---|---|
queued |
Accepted and waiting for execution. | Wait for a webhook or read again later. |
running |
Execution is in progress. | Continue waiting. |
succeeded |
Generation finished successfully. | Save the outputs. |
failed |
Generation ended with an error. | Read error and preserve any partial outputs. |
canceled |
Canceled before execution admission. | No further generation result is expected. |
The final three statuses are terminal. Stop polling for generation progress when you see one of them. Billing can still settle afterward: cost.status has its own lifecycle, described in Balance & pricing.
Read a result
curl 'https://fusara.ai/api/v3/run/123456' \
-H "Authorization: Bearer $FUSARA_API_KEY"Polling every few seconds is a reasonable starting point; increase the interval for longer generations. Polling frequency does not speed up execution. A read returning HTTP 200 can contain a failed run.
{
"id": 123456,
"status": "succeeded",
"cost": {
"unit": "common_fuz",
"expected": "40.0000",
"status": "settled",
"charged": "40.0000"
},
"outputs": [
{
"type": "image",
"media_id": 901,
"url": "https://media.example/result.png",
"url_expires_at": "2026-10-25T12:00:00Z"
}
]
}Outputs are typed. For image, video and audio, use url and keep media_id if you want to reuse the media. For text, json and boolean, read value. A run can have more than one output. Follow the types and fields returned by your model rather than assuming every result is an image.
For example, inline results use the same field for each type:
{
"id": 123457,
"status": "succeeded",
"cost": {
"unit": "common_fuz",
"expected": "10.0000",
"status": "settled",
"charged": "10.0000"
},
"outputs": [
{
"type": "text",
"value": "A cat sitting on a windowsill."
},
{
"type": "json",
"value": {
"objects": [
"cat"
],
"count": 1
}
},
{
"type": "boolean",
"value": false
}
]
}json values are native JSON, not JSON encoded inside a string. Preserve false, 0, empty strings and null; they are values, not missing results. Webhooks contain these same output objects.
Save files before url_expires_at. Expired output metadata can remain available with expired: true and no URL. Reading a run does not guarantee that its files are still downloadable.
Retry a submission safely
Idempotency-Key is optional. Use it when your application may repeat a submission after a connection failure. Create one key per intended generation and persist it with the original body before sending.
curl 'https://fusara.ai/api/v3/run' \
-H "Authorization: Bearer $FUSARA_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: product-784-cover-v1' \
--data-raw '{
"model": "qwen-image21-create",
"prompt": "A white ceramic lamp on a pale lavender background, soft studio light."
}'Repeating the same key and request for the same account returns the original run instead of generating again. Keep the request unchanged, including webhook_url. Reusing a key for a different request returns 422 idempotency_conflict with retryable: false.
If the original submission is still being accepted, the replay returns 409 request_in_progress with retryable: true and Retry-After: 2. Wait for the indicated delay, then retry with the same key and unchanged body.
A new key means a new generation. Do not generate a fresh key inside an automatic retry loop. If you already know the run ID, retrieve that run instead of submitting again. See uncertain submission outcomes.
Browse run history
curl 'https://fusara.ai/api/v3/run?status=succeeded&model=qwen-image21-create' \
-H "Authorization: Bearer $FUSARA_API_KEY"History contains your account's API v3 runs. It returns up to 100 runs per page, newest first; there is no limit, page or offset parameter for runs.
| Filter | Value | Behavior |
|---|---|---|
model |
An exact public model ID. | Matches that model variant. |
status |
One or more comma-separated statuses. | Matches any supplied status, for example succeeded,failed. |
created_from |
RFC 3339 timestamp with a timezone. | Includes runs created at or after this time. |
created_to |
RFC 3339 timestamp with a timezone. | Includes runs created before this time. |
ids |
Up to 100 comma-separated run IDs. | Finds a known set of your runs. |
Different filters are combined with AND. A date window such as created_from=2026-10-01T00:00:00Z&created_to=2026-10-02T00:00:00Z selects one UTC day. Encode query values normally, including + in timezone offsets.
Move between pages
Follow the response's next or previous URL unchanged, with your usual API-key header. next moves to older runs and previous to newer ones. Missing links mean there is no page in that direction. An empty result is {"data":[]}.
Pagination links carry an opaque cursor; do not build or decode it, and do not add the original filters to a cursor URL. Cursors expire 24 hours after the first page. On 410 cursor_expired, restart from your initial filtered request.
New runs do not move the head of a traversal already in progress. Existing runs' status and cost can still change, so history is not an event stream. Refresh the first page when you want the newest runs.
History returns summaries and output descriptors. Read GET /run/{id} when you need full inline output values.
Cancel a run
curl 'https://fusara.ai/api/v3/run/123456/cancel' \
--request POST \
-H "Authorization: Bearer $FUSARA_API_KEY"Send this request with no body. Cancellation succeeds only before the run enters execution admission or dispatch. Even a run currently shown as queued can be too late to cancel; in that case the API returns 409 run_not_cancelable.
A successfully canceled run has status canceled and no generation charge. If the run is already terminal, the method returns its current result unchanged. Cancellation is not a way to undo a finished generation or request a refund.
Recover after an interruption
Keep your run ID independently of your browser tab or worker process. If a webhook is missed, GET /run/{id} retrieves the same result. If your application lost an ID, use history and its filters to locate your runs; do not automatically resubmit every unfinished local task.