Skip to content

Get run status

Poll a run for its state and collect its outputs.

On this page

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.

GEThttps://api2.rundiffusion.com/api/v2/generate/{request_id}
AcceptsOAuth device flowPersonal API Access TokenCompany API Access Token

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

json
{
  "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:

json
{
  "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:

StatusMeaning
pendingAccepted and queued.
processingRunning now.
succeededFinished. outputs holds the results.
failedTerminal 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_seconds gives 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/.gltf natively 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.

json
"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

CodeWhat to do
REQUEST_NOT_FOUNDReturns 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_LIMITEDReturns 429. See Rate limits. See Rate limits.

The full list is in Errors.

View as Markdown