API reference
The baseline catalog is public. Files and full fact sheets need a token. Everything is plain HTTPS and JSON, so any test runner, script or agent can use it.
https://testsrc.com/v1The API is in early access. Ask for a token: free for open-source projects, $99/month per organisation for companies.
Basics
- Tokens
- Send
Authorization: Bearer tsk_.... Create and revoke tokens on your account, one per place they run, and keep them in a CI secret. - Snapshots
- Every file belongs to a snapshot named by date. Add
?snapshot=2026-11-02to pin one: the same snapshot always returns the same bytes. Without it you get the latest. - Browsers and players
- CORS and range requests are on, so a web player or a test page can fetch files directly.
The file, then its facts, pinned to a snapshot
curl -H "Authorization: Bearer $TESTSRC_TOKEN" -O \ "https://testsrc.com/v1/files/codec/hevc-aac.mp4?snapshot=2026-11-02" curl -H "Authorization: Bearer $TESTSRC_TOKEN" \ "https://testsrc.com/v1/cases/codec/hevc-aac.mp4/facts?snapshot=2026-11-02"
Endpoints
/v1/catalog
PublicThe cases in the snapshot: each one's id, layer (1 for baseline files, 2 for edge cases), expected outcome, size in bytes, and the files it consists of. Streaming cases list their playlist and segments.
Without a token it lists the baseline files and the edge cases described on testsrc.com/cases, and says how many more there are; with a token, every case.
| Parameter | In | Description |
|---|---|---|
snapshot | query | Optional. A snapshot name; the latest if left out. |
Authorization | header | Optional. With a token, the catalog lists every case. |
Request
curl -H "Authorization: Bearer $TESTSRC_TOKEN" https://testsrc.com/v1/catalog
Response
{
"snapshot": "2026-11-02",
"count": 163,
"cases": [
{
"id": "codec/av1-aac.mp4",
"layer": 1,
"outcome": "must-play",
"size_bytes": 3602413,
"files": ["av1-aac.mp4"]
},
{
"id": "hls/ts.m3u8",
"layer": 1,
"outcome": "must-play",
"size_bytes": 5616913,
"files": ["ts.m3u8", "ts_000.ts", "ts_001.ts", "ts_002.ts"]
},
…
]
}
/v1/snapshots
PublicThe snapshot names and which one is the latest.
Request
curl https://testsrc.com/v1/snapshots
Response
{ "latest": "2026-11-02", "snapshots": ["2026-10-29", "2026-11-02"] }
/v1/cases/{id}
PublicWhat a case is. Baseline cases come with their full fact sheet and recipe; edge cases with headline facts. Edge cases that aren't on testsrc.com/cases answer only with a token.
| Parameter | In | Description |
|---|---|---|
id | path | The case id from the catalog, like edge/rotation-90-display-matrix.mp4. |
snapshot | query | Optional. |
Request
curl https://testsrc.com/v1/cases/edge/rotation-90-display-matrix.mp4
Response
{
"id": "edge/rotation-90-display-matrix.mp4",
"layer": 2,
"why": null,
"files": ["rotation-90-display-matrix.mp4"],
"size_bytes": 5532359,
"verified_with": {
"generator": "gen v0.2",
"ffmpeg": "7.1.5-0+deb13u1",
"gstreamer": "1.26.2",
"image": "sha256:c74db3c8f454"
},
"container": { "format": "mov,mp4,m4a,3gp,3g2,mj2", "duration_s": 6 },
"video": { "codec": "h264", "width": 1920, "height": 1080, "fps": "30" },
"audio": [{ "codec": "aac", "channels": 2, "sample_rate": 48000 }],
"more": "Full facts, exact parameters and the expected outcome: GET /v1/cases/{id}/facts with a token."
}
/v1/cases/{id}/facts
TokenThe full fact sheet: container layout, every measured value, keyframes, the expected outcome and what a test can assert, with tolerances, and per-frame decode checksums where the codec allows them.
| Parameter | In | Description |
|---|---|---|
id | path | The case id. |
snapshot | query | Optional. |
Request
curl -H "Authorization: Bearer $TESTSRC_TOKEN" \
https://testsrc.com/v1/cases/codec/av1-aac.mp4/facts
Response
{
"id": "codec/av1-aac.mp4",
"layer": 1,
…,
"expect": {
"outcome": "must-play",
"duration_s": 6,
"duration_tol_s": 0.15,
"frames": 180,
"first_code": 0,
"fps": 30,
"display_aspect": "16:9",
"orientation": "none",
"colour": { "matrix": "bt709", "range": "limited", "tol": 8 },
"max_log_errors": 0,
"exact_seek": false,
"beep_every_frames": 30,
"av_offset_ms": 0,
"av_tol_ms": 20
},
"gated": {
"frame_md5": {
"decoder_output": "yuv420p",
"frames": [
"a4d1b7fa8d7a711139129a8c9378c17f",
"2a31ab08c8a1aafe2da7376f75803647",
…
]
}
}
}
/v1/files/{path}
TokenA test file, or a playlist or segment of a streaming case. Files never change within a snapshot, so they're served with an ETag and cached as immutable. A trial token counts each case once, whichever of its files it fetches.
| Parameter | In | Description |
|---|---|---|
path | path | A case id, or a file next to it from the catalog's files, like hls/ts_001.ts. |
Range | header | Optional. bytes=0-1023, open-ended and suffix ranges answer 206. |
snapshot | query | Optional. |
Request
curl -H "Authorization: Bearer $TESTSRC_TOKEN" -r 0-1023 -D - -o /dev/null \
https://testsrc.com/v1/files/codec/av1-aac.mp4
Response headers
HTTP/2 206 content-type: video/mp4 content-range: bytes 0-1023/3602413 content-length: 1024 accept-ranges: bytes cache-control: private, max-age=86400, immutable x-testsrc-snapshot: 2026-11-02
Players that can send a header play HLS and DASH cases straight from the API:
ffmpeg -headers "Authorization: Bearer $TESTSRC_TOKEN"$'\r\n' \
-i https://testsrc.com/v1/files/hls/ts.m3u8 -f null -
/v1/usage
TokenWhat the token used today (UTC), its limits and when they reset. Doesn't count against the quota.
Request
curl -H "Authorization: Bearer $TESTSRC_TOKEN" https://testsrc.com/v1/usage
| Field | Description |
|---|---|
token | The token's id, like t_1a2b3c4d5e. The token itself is never echoed. |
plan | oss, company or trial. |
day | The UTC day the counts are for. |
used.requests, used.bytes | Requests and bytes served today. |
limits.requests_per_day, limits.bytes_per_day | The daily quota. |
limits.distinct_cases | How many different cases the token may fetch: a number for trial tokens, null otherwise. |
resets_in_s | Seconds until 00:00 UTC. |
Limits
Requests are limited per minute and per UTC day, in requests and in bytes; /v1/usage
shows where a token stands. A trial token can fetch 30 different cases; the full catalog opens after the first
payment. When a limit is hit, the response says which one and when it resets.
Errors
Errors are JSON with a message for people and a stable code for programs:
{
"error": "Daily quota used up. It resets at 00:00 UTC. See GET /v1/usage.",
"code": "quota"
}
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_snapshot | The snapshot isn't a date like 2026-11-02. |
| 401 | no_token | No Authorization header. |
| 401 | bad_token | Unknown, malformed or revoked token. |
| 403 | trial_limit | The trial token has fetched its 30 cases. |
| 404 | no_case, no_file, no_snapshot | Not in that snapshot. |
| 405 | method_not_allowed | Only GET and HEAD (and CORS preflight) are served. |
| 429 | rate_limited | Too many requests this minute. Retry after Retry-After seconds. |
| 429 | quota | Daily quota used up. It resets at 00:00 UTC; Retry-After says when. |