Follow a job and get its outputs
GET /v1/jobs/{id}Progress is refreshed on every request while the job runs. Rather than polling, listen for the webhooks: job.succeeded, job.failed and job.canceled carry the job as this request shows it.
Status
Section titled “Status”queued → processing → succeeded | failed | canceled
A job waits in queued while your organization already runs as many jobs as its plan allows (max_concurrent_jobs); it starts as soon as one finishes. progress (0–100) is the whole job’s.
tasks lists everything being produced, each with its status (pending, running, done, stopped), progress (0–100, or null while it runs without a measurable percentage) and times:
type |
|
|---|---|
import |
The copy from input.url, with bytes and total_bytes. |
upload |
A progressive upload arriving, with bytes and total_bytes. |
analysis |
Reading the source. |
video |
One per quality (resolution). Qualities advance together, since they are encoded from the same frames. |
audio |
One per language: track, language, name, and mode: copy (the source’s audio reused, no quality lost) or encode. |
subtitles |
count tracks. |
mp4, mp3, poster, storyboard |
Each deliverable. |
streaming |
The HLS and DASH manifests (formats). |
Timing
Section titled “Timing”| Field | |
|---|---|
queued_seconds |
Waiting for room in your plan. |
import_seconds |
Copying from input.url, if the job had one. |
processing_seconds |
From the start of the analysis to the outputs being ready, without the queue or the upload. When the video was processed while it arrived, it counts from the end of the upload or copy: it is the wait once the file is in. |
speed |
Seconds of video per second of processing: 33.5 means 33.5× real time. |
stages |
analysis_seconds, encoding_seconds, assembly_seconds. |
timeline |
When each stage started and ended (created_at, dispatched_at, import_started_at, imported_at, analysis_started_at, analyzed_at, encoded_at, completed_at), to draw them on one axis. |
Outputs
Section titled “Outputs”"outputs": { "streaming": { "hls_url": "https://…/master.m3u8", "dash_url": "https://…/manifest.mpd", "variants": [{ "resolution": "1080p", "width": 1920, "height": 1080, "size_bytes": 612345678 }], "size_bytes": 640123456, "audio_size_bytes": 27000000 }, "mp4": [{ "resolution": "720p", "width": 1280, "height": 720, "url": "https://…", "size_bytes": 301234567 }], "mp3": { "url": "https://…", "size_bytes": 57600000 }, "posters": [{ "url": "https://…", "size_bytes": 84512 }], "storyboard": { "image_url": "https://…/sprite-1-000.jpg", "image_urls": ["…"], "vtt_url": "https://…/storyboard.vtt", "size_bytes": 1203344 }, "audio_tracks": [{ "language": "spa", "name": "Español", "default": true }], "subtitles": [{ "language": "spa", "name": "Español (forzados)", "forced": true, "url": "https://…/subtitles-0.vtt" }], "quality": { "metric": "vmaf", "model": "vmaf_v0.6.1", "sampled_seconds": 96, "variants": [{ "resolution": "1080p", "vmaf": 95.8, "vmaf_min": 93.1 }] }, "total_size_bytes": 999123456, "source_size_bytes": 4812345678}width and height are what came out, after fit and rotation. A long storyboard is split into several images (sheets of 10 rows); image_url is the first, image_urls all of them, and the WebVTT maps each moment to its tile.
Every output has size_bytes, what it weighs as published. In streaming, size_bytes is the whole folder: HLS and DASH share their segments, so they count once; each variant’s is its video, and audio_size_bytes is the audio they all share. total_size_bytes is everything published for the job; source_size_bytes, the source.
Quality (VMAF)
Section titled “Quality (VMAF)”With quality_metrics: true, outputs.quality scores how each variant looks next to the source, from 0 to 100, on samples spread over the video (the whole video if it is short): vmaf is the mean of the samples, vmaf_min the weakest one. From about 93 the difference is hard to see; 80–93 is good; below 60, poor. Variants are compared at 1080p (or the source’s size, if smaller) with the standard vmaf_v0.6.1 model. Variants with a watermark are not scored, since the mark would count as a defect. Without quality_metrics it is null.
Other fields
Section titled “Other fields”source: what the source is (duration, size, frame rate, bitrate, codecs,audio_tracks,subtitle_tracks).adjustments: corrections applied so the outputs play everywhere:deinterlaced,frame_rate_normalizedandframe_rate_capped(withfromandto),rotation_applied(degrees),anamorphic_corrected,hdr_tone_mapped.skipped: qualities left out because they were larger than the source (on_upscale: "skip").skipped_tracks: requested tracks that didn’t come out:not_found(an optional pick),image_subtitles,too_many.request: the settings the job was created with, defaults applied: what a retry starts from.billed_minutes: the job’s billed minutes per category, once it succeeded (only inGET /v1/jobs/{id}).
When a job fails
Section titled “When a job fails”error says whose the problem is and whether trying again can help:
"error": { "code": "unreadable_source", "category": "source", "retryable": false, "message": "…" }code |
category |
retryable |
What happened, what to do |
|---|---|---|---|
unsupported_source |
source |
no | The file can’t give what was asked: no audio and an MP3 asked, smaller than a quality with on_upscale: "fail", a required track that isn’t there, an unsupported video. Change the request or the file. |
unreadable_source |
source |
no | The file is damaged, incomplete (an MP4 without its index, cut off mid-upload) or not audio or video. Upload it again, whole. |
source_import_failed |
source |
yes | It couldn’t be copied from input.url (HTTP 4xx or 5xx, timeout, too large…). Check the URL and retry. |
upload_incomplete |
source |
no | A progressive upload stopped arriving for 30 minutes. Upload the file again. |
validation_error |
request |
no | The settings don’t work for this file (a watermark that can’t be downloaded, say). Fix them. |
timeout, launch_failed |
internal |
yes | Ours, and temporary: retry. |
processing_error |
internal |
no | Ours: we are already looking into it. The message is generic on purpose. |
Momentary failures (network, capacity) are retried inside the job and never reach failed.
Retry, cancel, delete
Section titled “Retry, cancel, delete”POST /v1/jobs/{id}/retryPOST /v1/jobs/{id}/cancelDELETE /v1/jobs/{id}- Retry creates a new job from the same source (no new upload) with the same settings, except those in the body:
outputs,audio,subtitles,on_upscale,quality_metrics,webhook_url,metadata, andinput.clip(nullfor the whole video). It also makes a variation of a finished job. The new job’sretry_ofpoints at the original. Sources are kept 7 days: after that, one brought from a URL is copied from it again, and an uploaded one answers409(upload it again). - Cancel stops a queued or running job.
- Delete removes a finished job’s files. The job stays listed, with
deleted: true, for your history and billing.
Listing jobs
Section titled “Listing jobs”GET /v1/jobs?status=processing&limit=20Newest first. Pass the page’s next_cursor as cursor for the next one; it is null on the last. Pages stay consistent while new jobs arrive.