Concepts

Jobs

Every create call starts a job that moves from queued to running and ends as succeeded or failed. A create call waits up to 60 seconds for it by default, and any request can wait up to 300 seconds or poll the job until it's done.

The job object is the same everywhere: in create responses, from GET /v1/jobs/{id} and in webhooks.

GET/v1/jobs/{id}

The job object#

API responses wrap the job in { "job": ... }, and webhooks send the job object on its own.

FieldDescription
idstring

job_ followed by 20 lowercase letters and digits.

opstring

The endpoint that created the job.

slideshowcaptionsshortsffmpeg
statusstring

Where the job is. See Statuses.

queuedrunningsucceededfailed
stagestring or null

What a running short is doing. Always null for the other endpoints, and for a short once it finishes.

planningvoiceimagesrendering
created_atstring

When the job was created, in ISO 8601 UTC.

finished_atstring or null

When the job succeeded or failed. null until then.

outputsobject

The finished files as URLs, keyed by name, such as video, srt or your own ffmpeg file names. Empty until the job succeeds.

resultobject or null

Shorts only: title, post_caption and scenes. null for the other endpoints.

duration_snumber or null

Length of the main video in seconds. null when there is none.

errorobject or null

{ code, message } when the job failed. See Errors.

usageobject

What the job used. See Usage.

poll_urlstring

The job's GET /v1/jobs/{id} URL.

Statuses#

The HTTP status of every job response follows the job's status, so you can branch on either.

statusHTTPMeaning
queued202Accepted and waiting for a worker, or waiting for its automatic retry.
running202Rendering now. Shorts also report a stage.
succeeded200Done. outputs holds the files.
failed422 or 500Stopped for good. 422 means the inputs were the problem and 500 means we were. See Errors.

A 202 also carries a Location header with the job's poll_url and Retry-After: 5. A failed job comes back as an error with the job next to it, in { "error": ..., "job": ... }.

202 Accepted
{
  "job": {
    "id": "job_x5wepdaqkop4tepq05xp",
    "op": "shorts",
    "status": "queued",
    "stage": null,
    "created_at": "2026-10-26T01:15:26.166Z",
    "finished_at": null,
    "outputs": {},
    "result": null,
    "duration_s": null,
    "error": null,
    "usage": {
      "output_seconds": 0,
      "audio_seconds": 0,
      "tts_characters": 0,
      "images": 0
    },
    "poll_url": "https://api.reelsdk.com/v1/jobs/job_x5wepdaqkop4tepq05xp"
  }
}

Get a job#

GET /v1/jobs/{id} returns the job with the same status codes as the create call. It answers at once unless you ask it to wait, and an id that doesn't exist or belongs to another key returns 404 NOT_FOUND with unknown job.

curl -sS "https://api.reelsdk.com/v1/jobs/job_x5wepdaqkop4tepq05xp" \
  -H "Authorization: Bearer $REELSDK_KEY"

Waiting#

Any create call or GET /v1/jobs/{id} can hold the connection open until the job finishes. The response comes back the moment the job ends, so a long wait never slows down a fast job.

SendEffect
?wait=NWait up to N seconds, from 0 to 300. Anything else returns 400 with wait must be 0..300 seconds.
Prefer: wait=NThe same wait, sent as a header. Values above 300 count as 300.
Prefer: respond-asyncReturn at once, like ?wait=0.
None of theseCreate calls wait up to 60 seconds. GET /v1/jobs/{id} returns at once.

When you send both, ?wait wins.

Polling#

When a job is still running after the wait, poll it. The simplest loop holds each GET open for up to 60 seconds, so a 100 second short needs only two or three calls. Plain polling works too: wait Retry-After seconds between calls. The curl loop uses jq to read the status.

JOB_ID="job_x5wepdaqkop4tepq05xp"

# We poll until the job leaves queued and running
while :; do
  body=$(curl -sS "https://api.reelsdk.com/v1/jobs/$JOB_ID?wait=60" \
    -H "Authorization: Bearer $REELSDK_KEY")
  state=$(printf '%s' "$body" | jq -r ".job.status")
  [ "$state" = queued ] || [ "$state" = running ] || break
done

printf '%s' "$body" | jq ".error // .job.outputs"
  • Stop as soon as the status is succeeded or failed, which is also when the HTTP status stops being 202.
  • Treat queued and running the same, since a job can go back to queued for its automatic retry.
  • If a create call fails with a network error, look the job up instead of sending the request again. A second POST starts a second job.

Automatic retries#

When a job fails on our side, we put it back to queued and run it once more, 1 second later, or 10 seconds later when no worker was free. A short picks up from its last finished step. You only see a 500 after that retry also failed, except for TIMEOUT, which isn't retried.

Failures caused by the inputs, such as a URL that returns 404, fail at once with 422 and are never retried.

Usage#

usage reports what the job consumed. Values are 0 while the job runs.

FieldMeaning
output_secondsLength of the rendered video. For ffmpeg, the total length of every audio and video output.
audio_secondsSeconds of audio we transcribed for captions. For shorts, the length of the voiceover.
tts_charactersCharacters of text we turned into speech for a voiceover or a short.
imagesScene images drawn for a short.

The files in outputs are deleted 7 days after the job finishes, so copy what you want to keep.

Last updated 26 October 2026