Endpoints
Get a job
Returns a job's status and, once it succeeds, its clips. Call it to poll, or hold the request open until the job is done.
api.clipsdk.com/v1/jobs/{id}Request
Pass the job id you got from POST /v1/clips. The job's poll_url is this same URL.
idstringIn pathRequired- The job id, like
job_q8w2e4r6t1y3u5i7o9p0. waitintegerIn query- Seconds to hold the request open while the job runs, 0 to 300. It returns as soon as the job finishes.Default
0
Unknown ids, and jobs created with another API key, return 404 NOT_FOUND.
The job object
Every endpoint and webhook returns the job in this shape, wrapped as { job }.
idstring- Job id:
job_plus 20 lowercase letters and digits. opstring- Always
"clips"for jobs fromPOST /v1/clips. statusstring- Where the job is. See statuses.One of
"queued""running""succeeded""failed" stagestring | null- The step a running job is on.
nullwhile queued, while the job's machine starts and once finished.One of"fetching""transcribing""ranking""rendering" created_atstring- When the job was created, in ISO 8601 UTC.
finished_atstring | null- When it succeeded or failed.
nulluntil then. resultobject | null- Set only when
statusissucceeded. There are no partial results while the job runs. result.numbersource. duration_s - Length of the source video in seconds.
result.string | nullsource. language - Spoken language code, as you set it or as we detected it.
result.nullsource. speakers - Always
nullfor now. result.arrayclips - The delivered clips, best first. See the clip object.
usage.numbersource_minutes - Minutes of source video, which is what you pay for.
0unless the job succeeded: failed jobs cost nothing. usage.integerclips - How many clips were delivered.
usage.numberoutput_seconds - Total length of all clips, in seconds.
errorobject | null- Set only when
statusisfailed: acodeand amessage. poll_urlstring- This job's URL. GET it to check on the job.
Statuses
A job starts queued, runs through its stages and ends succeeded or failed. Both ends are final.
GETapi.clipsdk.com/v1/jobs/{id}
HTTP 202 Accepted
"status": "queued",
"stage": null,
"result": null
job_q8w2e4r6t1y3u5i7o9p0not started
- queuedHTTP 202
- Accepted, not started yet.
- runningHTTP 202
- Being worked on.
stagesays which step, ornullwhile the job's machine starts. - succeededHTTP 200
- Done.
resultholds the clips. - failedHTTP 200
- Stopped.
errorsays why. See failed jobs.
Stages
A running job moves through 4 stages, in order. Use them to show progress; how long each one takes depends on the video.
- 1
fetching - Downloading the video from
video_url. - 2
transcribing - Turning speech into timed words.
- 3
ranking - Scoring candidate moments and picking the best ones.
- 4
rendering - Cropping each clip to your aspect, burning in captions, and writing the poster and caption files.
Waiting
Both POST /v1/clips and GET /v1/jobs/{id} can hold the request open until the job is done, so a script can skip polling. Ask for it either way:
?wait=Nquery- Wait up to N seconds, 0 to 300.
Prefer: wait=Nheader- The same, as a header.
Prefer: respond-asyncheader- Return right away, without waiting.
Without either, create waits 60 seconds and GET doesn't wait. A wait outside 0 to 300 gets 400 INVALID_REQUEST. The answer comes as soon as the job finishes. If the wait runs out first, you get 202 Accepted with the job as it is now, a Location header that points at it, and Retry-After: 5:
- Location:
- https://api.clipsdk.com/v1/jobs/job_q8w2e4r6t1y3u5i7o9p0
- Retry-After:
- 5
{
"job": {
"id": "job_q8w2e4r6t1y3u5i7o9p0",
"op": "clips",
"status": "running",
"stage": "ranking",
"created_at": "2026-10-26T09:14:02Z",
"finished_at": null,
"result": null,
"usage": {
"source_minutes": 0,
"clips": 0,
"output_seconds": 0
},
"error": null,
"poll_url": "https://api.clipsdk.com/v1/jobs/job_q8w2e4r6t1y3u5i7o9p0"
}
}To start a job without waiting, then hold a GET open until it's done:
# Start a job and return right away
curl https://api.clipsdk.com/v1/clips \
-H "Authorization: Bearer $CLIPSDK_KEY" \
-H "Content-Type: application/json" \
-H "Prefer: respond-async" \
-d '{"video_url": "https://cdn.example.com/off-script-112.mp4"}'
# Later: hold a request open until the job is done, for up to 300 s
curl "https://api.clipsdk.com/v1/jobs/job_q8w2e4r6t1y3u5i7o9p0?wait=300" \
-H "Authorization: Bearer $CLIPSDK_KEY"Mind your client timeout
Polling
Without a webhook, check the job until status is succeeded or failed. Wait the Retry-After seconds between calls. Adding ?wait=60 to each call means fewer requests, and you hear about the result the moment it's ready.
JOB=job_q8w2e4r6t1y3u5i7o9p0
# Each call waits up to 60 s. The loop ends when the job finishes or a call fails
while true; do
STATUS=$(curl -sSf "https://api.clipsdk.com/v1/jobs/$JOB?wait=60" \
-H "Authorization: Bearer $CLIPSDK_KEY" | jq -r .job.status)
echo "$STATUS"
case "$STATUS" in succeeded|failed|"") break ;; esac
sleep 5
doneWith many jobs in flight, a webhook beats polling.
Failed jobs
A job that can't finish doesn't fail the HTTP request. It ends with status failed, the GET still returns 200, and error holds a code and a message. stage and result are null and usage is all zeros:
{
"job": {
"id": "job_f3k9m2p7x4c8v1b6n5z0",
"op": "clips",
"status": "failed",
"stage": null,
"created_at": "2026-10-26T09:14:02Z",
"finished_at": "2026-10-26T09:14:09Z",
"result": null,
"usage": {
"source_minutes": 0,
"clips": 0,
"output_seconds": 0
},
"error": {
"code": "INPUT_FETCH_FAILED",
"message": "Could not fetch video_url: HTTP 404"
},
"poll_url": "https://api.clipsdk.com/v1/jobs/job_f3k9m2p7x4c8v1b6n5z0"
}
}The codes are INPUT_FETCH_FAILED, INPUT_UNSUPPORTED, INPUT_TOO_LONG, INPUT_TOO_LARGE, NO_SPEECH, and INTERNAL when something breaks on our side. A failed job costs nothing, so fix the cause and send the same request again. Errors explains each code.
Files
Clip, poster and caption files are kept for 7 days. After that their URLs return 404, so copy the clips you want to keep. Use the URLs in the job rather than building them yourself.
File URLs need no API key and support Range requests, so video players can stream and seek, and downloads can resume.