Get run status
Poll a run for its state and collect its outputs.
Returns the current state of a run, and its outputs once it succeeds. Prefer a webhook in production and use this to poll when you cannot receive one.
https://api2.rundiffusion.com/api/v2/generate/{request_id}Request
Headers
AuthorizationstringRequired- Bearer token. This endpoint accepts an OAuth device flow token, a Personal API Access Token, or a Company API Access Token. See Authentication.
Authorization: Bearer eyJhbGciOi…
Path
request_idstringRequired- The request_id from the create response. Prefer following status_url, which already has it filled in.
https://api2.rundiffusion.com/api/v2/generate/9f8e7d6c-…
No query parameters and no body.
Response
{
"request_id": "9f8e7d6c-…",
"status": "succeeded",
"created": "2026-05-04T10:00:00Z",
"queued_at": "2026-05-04T10:00:01Z",
"completed_at": "2026-05-04T10:00:09Z",
"retry_after_seconds": null,
"outputs": [
{
"type": "IMG",
"url": "https://rundiffusion.com/...",
"preview_url": null,
"mime_type": "image/jpeg",
"width": 1024,
"height": 1024,
"duration_seconds": null,
"size_bytes": 244000,
"expires_at": "2026-05-18T10:00:09Z",
"expired": false
},
{
"type": "VID",
"url": "https://rundiffusion.com/....mp4",
"preview_url": "https://rundiffusion.com/..._preview.jpg",
"mime_type": "video/mp4",
"width": 1024,
"height": 1024,
"duration_seconds": 5.0,
"size_bytes": 2570619,
"expires_at": "2026-05-18T10:00:09Z",
"expired": false
}
],
"metadata": { "trace_id": "client-trace-1" },
"error": null,
"tokens_charged": 85,
"tokens_returned": 0
}A terminal failure returns 200 too, with the reason in error and the hold
refunded when the failure was not the caller's fault:
{
"request_id": "9f8e7d6c-…",
"status": "failed",
"created": "2026-05-04T10:00:00Z",
"queued_at": "2026-05-04T10:00:01Z",
"completed_at": "2026-05-04T10:00:04Z",
"retry_after_seconds": null,
"outputs": [],
"metadata": {},
"error": {
"code": "CONTENT_POLICY_VIOLATION",
"message": "Provider or NSFW filter flagged the result.",
"retryable": false
},
"tokens_charged": 85,
"tokens_returned": 85
}status is one of:
| Status | Meaning |
|---|---|
pending | Accepted and queued. |
processing | Running now. |
succeeded | Finished. outputs holds the results. |
failed | Terminal failure. error holds the envelope. |
Only succeeded and failed are terminal. While the run is in flight,
retry_after_seconds tells you how long to wait before polling again; it is
null once the run is terminal.
Output fields
Output URLs are signed and expiring, and a run's objects live for about
14 days from creation. Every status fetch inside that window mints a fresh
signed URL, so re-fetch rather than storing one. Once expires_at passes, the
same endpoint returns expired: true and url: null and the bytes are gone, so
download anything you need to keep.
Each entry in outputs
typestring- Output kind: IMG, VID, AUDIO, ASSET_3D, LAYERS, or LAYER for one layer of a LAYERS result. See Layered output.
urlstring | null- Signed download URL. Null when the output has no stored object or has expired.
preview_urlstring | null- Signed URL of a still image for an output you cannot render directly, such as video. Null for outputs that are already displayable, for audio and 3D assets, and for any video whose still could not be produced.
mime_typestring | null- MIME type of the output.
widthinteger | null- Pixel width, for visual output.
heightinteger | null- Pixel height, for visual output.
duration_secondsnumber | null- Duration, for video or audio output.
size_bytesinteger | null- Size of the output file in bytes.
layer_boundsobject | null- LAYER outputs only. Where the layer belongs on the image you sent, as { left, top, right, bottom } fractions of its width and height, from 0 to 1. Null when the layer covers the whole image, and on every other output type.
layer_namestring | null- LAYER outputs only. The tool's own description of the layer, when it gives one. Null otherwise.
expires_atstring (ISO 8601) | null- When the signed URL stops working.
expiredboolean- Whether the URL has already expired.
tokens_charged is what the run actually cost, and tokens_returned is any
refund for results that failed to produce.
Previews for video output
A video output also carries preview_url: a signed JPEG still taken from the
video's first frame, so you can show the result without decoding the video
yourself. This exists for surfaces that ship no video decoder, such as Photoshop
UXP and Omniverse Kit panels.
The still is stored alongside the output and signed on the same TTL, so it expires with the output it previews and you re-fetch it the same way. It is scaled to fit a 512px longest edge, preserving aspect ratio, and is never upscaled past the video's own dimensions.
preview_url is null in four ordinary cases, so treat a missing preview as
normal and fall back to your own placeholder rather than an error:
- The output is already displayable, such as an image.
- The output is a sound. Audio carries no server-side still;
duration_secondsgives you its length for a player or a placeholder. - The output is a 3D asset. Those carry no server-side preview; a renderer that
loads
.glb/.gltfnatively can capture its own still. - The still could not be produced from the video. Extraction is best effort and never fails the run.
Layered output
A tool that splits an image into layers returns a LAYERS output first, which
is a flat preview of the whole result, followed by one LAYER output per layer.
The layers are in stacking order, back to front: the first LAYER is the
bottom of the stack, and each one after it sits above the one before. There is no
index field, so keep the array order when you place them.
"outputs": [
{ "type": "LAYERS", "url": "https://rundiffusion.com/....png", "layer_bounds": null, "layer_name": null, … },
{ "type": "LAYER", "url": "https://rundiffusion.com/...-layer-000.png", "layer_bounds": null, "layer_name": "Background", … },
{ "type": "LAYER", "url": "https://rundiffusion.com/...-layer-001.png",
"layer_bounds": { "left": 0.65, "top": 0, "right": 0.723, "bottom": 0.149 },
"layer_name": "Logo", … }
]Most layer tools return full-size layers, which have layer_bounds: null and go
straight on top of the original. Some return each layer cropped tight around its
content instead, and for those layer_bounds says where the crop belongs. Scale
each fraction by the size of the image you sent: in the example above, the logo
starts 65% of the way across and occupies the top 14.9%. Treat a null
layer_bounds as covering the whole image rather than as missing data.
layer_name is optional on every layer. Fall back to your own label, such as
the layer's position, when it is null.
Errors
| Code | What to do |
|---|---|
REQUEST_NOT_FOUND | Returns 404. No such request_id, or not one this credential can see. A run belonging to somebody else reads the same as one that never existed. |
RATE_LIMITED | Returns 429. See Rate limits. See Rate limits. |
The full list is in Errors.
