ProductGuru · Recalls · Data · Developers

Recall API & feeds

A free, open, read-only API over consumer-product recall notices compiled from 31 public regulatory authorities worldwide. No key. No account. No write endpoints. Every record names the authority that issued it and links to that authority’s own notice.

The one call that matters
curl -s "https://myproduct.guru/api/v1/recalls/3109850018511"
Is this barcode recalled anywhere in the world?

At a glance

Base URLhttps://myproduct.guru/api/v1
AuthNone. No key, no registration, no tiers.
MethodsGET and HEAD only. There are no write endpoints.
FormatJSON (UTF-8). CORS Access-Control-Allow-Origin: * — call it straight from the browser.
Rate limit60 requests/minute per IP, burst 20. Over the limit you get 429 with Retry-After.
CachingEvery response carries ETag and Last-Modified. Send If-None-Match, get 304.
LicenceCC-BY-4.0 — header X-Data-License: CC-BY-4.0
Cite asProductGuru consumer recall dataset (https://myproduct.guru), CC BY 4.0, DOI 10.5281/zenodo.22685727 — 10.5281/zenodo.22685727
OpenAPI/api/v1/openapi.json
authority vs issuing_body. authority is the register a notice was compiled from; issuing_body is the government body that actually issued it. For most registers these are the same. They differ for shared portals — canada-recall-portal carries Transport Canada, the Canadian Food Inspection Agency and several Health Canada branches side by side, so we resolve the issuer per notice rather than crediting one regulator for all of them. Where the portal does not say, issuing_body names the portal itself; we never guess a regulator.

Endpoints

GET /api/v1/recalls/{ean} — one barcode

The core call. Pass an EAN/GTIN of 8–14 digits. Returns every notice on file for that barcode, newest first.

curl -s "https://myproduct.guru/api/v1/recalls/3109850018511"
{
  "meta": { "license": "CC-BY-4.0", "doi": "10.5281/zenodo.22685727", ... },
  "ean": "3109850018511",
  "recalled": true,
  "count": 1,
  "recalls": [
    {
      "authority": "rappelconso-france",
      "authority_name": "RappelConso — Government of France",
      "authority_url": "https://rappel.conso.gouv.fr/",
      "notice_id": "2024-05-0123",
      "notice_url": "https://rappel.conso.gouv.fr/fiche-rappel/.../interne",
      "published_date": "2024-05-14",
      "first_indexed_at": "2024-05-15T03:12:44Z",
      "country": "FR",
      "country_name": "France",
      "risk_level": null,
      "authority_classification": "volontaire (sans arrêté préfectoral)",
      "hazard": "… the authority's own hazard wording …",
      "product_name": "antesite reglisse anis",
      "product_category": "Food",
      "ean": "3109850018511",
      "productguru_url": "https://myproduct.guru/wiki/recall/…"
    }
  ]
}

"recalled": false with "count": 0 is a normal 200 — a barcode with no notice on file is an answer, not an error. A string that is not 8–14 digits is a 400.

GET /api/v1/recalls — paged listing

Filter by date, country, authority, category or brand. Newest first.

curl -s "https://myproduct.guru/api/v1/recalls?since=2026-08-01&country=FR&limit=5"

Everything the EU Safety Gate recalled in the Toys category.

curl -s "https://myproduct.guru/api/v1/recalls?authority=eu-safety-gate&category=Toys&limit=50"

Polling for new data? Sort by indexed and filter on updated_since — that is when we first saw the notice, so nothing slips past you when an authority backdates one.

curl -s "https://myproduct.guru/api/v1/recalls?updated_since=2026-09-01&sort=indexed&limit=100"
ParameterMeaning
since, untilYYYY-MM-DD. Filters the authority’s publication date.
updated_sinceYYYY-MM-DD. Filters when ProductGuru first indexed the notice.
countryISO 3166-1 alpha-2, e.g. FR. See /countries.
authoritySlug from /authorities, e.g. us-cpsc.
category, brandExact match, case-insensitive.
sortpublished (default) or indexed.
limit1–100, default 20.
offset0–10,000. Past that, narrow the window with since/until — deep offsets are slow for everyone.

The response’s meta.next is the ready-made URL for the following page, or null at the end.

GET /api/v1/recalls/stats — dataset shape

Totals by authority, by year and by country, plus when the dataset was last added to. Cached for an hour.

curl -s "https://myproduct.guru/api/v1/recalls/stats"

GET /api/v1/authorities — who we compile from

Slug, official name, homepage and current notice count for every register.

curl -s "https://myproduct.guru/api/v1/authorities"

GET /api/v1/countries

Every ISO 3166-1 alpha-2 code present in the data, with counts.

curl -s "https://myproduct.guru/api/v1/countries"

Feeds

Newly indexed notices, as they land. Both feeds accept the same ?authority=, ?country= and ?limit= filters as the listing endpoint.

Subscribe in any reader, or wire it into Slack, Zapier or an alerting job.

curl -s "https://myproduct.guru/recalls.xml"                    # RSS 2.0
curl -s "https://myproduct.guru/recalls.atom"                   # Atom 1.0
curl -s "https://myproduct.guru/recalls.xml?country=DE&limit=25"  # filtered

A worked example

Python, standard library only — no SDK, no key.

import urllib.request, json
url = "https://myproduct.guru/api/v1/recalls/3109850018511"
with urllib.request.urlopen(url) as r:
    data = json.load(r)
print(data["recalled"], data["count"])
for rec in data["recalls"]:
    print(rec["authority_name"], "-", rec["hazard"][:80])
    print("  official notice:", rec["notice_url"])

Caching and etiquette

Send back the ETag you were given and unchanged pages cost you nothing.

ETAG=$(curl -sI "https://myproduct.guru/api/v1/recalls?limit=5" \
        | awk -F": " 'tolower($1)=="etag"{print $2}' | tr -d "\r")

curl -s -o /dev/null -w '%{http_code}\n' \
     -H "If-None-Match: $ETAG" "https://myproduct.guru/api/v1/recalls?limit=5"
# 304

Attribution

The data is licensed CC-BY-4.0. Use it commercially, remix it, ship it in an app — just say where it came from:

ProductGuru consumer recall dataset (https://myproduct.guru), CC BY 4.0, DOI 10.5281/zenodo.22685727
ProductGuru is the compiler, not the issuing body. Every record carries authority_name and notice_url pointing at the register that actually issued it. When something matters, verify it there. This API is informational only and is not legal, medical or safety advice.

Registers we compile

SlugAuthorityJurisdiction
accc-australiaAustralian Competition and Consumer CommissionAU
bvl-germanyBundesamt für Verbraucherschutz und Lebensmittelsicherheit (Germany)DE
eu-safety-gateEuropean Commission — Safety Gate (RAPEX)EU
fsai-irelandFood Safety Authority of IrelandIE
fsanz-australia-nzFood Standards Australia New ZealandAU
canada-recall-portalGovernment of Canada — recall portal (recalls-rappels.canada.ca)CA
gis-polandGłówny Inspektorat Sanitarny (Poland)PL
oecd-globalrecalls-bnOECD GlobalRecalls — Brunei DarussalamBN
oecd-globalrecalls-twOECD GlobalRecalls — Chinese TaipeiTW
oecd-globalrecalls-coOECD GlobalRecalls — ColombiaCO
oecd-globalrecalls-crOECD GlobalRecalls — Costa RicaCR
oecd-globalrecalls-idOECD GlobalRecalls — IndonesiaID
oecd-globalrecalls-ilOECD GlobalRecalls — IsraelIL
oecd-globalrecalls-jpOECD GlobalRecalls — JapanJP
oecd-globalrecalls-krOECD GlobalRecalls — KoreaKR
oecd-globalrecalls-myOECD GlobalRecalls — MalaysiaMY
oecd-globalrecalls-phOECD GlobalRecalls — PhilippinesPH
oecd-globalrecalls-sgOECD GlobalRecalls — SingaporeSG
oecd-globalrecalls-thOECD GlobalRecalls — ThailandTH
oecd-globalrecalls-aeOECD GlobalRecalls — United Arab EmiratesAE
oecd-globalrecalls-vnOECD GlobalRecalls — Viet NamVN
rappelconso-franceRappelConso — Government of FranceFR
senacon-brazilSenacon — Secretaria Nacional do Consumidor (Brazil)BR
transport-canadaTransport Canada — Vehicle Recalls DatabaseCA
us-cpscU.S. Consumer Product Safety CommissionUS
us-fda-foodU.S. Food and Drug Administration — Food EnforcementUS
us-fda-devicesU.S. Food and Drug Administration — Medical DevicesUS
us-nhtsaU.S. National Highway Traffic Safety AdministrationUS
uk-dvsaUK Driver and Vehicle Standards AgencyGB
uk-fsaUK Food Standards AgencyGB
uk-opssUK Office for Product Safety and StandardsGB