How long video transcoding takes on an API

Read where the time goes when a short clip is transcoded through a video API. Waiting to start took a median 3.6 s, over four times the FFmpeg encode.

Share

On short clips sent through a video API, the FFmpeg encode was the fastest part of the job. The median encode took 861 ms, while the median wait before any work started was 3,626 ms, more than four times longer. So if a transcode feels slow, send jobs in parallel and shrink the output before you touch the preset.

StepMedian90th percentileSlowest
Waiting to start3,626 ms21,966 ms119,886 ms (99th percentile)
Download the input69 ms653 ms15,812 ms
The FFmpeg encode861 ms3,886 ms110,103 ms
Upload the output578 ms1,644 ms82,646 ms
Probe metadata (standalone ffprobe job)171 ms411 ms4,367 ms

These are completed jobs in the Rendobar job archive as of 2026-08-20, dominated by one 5.013 second 1280x720 H.264 clip (setup in how we benchmark). Each timing is a single production run on shared infrastructure. Every job records how long each step took, so this breakdown came from reading finished jobs, with no extra instrumentation.

Why the queue dominates

Measured as a share of each job’s own total, waiting to start was 57.1% of the time at the median and 94.5% at the 90th percentile. On a typical short clip more than half the time passes before any work begins, and on a bad day almost all of it does.

Nothing is running during that window. The job has been accepted and its credits checked, and it is waiting for capacity. The figure runs from job creation to execution start, so it folds admission, dispatch and scheduling into one number. Cold starts contribute to the slowest waits, and I have not measured them separately.

The wait does not scale with the work. A five-second clip and a five-minute one queue the same way, which is why the queue share is worst on exactly the jobs people expect to be fast. A 200 ms encode behind a 3.6 second wait is a pipeline that spends 5% of its time working, and no preset changes that ratio.

The distribution is skewed. The 10th percentile wait was 629 ms, so medians describe the common case here and averages do not.

Parallel jobs help, up to your concurrency limit

With a hundred clips, submitting them together beats optimising any single one, because the waits overlap instead of adding up. That holds only up to your plan’s concurrency limit, which is 1 on the free plan and 20 on Pro. Past the limit, jobs wait in line for a free slot.

On the free plan, most of a hundred clips wait in line however you submit them, and the backlog holds at most 50 waiting jobs, so a larger batch has to be fed in as slots free up.

Upload is slower than download, by 8x

Download had a median of 69 ms and upload a median of 578 ms. Fetching an input pulls a file from a CDN edge built for reads. Writing an output puts a file into object storage, which is a different and slower operation. I have not isolated how much of the gap is output size and how much is write latency, and on compression jobs the output is often smaller than the input.

At the median, handing the result back takes 67% as long as producing it. Upload is the third-largest step after the wait and the encode, and on a short clip it is close enough to the encode that shrinking the output matters. A higher CRF, a more efficient codec or a smaller resolution shortens the upload as well as the encode, so encoder settings change latency twice. Across an ABR ladder this compounds, since in one measured five-rung ladder the upscaled 1080p rung took 63% of the encode time and 41% of the cost, about 1.7x the encode time of the other four rungs together.

The encode has the widest tail

The encode is fast in the common case and occasionally very slow. The slowest single run was 128 times the median.

Two things drive that. Longer or larger inputs do more work, as expected. The second catches people out: x264 with -threads auto sizes itself to whatever the machine has free, so identical work on a busy machine takes far longer than on an idle one. In a separate experiment, pinning threads cut run-to-run variance from 85% to between 1.4% and 8.9%, and FFmpeg benchmark variance has the full data.

So a 40% difference between two encoder settings, measured once, sits inside the machine’s noise. Sizes are the opposite: the same command produced byte-identical output on all ten runs tested, so a size comparison needs one run and a timing comparison needs many.

The probe is the cheapest step

Reading metadata took about a fifth of a median encode and about a twentieth of the median wait. It bills a flat $0.0010, and it catches the most common cause of failed jobs, an input that was never going to load, before you pay for an encode. Common FFmpeg errors has the failure breakdown, and validating uploads with ffprobe shows the probe as a gate.

Where the advice inverts

All of this is short-clip data. On long-form video the encode grows with duration while the wait does not, so on a ten-minute source the encode dominates and preset choice matters again. The encoding settings comparison covers that side.

For short clips my order is fixed: run jobs in parallel up to the concurrency limit, shrink the output, probe first, and only then look at the preset. Tuning the encode first optimises the smallest number in the table.

Frequently asked questions

Why is my video API slower than running FFmpeg locally?

A local run starts immediately and reads from your own disk. An API job waits for capacity, downloads the input and uploads the result, and on a clip of a few seconds those steps together take longer than the encode itself.

What makes video transcoding slow?

On short clips, waiting for capacity first, then the encode, then writing the output back, and the last two are close. On long sources the encode grows with duration and becomes the largest step.

Sources

Tags #ffmpeg#performance#latency#transcoding
All posts
Share
  1. How we benchmark FFmpeg Engineering blog
  2. Custom fonts in a video API fail silently Engineering blog
  3. Opus vs AAC vs MP3, requested vs delivered bitrate Engineering blog