# Common ffprobe commands compared

Canonical: https://rendobar.com/blog/ffprobe-commands-compared/
Author: Abdelrahman Essawy
Published: 2026-08-20
Updated: 2026-08-20

---

## Key takeaways

- 213 probe jobs ran 25 distinct ffprobe command shapes. 200 completed and 13 did not.
- The median run time of every single command shape landed between 95 ms and 402 ms, so which flags you pass barely moves the clock.
- The spread within one shape was far larger than the spread between shapes: 94 ms to 4,367 ms, a 46x range driven by network and file layout.
- The ffprobe prefix is optional and so is almost everything else. 107 of the 213 jobs passed nothing but a URL.
- Every probe run since 2026-07-20 billed exactly $0.0010, with no variance across 169 consecutive jobs.

## Short version

`ffprobe` reads a media file's metadata and prints it. The flags decide which sections you get and in what shape, and almost nothing else.

We pulled 213 ffprobe jobs out of Rendobar's job history. They ran **25 distinct command shapes**, 200 completed and 13 did not. Every shape's median run time landed between **95 ms and 402 ms**, which is the useful headline: the flags you choose change what you see, not what it costs you in time.

The variation that matters is inside a single shape rather than between shapes. The same command against different files ran anywhere from **94 ms to 4,367 ms**.

## The commands, with what they measured

Medians below are across the jobs that used that exact shape, so the counts are honest and some are small. Read them as an order of magnitude and nothing finer.

| Command shape | Completed jobs | Median |
|---|---|---|
| just a URL, no flags | 107 | 171 ms |
| `-show_format -show_streams` | 34 | 282 ms |
| `ffprobe <url>` | 10 | 294 ms |
| `-v quiet -print_format json -show_format -show_streams` | 8 | 251 ms |
| `-show_streams` | 5 | 113 ms |
| `-v error -show_entries format=duration:stream=index -of json` | 4 | 97 ms |
| `-v quiet -print_format json -show_format` | 3 | 302 ms |
| `-print_format csv -show_format` | 3 | 101 ms |
| `-show_frames -read_intervals %+#5` | 3 | 120 ms |
| `-select_streams v:0 -show_entries stream=codec_name,width,height,r_frame_rate -print_format json` | 3 | 101 ms |

The gap between the fastest and slowest median here is under 4x, on samples as small as three jobs, against different files, over a network. That is not enough to call one shape faster than another and we are not going to. What it does establish is that **no shape in this set is expensive**. There is no flag here that turns a 100 ms operation into a 10 second one.

`-count_frames` would be the exception, because it decodes packets rather than reading a header, but it did not appear in this archive often enough to publish a number for.

## The prefix is optional and so is most of the rest

116 of 213 jobs, half the corpus, passed **nothing but a URL**. No `ffprobe`, no flags.

That works because sensible defaults fill in for flags you did not pass. JSON output, format, streams and chapters all get added when absent, and anything you did specify is left exactly as written. So a command copied out of a Stack Overflow answer runs the way it does on your laptop, and a bare URL still returns something useful.

The practical effect is that these three are equivalent for most purposes:

```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
ffprobe input.mp4
input.mp4
```

## Picking the right shape for the job

**Looking at a file by hand.** Use the full JSON. You want everything, and you are reading it rather than parsing it.

```bash
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
```

**Pulling one field in a script.** Use `-show_entries` with `-of csv=p=0`. Anything else makes you parse output you did not want.

```bash
ffprobe -v error -show_entries format=duration -of csv=p=0 input.mp4
```

**Deciding what to do next.** Select the stream, then ask for the fields the decision depends on. This is the shape that appeared before compose and transcode jobs in the archive.

```bash
ffprobe -v error -select_streams v:0 \
  -show_entries stream=codec_name,width,height,r_frame_rate \
  -print_format json input.mp4
```

**Checking the start of a file without reading all of it.** `-read_intervals %+#5` stops after five packets, which is enough to see whether the first frames decode at all.

```bash
ffprobe -show_frames -read_intervals %+#5 input.mp4
```

## The output shapes

`-print_format` (or `-of`, they are the same flag) decides the writer.

`json` is the one to use in code. It nests cleanly, it survives commas in tag values, and every language reads it. `csv` is the one to use in a shell when you want a single value, especially with `p=0` to drop the section prefix. `flat` and `ini` produce assignable key-value lines, which suit `eval` in a shell script if you trust the input. `xml` exists.

The trap in `csv` is that it does not quote fields, so a title tag containing a comma splits into two columns. If the value could contain arbitrary text, use `json` and parse it properly.

## Cost, and why it is a flat number

Every probe run since **2026-07-20 billed exactly $0.0010**, across **169 consecutive jobs** with no variance at all. Before that date the amounts moved with execution time and ranged down to $0.000091.

A flat floor is the right shape for this operation. The work is dominated by fetching enough of the file to read a header, and the difference between a 40 KB JPEG and a six-minute MP3 is not worth metering. It also makes the number predictable, which matters when a probe runs before every job in a pipeline rather than occasionally.

For comparison, the FFmpeg jobs in the same archive have a median of $0.0025 and a 90th percentile of $0.0042 over the last 425 runs. Probing costs roughly **40% of what a median encode costs**, and it is the step that tells you whether the encode is worth running at all.

## Running it over HTTP

Every measurement on this page came from a job submitted to Rendobar's FFmpeg API, which takes the ffprobe command as a string.

const rb = createClient({ apiKey: process.env.RENDOBAR_API_KEY });

const job = await rb.jobs.run({
  type: "ffprobe",
  params: {
    command:
      "ffprobe -v quiet -print_format json -show_format -show_streams https://cdn.rendobar.com/assets/examples/sample.mp4",
  },
});

// Raw ffprobe report, exactly as the binary produced it.
console.log(job.output.data.format.format_name); // "mov,mp4,m4a,3gp,3g2,mj2"

// Plus a normalised summary: codec, resolution, fps, rotation, HDR, audio layout.
console.log(job.output.data.summary.video); // { codec: "h264", width: 1280, ... }`}
  curl={`curl -X POST https://api.rendobar.com/jobs \
  -H "Authorization: Bearer $RENDOBAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "ffprobe",
    "params": { "command": "ffprobe -v quiet -print_format json -show_format -show_streams https://cdn.rendobar.com/assets/examples/sample.mp4" }
  }'`}
/>

The response carries the raw ffprobe report and a normalised `summary` alongside it, with codec, resolution, duration, fps, rotation, HDR signalling and audio layout already resolved. That summary exists because reading those fields correctly out of raw ffprobe output is harder than it looks.

## Where this stops

Every number here is an observation from production traffic, not a controlled benchmark. The jobs ran on different days against different files over a network we do not control, so the timings carry all of that variance and none of it was held constant. Several shapes have a sample of one to three jobs and their medians should be read as "roughly this order" rather than as measurements.

The 13 jobs that did not complete are not analysed here. The list endpoint does not carry an error code, so classifying them needs a separate pass.

Nothing here covers `-count_frames` at scale, MKV or WebM inputs, HLS or DASH manifests, or files large enough for header placement to dominate. Those are the cases where a probe stops being a fixed 200 ms operation.

Two posts go deeper on specific traps. [How to get video duration with ffprobe](/blog/ffprobe-video-duration/) covers why the container and the video stream disagree by up to 493 ms. [ffprobe metadata gotchas](/blog/ffprobe-metadata-gotchas/) covers rotation, fake image durations, missing channel layouts and cover art posing as video.
