API documentation

A read-mostly REST API over normalised government recall data. Every response carries data_as_of, and every list is cursor-paginated. Examples below are rendered from the live corpus.

Authentication

Send your key as a bearer token. X-API-Key is accepted as an alternative for clients that cannot set Authorization.

curl https://api.recallproven.com/v1/recalls?limit=1 \
  -H "Authorization: Bearer rk_xxxxxxxxxxxxxxxx_..."

Keys are issued on request and are shown once — we store only a hash and cannot recover a lost key. /v1/health and this specification need no key at all.

Rate limits and quotas

TierPriceRequests / monthIdentifiers / monthBurst
free Free 500 5,000 10/min
starter $29/mo 10,000 100,000 60/min
growth $99/mo 100,000 1,000,000 300/min
scale $299/mo 1,000,000 10,000,000 1000/min

The monthly quota is the limit that governs. Burst limits are enforced per Cloudflare location and are intentionally permissive, so treat them as a courtesy rather than a contract. Exceeding the monthly quota returns 402; exceeding a burst limit returns 429 with Retry-After.

Errors

Errors are RFC 9457 problem documents served as application/problem+json. Branch on type, which is stable; title may be reworded.

{
  "type": "/problems/invalid-parameter",
  "title": "Invalid query parameter",
  "status": 400,
  "detail": "Unknown query parameter. Supported filters: jurisdiction, source, ...",
  "instance": "/v1/recalls",
  "parameter": "jurisdication"
}
typeMeaning
/problems/unauthorizedunauthorized
/problems/forbiddenforbidden
/problems/quota-exceededquota exceeded
/problems/rate-limitedrate limited
/problems/not-foundnot found
/problems/invalid-parameterinvalid parameter
/problems/invalid-cursorinvalid cursor
/problems/internalinternal

Unknown query parameters are rejected rather than ignored. A typo in a filter would otherwise return the entire corpus and look like a working query.

Caching

Every response carries a weak ETag. Send it back as If-None-Match and an unchanged corpus returns 304 with no body. The data is rebuilt at most once a day, so this is the difference between a cheap integration and an expensive one.

curl -H "Authorization: Bearer $KEY" \
     -H 'If-None-Match: W/"1d22962d4764190921860e3c8553e2d1"' \
     https://api.recallproven.com/v1/recalls?limit=50
# HTTP/1.1 304 Not Modified

Recalls

List recalls

Requires an API key
GET /v1/recalls

Recalls newest first.

Parameters

NameTypeDescription
jurisdiction string ISO-ish jurisdiction code as published, e.g. `EU`, `FR`, `US`.
source string Restrict to one source feed. See `/v1/sources`.
hazard_category string Canonical hazard taxonomy value.
severity string Normalized severity.
One of: serious, high, medium, low, unknown
remedy string Normalized remedy offered to consumers.
One of: repair, replace, refund, dispose, stop_use, other, unknown
from string Earliest `recall_date`, inclusive (YYYY-MM-DD).
to string Latest `recall_date`, inclusive (YYYY-MM-DD).
limit integer Rows per page. Default 50, maximum 200.
cursor string Opaque cursor from `meta.next_cursor` of the previous page. Do not construct by hand; its encoding is not part of the contract.
include_total boolean Include `meta.total`. Off by default because an exact count scans every matching row, which costs materially more than the page itself.

Request

curl https://api.recallproven.com/v1/recalls \
  -H "Authorization: Bearer $KEY"

Response — real output from the live corpus

{
  "data": [
    {
      "id": "9aece4d6115ea7273c80a6027a5025c2",
      "source": {
        "id": "nhtsa",
        "record_id": "26V329000",
        "agency": "NHTSA",
        "jurisdiction": "US"
      },
      "title": "CADILLAC ESCALADE ESV — AIR BAGS:FRONTAL:DRIVER SIDE:INFLATOR MODULE",
      "summary": "General Motors, LLC (GM) is recalling certain 2015 Cadillac Escalade, Escalade ESV, XTS, 2015-2016 Cadillac ATS, CTS, and SRX vehicles. The front-driver air bag inflator may rupture during deployment.",
      "hazard": {
        "category": "other",
        "text": "An inflator rupture may result in sharp metal fragments striking the driver or other occupants, resulting in serious injury or death."
      },
      "product": {
        "category": "motor_vehicle",
        "brands": [
          "General Motors, LLC"
        ],
        "models": [
          "CADILLAC ESCALADE ESV 2015",
          "CADILLAC SRX 2015",
          "CADILLAC ATS 2016",
          "CADILLAC CTS 2015",
          "CADILLAC CTS 2016",
          "CADILLAC SRX 2016",
          "CADILLAC XTS 2015",
          "CADILLAC ESCALADE 2015",
          "CADILLAC ATS 2015"
        ]
      },
      "severity": "unknown",
      "source_classification": "AIR BAGS:FRONTAL:DRIVER SIDE:INFLATOR MODULE",
      "remedy": "replace",
      "recall_date": "2027-08-16",
      "published_date": "2026-05-21",
      "countries": [
        "United States"
      ],
      "url": "https://www.nhtsa.gov/recalls?nhtsaId=26V329000",
      "revision": 20,
      "data_as_of": "2026-09-13",
      "first_seen_at": "2026-08-16T09:30:47.092Z",
      "last_seen_at": "2026-09-13T09:31:08.446Z"
    }
  ],
  "meta": {
    "count": 1,
    "limit": 1,
    "next_cursor": "eyJkIjoiMjAyNi0wNy0yNCIsImkiOiJhYmMifQ",
    "data_as_of": "2026-09-13",
    "attribution": [
      "Source: …"
    ]
  }
}

