Skip to main content
Beta. The Video API is in beta. Everything below runs against production.

1. Get your API key

Create a key in the dashboard under Settings > API Keys. Every request authenticates with the Authorization: Bearer YOUR_API_KEY header.

2. Provide your input video

input_url (and input_urls) accept a publicly downloadable https URL to a media file — we stage your media into eachlabs storage automatically before processing. Private footage: a presigned URL from your own storage works — any https URL the engine can fetch is staged the same way (see Acceptable Use). s3:// input URIs from your onboarding keep working unchanged, and you can chain jobs by passing a previous job’s output URL as the next job’s input. Local files: upload them to eachlabs storage and pass the returned public_url directly as input_url.
The URL must point directly at a media file, identified by its file extension. The staged extensions are: video .mp4 .webm .mov .avi .mkv .flv; audio .mp3 .wav .ogg .flac .m4a .aac .opus .wma; image .png .jpg .jpeg .gif .webp .bmp .tiff; subtitle .vtt .srt .ass; color LUT .cube. A URL we cannot stage (unreachable, not public, or not identifiable as media) is rejected at $0 with PROVIDER_REJECTED and the offending URL in the message.A URL with no path extension at all can declare its type with a mime_type= query parameter (the parameter is only consulted when the URL path carries no extension — it cannot override an unrecognized extension). The value is the MIME type with the separators encoded as underscores — the first _ becomes /, any later _ becomes -: mime_type=video_mp4video/mp4, video_quicktimevideo/quicktime, audio_x_wavaudio/x-wav. Any video/*, audio/*, or image/* type is accepted (the subtype is not checked against a list); non-media types are rejected. Note .ts artifacts (hls_ladder segments, concat_copy container: "ts") are terminal today: .ts is not a staged extension and the extension blocks the mime_type= fallback, so they cannot be chained as inputs.
.cube LUTs stage only when the URL path carries the .cube extension — the mime_type= fallback accepts media types only, so an extensionless LUT URL is not staged. sendcmd scripts are not staged from public URLs at all: host them on eachlabs storage instead — an s3:// URI from your onboarding or a cdn-us.eachlabs.ai URL.

3. Submit a job

Any capability from the reference works the same way — a request carries capability, input_url, and params.
A successful submit returns:

4. Poll until terminal

The typical flow is createdprocessingsuccess. The complete status set is created, processing, success, error, cancelled — a failed prediction reports error, never "failed". created is the first status you see; the API never returns starting, so do not branch on it. Poll every 3-5 seconds: the platform’s own status checks are quantized to ~5 s, so tighter loops buy nothing. Most short jobs finish in well under a minute (a 100-job burst of short transcodes drains in ~15-30 seconds). A prediction sitting in created unusually long is queued behind a concurrency cap — keep polling; see Rate limits & concurrency.
  • output — hosted artifact URL(s) for media capabilities, or a structured JSON object for the analysis capabilities (probe, scene_detect, silence_detect, audio_analysis). The exact JSON per output class is in Response shapes.
  • metrics.cost — what the job actually billed, in USD: billed_seconds × 0.0015,plusdeliveryonoutputgbbeyondthe200MBfreeallowanceperjob(0.0015, plus delivery on `output_gb` beyond the 200 MB free allowance per job (0.30/GB on the excess) — see the formula. Rejected, failed, and cancelled jobs bill 0 (see Failed compute for how failures are metered).
  • metrics.billed_seconds, metrics.output_gb, metrics.tier — the billed quantities, present on every successful prediction. Failed predictions carry only cost and predict_time.
  • metrics.predict_time — the prediction’s full wall time in seconds, from processing start to completion detection, including internal queueing and poll quantization. It is not the billed quantity: cost comes from metrics.billed_seconds × 0.0015.Intheexampleabovebilledsecondsreads191191×0.0015. In the example above `billed_seconds` reads 191 → `191 × 0.0015 = $0.2865, while predict_time` reads 194.86 — see the formula.
  • input — your submitted input, echoed back. logs is present on every poll and is null today.
  • urls.get — this prediction’s poll URL. urls.cancel — its cancel endpoint (POST, same Authorization header); cancellation is record-only and a cancelled prediction never bills — see Cancelling a prediction.
  • On status: "error" the output object carries error_code and error_message — except on a dispatch failure at submit, which writes a minimal envelope carrying only error, so guard for their absence before reading them. The field-by-field envelope with two real captured examples, and the billing impact per class, are in the error catalog.
  • Download or persist artifacts promptly; treat artifact URLs as valid for at least 24 hours.

5. Optional: completion webhooks

Instead of polling, pass the platform’s standard webhook_url when creating the prediction and receive a callback on completion — see the webhooks documentation.

Next steps

Capabilities Reference

Every capability with parameters, request bodies, and response shapes.

Billing & Limits

The billing formula, the error catalog, and the operational limits.
Policies: Acceptable Use · Abuse & Takedown · Versioning & Deprecation
Last modified on August 13, 2026