Skip to content

Create a job

POST /v1/jobs
Idempotency-Key: 5f0c2a8e-…
{
"input": { "upload_id": "0199a0e0-5f1c-7c3a-9d2e-1b4f6a8c2d10", "clip": { "start": 10, "duration": 60 } },
"outputs": {
"streaming": { "formats": ["hls", "dash"], "variants": ["1080p", "720p", "480p", "360p", "240p"] },
"mp4": { "variants": ["720p", { "resolution": "1080p", "audio": false }] },
"mp3": { "bitrate": 192000 },
"posters": [{ "at": 5, "width": 1280 }],
"storyboard": { "interval": 5 }
},
"audio": { "tracks": [{ "language": "es" }, { "language": "en", "required": false }] },
"subtitles": { "tracks": "all" },
"on_upscale": "skip",
"quality_metrics": true,
"webhook_url": "https://your-server.example.com/hooks/iomza",
"metadata": { "video_id": "123" }
}

The answer is the job (201), queued until your plan has room for it, then processing. See Follow a job.

Idempotency. Send an Idempotency-Key (any unique string up to 255 characters, a UUID for instance): if the request is repeated with the same key, after a timeout say, you get the same job back (200) instead of a second one. The same key with a different body is a 409.

Field
input The source: exactly one of upload_id (an upload of yours: a completed one, or a progressive one still arriving) or url (copied first: see Or give a URL).
input.clip Process a stretch only: start in seconds (default 0) and duration (default: to the end).
outputs At least one of streaming, mp4, mp3, posters, storyboard.
outputs.streaming Adaptive streaming. formats: hls and/or dash (default both; they share their segments, so asking for both costs nothing extra). variants: the qualities.
outputs.mp4 One MP4 file per variant, each carrying every chosen audio track.
outputs.mp3 The audio alone. bitrate 32 000–320 000 (default 128 000).
outputs.posters 1–10 JPEG images: at (second), optional width/height (one alone keeps the shape).
outputs.storyboard Thumbnail sprites and a WebVTT index, for previews over a player’s seek bar: interval (1–60 s), width/height (default 160×90 in the video’s shape), columns.
audio.bitrate Forces re-encoding the audio as AAC at this bitrate. Without it, mono and stereo AAC is copied as is: faster, and no quality lost.
audio.tracks, subtitles.tracks Which tracks of the source come out: see Languages and subtitles.
on_upscale For qualities larger than the source: skip (default) leaves them out and lists them in the job’s skipped; fail rejects the job.
quality_metrics true scores each variant’s perceived quality (VMAF): see Quality. Adds a few seconds.
webhook_url Also send this job’s events here, besides your organization’s endpoints. https, public.
metadata Up to 10 text pairs of yours (keys up to 40 characters, values up to 500), returned in the job and its webhooks.

Each variant is a quality. Write it as its name for the defaults, or as an object to control it:

"variants": [
"1080p",
{ "resolution": "720p", "codec": "hevc", "quality": 24 },
{ "resolution": "480p", "bitrate": 1200000, "rate_control": "cbr" }
]

Up to 10 per output. The same quality asked in streaming and mp4 is encoded once and counted once.

Field Values Default
resolution 240p 360p 480p 720p 1080p 1440p 2160p, or any other even height from 144 to 2160 ("540p", "576p", "900p") required
codec h264 (plays everywhere), hevc (about 30 % smaller at the same quality; Apple devices, recent TVs and browsers), av1 (smallest; recent devices) h264
quality Constant quality, CRF 16–35: lower is better and heavier 22
max_bitrate A ceiling, in bps (100 000–100 000 000) per resolution
bitrate A target bitrate, in bps (100 000–100 000 000), instead of a quality —
rate_control With bitrate: vbr or cbr vbr
preset ultrafast … veryslow: speed against compression veryfast
profile H.264 only: baseline, main, high per resolution
fit How the picture fills the frame: see below contain
rotate 0, 90, 180, 270 (clockwise, before scaling) 0
flip horizontal, vertical —
watermark { "url": "https://…/logo.png", "position": "bottomright" }: topleft, topright, bottomleft, bottomright or center —
audio MP4 only: whether the file has audio true

A quality names a 16:9 box: 1080p is 1920×1080, 540p is 960×540. With fit: "contain" (the default) the picture keeps its shape inside the box and the box turns with the video, so a vertical phone video at 720p comes out 720×1280, with no bars. pad keeps the 16:9 box and adds bars; crop fills it and cuts what overflows.

Custom heights ("540p", "900p"…) get a default ceiling interpolated between the standard qualities around them.

There are two ways to control the bitrate. Use one per variant: quality and bitrate are exclusive.

Quality mode (the default): quality sets a constant perceived quality, and the encoder spends what each scene needs. A talking head takes a fraction of what a football match does. max_bitrate is a ceiling, not a target: simple scenes stay well under it, complex ones are held at it. The default ceilings are 400 Kbps at 240p up to 15 Mbps at 2160p. The output also never goes above the source’s own bitrate: re-encoding can’t add detail that isn’t there. This is the best choice for most uses, since you pay for bits only where they show.

Bitrate mode: bitrate asks for a bitrate, and it is honoured even when the source has less.

  • rate_control: "vbr" (default): bitrate is the average. Complex scenes may rise up to max_bitrate (default 1.5 × bitrate).
  • rate_control: "cbr": constant, padded where the picture needs less. For broadcast, ad servers and CDNs that require it. Files are bigger for the same quality. With cbr, give bitrate only.
  • AV1 supports vbr only, without max_bitrate.

By default every audio track of the source comes out, as do its text subtitles. To choose, list the tracks in order of preference: the first one is the default.

"audio": { "tracks": [{ "language": "es" }, { "language": "en", "name": "English (original)" }, { "index": 2, "required": false }] },
"subtitles": { "tracks": [{ "language": "es" }] }
  • Pick by language (es, spa and es-419 all work: the first track in that language) or by index (its position among the source’s tracks of that kind, from the tracks).
  • name is what players show. By default it is the language’s name in itself (“Español”, “English”), made unique if two share it (“English 2”). Untagged tracks are named by their title, or “Audio 1”…; names written by encoding tools are ignored.
  • required: false means “if it exists”: a missing track is listed in the job’s skipped_tracks instead of failing the job.
  • [] means none. "all" is the default. At most 8 audio tracks come out.

Each audio track comes out as stereo AAC: surround (5.1, 7.1, Atmos) is mixed down to stereo, which every device plays. In HLS and DASH they are alternative audio tracks with their language, so players show a language menu; MP4s carry every track, labeled, with the default one enabled. Each streaming audio language counts as audio minutes.

Text subtitles (SRT, ASS, WebVTT, mov_text…) are converted to WebVTT, cut to the clip, delivered as files and inside the streaming. Forced subtitles are flagged (forced: true) and named so in their own language: “Español (forzados)”, “English (forced)”. Image subtitles (PGS, VobSub) can’t be converted and are listed in skipped_tracks.

You get a 400 validation_error whose details lists every problem with its path, explained by the form you wrote, so you can fix them all at once:

{
"error": {
"code": "validation_error",
"message": "Invalid job request.",
"details": [
"outputs.streaming.variants.1.bitrate: give quality or bitrate, not both",
"outputs.mp4.variants.0: must be one of 240p, 360p, 480p, 720p, 1080p, 1440p, 2160p, or another even height from 144 to 2160 such as \"540p\""
]
}
}