Fetch one recall

Requires an API key
GET /v1/recalls/{id}

Returns a single recall including every extracted product identifier.

Parameters

NameTypeDescription
idrequired string Stable recall id, as returned in `data[].id`.

Request

curl https://api.recallproven.com/v1/recalls/RECALL_ID \
  -H "Authorization: Bearer $KEY"

Amendment history for a recall

Requires an API key
GET /v1/recalls/{id}/revisions

Every observed change to this record, oldest first, with the field-level delta and the date we observed it.

Parameters

NameTypeDescription
idrequired string Stable recall id.
limit integer Rows per page. Default 50, maximum 200.

Request

curl https://api.recallproven.com/v1/recalls/RECALL_ID/revisions \
  -H "Authorization: Bearer $KEY"

Lookup

Find recalls by product identifier

Requires an API key
GET /v1/identifiers/{kind}/{value}

The catalogue-check endpoint.

Parameters

NameTypeDescription
kindrequired string Identifier type.
One of: gtin, upc, ean, vin, ndc, udi, model, lot, batch
valuerequired string The identifier as printed on the product or in your catalogue.
limit integer Rows per page. Default 50, maximum 200.

Request

curl https://api.recallproven.com/v1/identifiers/gtin/00012345678905 \
  -H "Authorization: Bearer $KEY"

Compliance checks

Check a catalogue and record the evidence

Requires an API key
POST /v1/checks

Submits product identifiers, matches them against the corpus, and records an immutable receipt that the check happened.

Request

curl -X POST https://api.recallproven.com/v1/checks \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"Q3 audit","identifiers":[{"kind":"gtin","value":"00012345678905"}]}'

Response — real output from the live corpus

{
  "data": {
    "check_id": "186948a03f7c4eec76feb148a715b387",
    "label": "Q3 audit",
    "performed_at": "2026-09-13T11:23:47.403Z",
    "submitted_count": 2,
    "submitted_digest": "644639bfbbd085d8484122214dfc92e16f0c6d6cceabefbd362c27f22c427f0b",
    "rejected_count": 0,
    "rejected": [],
    "match_count": 1,
    "result_digest": "fd4a973133a9bd937127cad0cdd2e6d63cca678060f917a22c2a461f654b1d6e",
    "corpus_as_of": "2026-09-13",
    "corpus_size": 176037,
    "matches": [
      {
        "recall_id": "9aece4d6115ea7273c80a6027a5025c2",
        "matched_on": {
          "kind": "gtin",
          "normalized": "00012345678905"
        },
        "revision": 20,
        "title": "CADILLAC ESCALADE ESV — AIR BAGS:FRONTAL:DRIVER SIDE:INFLATOR MODULE",
        "jurisdiction": "US",
        "recall_date": "2027-08-16",
        "severity": "unknown",
        "url": "https://www.nhtsa.gov/recalls?nhtsaId=26V329000"
      }
    ]
  },
  "meta": {
    "count": 1,
    "data_as_of": "2026-09-13"
  }
}

Your check history

Requires an API key
GET /v1/checks

Every check recorded against your API key, newest first.

Parameters

NameTypeDescription
limit integer Rows per page. Default 50, maximum 200.

Request

curl https://api.recallproven.com/v1/checks \
  -H "Authorization: Bearer $KEY"

Retrieve one check receipt

Requires an API key
GET /v1/checks/{id}

The full receipt including the matches frozen as they stood at check time.

Parameters

NameTypeDescription
idrequired string Check id from `POST /v1/checks`.

Request

curl https://api.recallproven.com/v1/checks/RECALL_ID \
  -H "Authorization: Bearer $KEY"

Metadata

Data sources, licences and attribution

No key required
GET /v1/sources

Every upstream feed with its licence, the attribution string its licence requires, and its observed update cadence.

Request

curl https://api.recallproven.com/v1/sources \
  -H "Authorization: Bearer $KEY"

Ingestion freshness

No key required
GET /v1/health

Per-source last successful ingestion and upstream freshness stamp.

Request

curl https://api.recallproven.com/v1/health \
  -H "Authorization: Bearer $KEY"

Response — real output from the live corpus

{
  "status": "ok",
  "sources": [
    {
      "id": "cpsc",
      "cadence": "continuous",
      "last_success_at": "2026-09-13T09:40:58.246Z",
      "data_as_of": "2026-09-10",
      "consecutive_failures": 0
    },
    {
      "id": "eu-safety-gate",
      "cadence": "weekly (published every Friday)",
      "last_success_at": "2026-09-13T10:01:02.471Z",
      "data_as_of": "2026-09-13",
      "consecutive_failures": 0
    }
  ],
  "degraded": []
}

Need the archive instead?

The complete corpus is available as a one-time licensed export — a dated, checksummed set of JSON Lines files with every identifier, the full amendment history and all attribution obligations documented. Enquire.