Reference
API docs
One clean, versioned API per dataset. Two-segment paths, a stable envelope, and the license in every response header.
Base URL & auth
All endpoints live under https://api.datafloe.dev. Data reads require a Bearer key; catalog, schema, sample, and license are keyless.
Authorization: Bearer datafloe_live_… Get a free key — 100 calls/day, global across datasets. The demo key on each record page is scoped to the playground.
Endpoints
| method | path | auth | returns |
|---|---|---|---|
| GET | /v1/catalog | none | Machine-readable index: slug, title, schema/sample/license URLs, schema_hash, freshness |
| GET | /v1/datafloe/{dataset} | key | Rows (filter/sort/paginate) |
| GET | /v1/datafloe/{dataset}/schema | none | Columns, types, null %, row count |
| GET | /v1/datafloe/{dataset}/sample | none | Fixed 5 rows |
| GET | /v1/datafloe/{dataset}/license | none | Machine-readable license JSON |
Live datasets
- US grocery prices by area (USDA F-MAP) —
/v1/datafloe/grocery-prices - FDA recalls unified (food + drug + device) —
/v1/datafloe/fda-recalls
Query parameters
On the rows endpoint:
{column}=value— equality filter. Repeat a column for anIN(e.g.?product_type=Food&product_type=Drugs). Values are coerced to the column's type; string equality is case-sensitive.limit— default 50, max 1000.offset— for paging.sort— single column; prefix-for descending (rowid tiebreaker).count=true— includemeta.total(omitted by default).version— pin a dataset version.
Unknown filter columns return 400. Names starting with _ and containing __ are reserved.
Response envelope
{ "data": [ … ], "meta": { "limit": 50, "offset": 0, "version": "…", "total"?: 12806 } } Dates are RFC 3339 UTC. Nulls are explicit. Numbers are JSON numbers (values above 2^53 are documented per dataset).
Headers on every response
Including errors: X-License (the license JSON URL), X-Dataset-Version, X-Request-Id, and X-RateLimit-Limit/Remaining/Reset. CORS exposes all of them, so browsers can read X-License. Public endpoints carry a strong ETag — send If-None-Match for a 304.
Errors
Every error is application/problem+json (RFC 9457) with a stable type URI. Match on type, never on detail.
{
"type": "https://api.datafloe.dev/problems/rate-limited",
"title": "Rate limit exceeded",
"status": 429,
"detail": "Daily limit of 100 calls exceeded"
} | type | status |
|---|---|
| unauthorized | 401 |
| not-entitled | 403 |
| not-found | 404 |
| invalid-param | 400 |
| unknown-column | 400 |
| rate-limited | 429 |
| version-retired | 410 |
| payload-too-large | 413 |
| maintenance | 503 |
| internal | 500 |
The provenance standard
No dataset ships without a provenance record: the source URL, the original license, and the redistribution basis — written down before the API goes live. If we can't say where a row came from, we don't sell it.
Each dataset's /license endpoint returns this machine-readably, including a content hash and the declared origin. ai_training is false on every dataset by design. See the licenses & terms.