Batch export
Per-image /download is the right tool for a selection. It is the wrong tool for a whole benchmark: 159,672 images is 159,672 requests. Batch export delivers the same images as prebuilt parquet shards, so a full pull is a handful of large files instead.
GET /api/v1/data/release?benchmark=<id>
Returns signed URLs for the shard sets your key is entitled to, the credits the pull will cost, and the coverage you have. It signs before it charges, so a failure never leaves you billed for bytes you did not receive. passport-pad-v1 is available for batch export now; other benchmarks return no_release until their shards are published, and stay available per-image in the meantime.
1{2"object": "release",3"coverage": "full",4"release_id": "passport-pad-v1-cap5",5"benchmark": "passport-pad-v1",6"shard_count": 23,7"entitled_images": 159672,8"benchmark_total": 159672,9"credits_required": 159672,10"charged": true,11"balance": 340328,12"expires_in": 21600,13"shards": [14{15"set": "print_scan",16"rows": 6904,17"media": 6904,18"bytes": 1806864384,19"part_count": 2,20"composition": { "kind": ["real", "fake"], "skin_tone": ["dark", "..."] },21"parts": [22{ "part": 0, "rows": 3635, "bytes": 943718400,23"sha256": "…", "url": "https://…?X-Amz-Signature=…" },24{ "part": 1, "rows": 3269, "bytes": 863145984,25"sha256": "…", "url": "https://…" }26]27}28]29}
Shard sets
A benchmark is split into shard sets, one per condition. Each set is a complete, self-contained slice: every image of the benchmark under that condition. Sets are disjoint at the image level, so no image ever appears in two of them, and buying several sets is simple addition with no overlap to reason about.
A large set is split into numbered parts of under 1 GB (print_scan-000.parquet, -001, …). Every set is part-numbered, including single-part ones, so you always iterate parts and never special-case. A set is one logical unit: load all of its parts or none.
passport-pad-v1 every condition covers the same base images. On some benchmarks a base corpus is large while the robustness conditions cover only a subset of it, so the sets differ in size. Each set's own rows and media count is in the response, so read it before you buy rather than after.How credits are counted
One credit per image delivered, the same price as pulling those images individually. A set of 2,399 images costs 2,399 credits.
The charge is net of images you already own. Buying a set claims ownership of every image in it, so a later /download of any of those images is free, and re-pulling the same set costs nothing. If you sampled 500 images individually first, a release containing them charges 500 less. You are never charged twice for the same image, by any route.
The response reports credits_required before anything is signed, so the cost is visible before you commit, and insufficient_credits names the shortfall without signing anything.
Coverage and partial access
A shard set is served only when it is fully inside your access. Every response carries a coverage field so a client can branch without reading prose:
full— the whole benchmark.scoped— a clean subset; the listed sets are exactly your entitlement.partial— your access does not line up with shard boundaries (for example a restriction on a demographic cell, or on an attribute that only some images in every set carry). No shards are returned and the response points you at the per-image endpoint, which can serve any selection. This is a 200, not an error: you are entitled to the data, just not in this format.empty— nothing here matches your access.
Any response that is not full tells you how many images you can reach against the benchmark total, and how to request more.
Signed URLs
Each part URL is signed and expires in six hours(expires_in is seconds). That covers a slow multi-gigabyte pull without becoming a durable link. URLs honour HTTP Range, so an interrupted part can resume rather than restart. If a link does expire mid-pull, call /release again: once you own a release, re-calling it is free and re-signs every part.
Loading the parquet
Each part is a standard parquet file and loads with pandas.read_parquet or datasets.load_dataset("parquet", …). The image bytes are in an image column as a struct of { bytes, path }; decode row["image"]["bytes"] with PIL.Image.open(io.BytesIO(...)). Concatenate a set's parts in any order and de-duplicate on id if you loaded overlapping sets; media_id joins the same base image across conditions.
Integrity
Every part carries a sha256 of the file as written. Verify after download; a truncated transfer is the failure you are most likely to hit on a multi-gigabyte pull, and it does not always announce itself.