Inspect any media file and get its technical metadata
Inspect any media file and get its technical metadata. Container, duration, codecs, DISPLAY dimensions and rotation, frame rate, HDR, audio channels and sample rate, in a normalized summary plus the full raw analysis (JSON, XML, CSV, flat or INI). Provide a source URL or upload the file directly; the result is downloadable for 24 hours. A file that cannot be decoded still answers with the diagnosis of why. Fixed price per probe in USDC over x402; no account or API key. Input up to 100 MB.
Paid test badge: not yet. The checks above are free: we call the tool without paying and read the payment request it sends back. The Verified badge needs paid calls whose answers match the promised output, and nobody can buy a badge.
Endpoint
POST https://ffpipe.dev/v1/probe
| Category | Image and media |
|---|---|
| Provider host | ffpipe.dev |
| Networks | eip155:8453 |
| Payment schemes | exact |
| Self-reported calls, 30 days | 5 from 2 payers (the provider's figure, not ours) |
Our checks, last 30 days
| Day | Result | HTTP | Asked | Time |
|---|---|---|---|---|
| 2026-09-24 | valid payment request | 402 | $0.01 | 536 ms |
Example input (from the provider)
{
"body": {
"sourceUrl": "https://storage.example.com/clip.mov?signature=abc123"
},
"bodyType": "json",
"method": "POST",
"type": "http"
}
Promised output schema (from the provider)
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"input": {
"additionalProperties": false,
"properties": {
"body": {
"additionalProperties": true,
"description": "Provide EXACTLY ONE of sourceUrl (we fetch it) or upload: true (you PUT the bytes to the uploadUrl returned in the 202). Everything else is optional: the defaults return the full analysis as JSON with a normalized summary block. Numbers and booleans must be JSON numbers/booleans, not strings. Unknown properties are ignored; validation runs before any charge.",
"oneOf": [
{
"required": [
"sourceUrl"
]
},
{
"required": [
"upload"
]
}
],
"properties": {
"countFrames": {
"default": false,
"description": "Count frames exactly, which requires decoding the whole file - slow on long sources. Leave it off unless an exact count is the point.",
"type": "boolean"
},
"countPackets": {
"default": false,
"description": "Count packets exactly. Same cost caveat as `countFrames`.",
"type": "boolean"
},
"outputFormat": {
"default": "json",
"description": "Format of the delivered analysis. `json` (the default) is the only one that carries the normalized `ffpipe_summary` block; the others are the raw writer output, untouched. `flat` is delivered as .txt.",
"enum": [
"json",
"xml",
"csv",
"flat",
"ini"
],
"type": "string"
},
"readIntervals": {
"description": "Limit the analysis to a window, e.g. `%+60` (the first 60 seconds), `30%+10` (10 seconds from 0:30) or `%+#500` (the first 500 packets). Applied automatically when frame/packet detail is requested without one. A malformed expression is a 400 (never charged).",
"examples": [
"%+60"
],
"maxLength": 256,
"type": "string"
},
"sections": {
"default": [
"format",
"streams",
"chapters"
],
"description": "Which sections to report. Defaults to format + streams + chapters. Duplicates are collapsed; an unknown name is a 400 (never charged).",
"items": {
"enum": [
"format",
"streams",
"chapters",
"programs"
],
"type": "string"
},
"type": "array"
},
"selectStreams": {
"description": "Restrict the analysis to matching streams, e.g. `v:0` (first video stream) or `a` (all audio). A specifier that matches nothing still succeeds and says so.",
"examples": [
"v:0"
],
"maxLength": 256,
"type": "string"
},
"showEntries": {
"description": "Keep only the named fields, e.g. `frame=pict_type,pts_time,key_frame` or `format=duration`. Section and field names only; a malformed expression is a 400 (never charged) rather than a job that cannot succeed.",
"examples": [
"frame=pict_type,pts_time,key_frame"
],
"maxLength": 256,
"type": "string"
},
"showFrames": {
"default": false,
"description": "Report per-FRAME detail. Large: unless you also send `readIntervals`, a bounded window is applied for you so the analysis cannot outgrow what we can deliver. Combine with `showEntries` to keep only the fields you need.",
"type": "boolean"
},
"showPackets": {
"default": false,
"description": "Report per-PACKET detail. Same bounded-window treatment as `showFrames`.",
"type": "boolean"
},
"sourceUrl": {
"description": "https URL we fetch the input from. Rejected before any charge if it is not https, is an IP literal, or resolves to a localhost/.internal/.local name; redirects are followed (max 3) and every hop is re-checked. We fetch from datacenter IP ranges - hosts that block datacenter egress will fail with `ingest_failed`, so prefer a presigned URL from storage you control. Body size cap: 100 MiB by default, enforced while streaming.",
"examples": [
"https://storage.example.com/clip.mov?signature=abc123"
],
"format": "uri",
"type": "string"
},
"summary": {
"default": true,
"description": "Keep the normalized `ffpipe_summary` block (JSON only). Send false for byte-exact raw output. PREFER the summary: it answers coded-vs-display dimensions, rotation, fps as a number and HDR directly, which is where hand-rolled parsing of the raw sections usually goes wrong.",
"type": "boolean"
},
"upload": {
"const": true,
"description": "Push intake: instead of a `sourceUrl`, reserve an upload window and PUT the raw bytes (<= 100 MiB, same cap) to the `uploadUrl` in the 202 before `uploadExpiresAt`. You are charged at the 202, before any bytes move: letting the window expire is a terminal `error` with no refund; a pipeline failure after your upload earns a `retryVoucher`.",
"type": "boolean"
}
}
},
"bodyType": {
"enum": [
"json",
"form-data",
"text"
],
"type": "