Match a product to official recalls, and see exactly why it matched
The Product Safety & Recall Match API matches a product to safety recalls as clean JSON.
3 active endpoints, on 2 and 3 credit tiers.
- POST/enrich-recall/v1/product_match
- POST/enrich-recall/v1/brand_check
- POST/enrich-recall/v1/batch
What Product Safety & Recall Match endpoints does ReefAPI ship?
3 live read endpoints. Read-only data API: no writes, no account actions, no dashboard access on the target site.
Product Safety & Recall Match API
3 of 3 endpoints, ready to run
One product against the official recall record. High-confidence hits — where the brand matched independently — come back in one list; everything weaker comes back in a separate one, each with a confidence score, the signals that produced it and a component breakdown you can audit.
{ "ok": true, "meta": { "api": "enrich-recall", "endpoint": "product_match", "mode": "live", "latency_ms": 6096.3, "record_count": 1, "cache_hit": false }, "data": { "identity": { "resolved_title": "Similac Advance Powder Infant Formula", "brand": "Abbott", "category": "food", "gtin": null, "source": "user-input", "resolved": true }, "recalls": [ { "match_confidence": 0.958, "matched_on": [ "brand:exact:1.0", "title_token:0.9", "category:match" ], "components": { "brand": 1, "title": 0.895, "category": 1, "exact_id": false }, "agency": "U.S. FDA (openFDA RES)", "source": "openFDA", "recall_id": "F-0935-2022", "category": "food", "classification": "Class I", "severity_score": 95, "recalling_firm": "Abbott Laboratories", "product": "Abbott Similac Advance Infant Formula with Iron 12.4oz. can, 23.2oz. plastic jar, 20.6oz. plastic jar. 6 units per case. Abbott Similac Pro-Advance 34oz. plastic jar. 6 jars per case. Item numbers: 53359 68084 5595776 6748701 Retail UPCs: 070074533605 070074680859 070074559582 070074122373", "hazard": "Consumer complaints related to Cronobacter sakazakii or Salmonella Newport in infants and evidence of Cronobacter sakazakii in the manufacturing facility where the product had been produced", "remedy": "Recall (Voluntary: Firm initiated)", "status": "Ongoing", "date": "2022-03-16", "url": null } ], "possible": [ { "match_confidence": 0.84, "matched_on": [ "brand:exact:1.0", "title_token:weak:0.75", "category:match" ], "components": { "brand": 1, "title": 0.746, "category": 1, "exact_id": false }, "agency": "U.S. FDA (openFDA RES)", "source": "openFDA", "recall_id": "F-0923-2022", "category": "food", "classification": "Class I", "severity_score": 95, "recalling_firm": "Abbott Laboratories", "product": "Abbott Similac For Spit Up Infant Formula with Iron 22.5oz plastic jar. 6 jars per case. Item numbers: 5372976 Retail UPCs: 070074537306", "hazard": "Consumer complaints related to Cronobacter sakazakii or Salmonella Newport in infants and evidence of Cronobacter sakazakii in the manufacturing facility where the product had been produced", "remedy": "Recall (Voluntary: Firm initiated)", "status": "Ongoing", "date": "2022-03-16", "url": null }, { "match_confidence": 0.84, "matched_on": [ "brand:exact:1.0", "title_token:weak:0.75", "category:match" ], "components": { "brand": 1, "title": 0.746, "category": 1, "exact_id": false }, "agency": "U.S. FDA (openFDA RES)", "source": "openFDA", "recall_id": "F-0941-2022", "category": "food", "classification": "Class I", "severity_score": 95, "recalling_firm": "Abbott Laboratories", "product": "Abbott Similac For Spit Up Infant Formula with Iron 7oz. can and 19.5oz. plastic jar. 6 units per case. Item numbers: 67927 68086 Retail UPCs: 070074679280 070074680873", "hazard": "Consumer complaints related to Cronobacter sakazakii or Salmonella Newport in infants and evidence of Cronobacter sakazakii in the manufacturing facility where the product had been produced", "remedy": "Recall (Voluntary: Firm initiated)", "status": "Ongoing", "date": "2022-03-16", "url": null }, { "match_confidence": 0.84, "matched_on": [ "brand:exact:1.0", "title_token:weak:0.75", "category:match" ], "components": { "brand": 1, "title": 0.746, "category": 1, "exact_id": false }, "agency": "U.S. FDA (openFDA RES)", "source": "openFDA", "recall_id": "F-0945-2022", "category": "food", "classification": "Class I", "severity_score": 95, "recalling_firm": "Abbott Laboratories", "product": "Abbott Similac Organic 7oz. can. 6 cans per case. Abbott Similac Organic with A2 Milk Infant Formula with Iron 20.6oz. plastic jar. 4 jars per case. 23.2oz plastic jar. 6 jars per case. Item numbers: 68013 68092 5082178 Retail UPCs: 070074680156 070074680934 070074508221", "hazard": "Consumer complaints related to Cronobacter sakazakii or Salmonella Newport in infants and evidence of Cronobacter sakazakii in the manufacturing facility where the product had been produced", "remedy": "Recall (Voluntary: Firm initiated)", "status": "Ongoing", "date": "2022-03-16", "url": null } ], "counts": { "recalls": 1, "possible": 25, "candidates_scored": 50 }, "sources_searched": [ "U.S. FDA (openFDA RES)", "EU Safety Gate (RAPEX)" ], "coverage_gaps": [ "CPSC (.gov, not yet accessible)", "NHTSA (served by the `vehicle` engine)", "Health Canada / UK OPSS / OECD (per-portal, not yet integrated)" ], "no_match_note": "Absence of a recall match does NOT mean the product is safe. Coverage = U.S. FDA (openFDA food/drug/device) + EU Safety Gate (RAPEX); it does NOT include CPSC, NHTSA, Health Canada or other national portals. This is safety INFORMATION, not a compliance or legal decision — verify with the official agency before acting." } }
How the Product Safety & Recall Match API works
Product Safety & Recall Match is a normal ReefAPI surface — the same four rules that hold for every other engine on the key.
No OAuth app, no request signing, no per-site account. One key covers all 184 engines.
Every route is a POST with a JSON body. Parameters are validated against the published schema before anything is charged.
Credits, not seats. Failed and blocked calls are never charged, and cache hits cost nothing.
One envelope everywhere. meta carries latency_ms, record_count and the endpoint that answered.
Audit a catalogue before it goes live
The failure this prevents is listing a recalled product. The failure it must not cause is pulling a clean product because a title happened to look similar, which is why identity comes before the verdict.
{"items": [{"gtin": "…"}, {"title": "…", "brand": "…", "category": "…"}]}Twenty-five at a time. Pass a category — it moves matches between the high-confidence and low-confidence buckets, measured below.
{"title": "…", "brand": "…", "category": "…"}Re-run anything that landed in the low-confidence bucket on its own and read the component breakdown before a human decides.
{"brand": "<a supplier you keep hitting>", "date_from": "2024-01-01"}Turns a per-product check into a supplier decision: every recall that firm has filed, newest first, with hazard class and severity.
A catalogue scan where every flag carries the arithmetic that produced it — so a human reviews the twenty ambiguous rows instead of the two thousand clean ones, and nobody delists a product because a word matched.
curl -X POST https://api.reefapi.com/enrich-recall/v1/product_match \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"gtin":"0860007279478"}'{
"ok": true,
"data": { … },
"meta": {
"api": "enrich-recall",
"endpoint": "product_match",
"mode": "live",
"latency_ms": …,
"record_count": …
},
"error": null
}recall vs enrich-recall: same records, different job
These two are the pair people mix up. recall is a search engine over the FDA registries and you do the filtering. enrich-recall starts from a product you are holding, resolves what it is, then scores every candidate recall against it. Field names differ between the two even when the underlying record is identical, which is the usual reason an integration breaks when someone swaps one for the other.
| recall | enrich-recall | |
|---|---|---|
| What you pass | keyword, firm name or a recall_number | gtin, or title plus brand, or a listing_url |
| Recall id field | recall_number | recall_id, same value: H-0854-2026 in both |
| Date field | report_date as 20260610 (YYYYMMDD) | date as 2026-06-03 (ISO) |
| Product text | product_description | product |
| Reason text | reason_for_recall | hazard, plus a separate remedy field |
| Scoring | none, you decide what is relevant | match_confidence 0-1 with components{brand,title,category,exact_id} and matched_on[] |
| Result shape | one recalls[] list | recalls[] for confident hits, possible[] for near misses |
| Coverage statement | a meta.sources string | coverage_gaps[] and no_match_note on every response |
Measured confidence-to-list mapping: brand:exact:1.0 with title_token:1.0 scored 1.00 and went to recalls[]; brand:exact:1.0 with title_token:weak:0.73 scored 0.84 and went to possible[]; brand exact with a 0.265 title scored 0.706 and also went to possible[]. The weakest candidate returned at all scored 0.678.
What is matched, how confident, and what is deliberately not covered
Measured against the live gateway on real recalled products, on generic descriptions and on an invented brand. This engine's honesty about its own limits is the thing worth buying.
High-confidence matches, where the brand matched independently, come back in one list; everything weaker comes back in a separate one. A generic title with a real food brand scored eighty candidates, put twenty-five in the weak bucket and ZERO in the strong one. The record count on the response counts only the strong bucket, so a naive integration reads the conservative number by default.
A match carries a confidence figure, the signals that produced it, and a component breakdown scoring brand, title, category and exact-identifier separately. The hit we measured scored 0.958 on an exact brand and a strong title token; the runner-up on the same product scored 0.84 on a weak title token and was filed in the low-confidence bucket rather than reported as a hit.
The identical product title that found a Class I recall with its brand attached scored ZERO candidates with the brand removed — not a weaker match, not a longer possible list, nothing. That is the design working: the brand is what stops a title token from matching an unrelated firm's recall. An invented brand likewise returned nothing at all rather than a nearest neighbour.
The same product, same brand, run with and without a category: with it, the recall landed in the high-confidence list at 0.958. Without it, the strong list came back empty and the same firm's recalls appeared in the weak bucket at 0.69. If you have a category, send it — otherwise you will read a false negative on a true positive.
Four of five real branded products we tested returned zero recalls. Every response carries the engine's own note saying that absence of a match does not mean the product is safe, names the two sources it did search — the US FDA's recall database and the EU Safety Gate — and lists the portals it did NOT: the US consumer product and vehicle agencies, Health Canada, the UK regulator. Publishing the gaps is what makes the hits usable.
One brand returned nine recalls newest-first against a reported total in the thousands, each with the agency, the hazard class, a numeric severity score, the hazard and remedy wording, the current status and the report date. That is a due-diligence answer about a supplier rather than a yes-or-no about a listing.
Pass a barcode and the identity block reports the title and brand it resolved to and where that resolution came from. So when a match looks wrong you can see whether the recall matching was wrong or the barcode lookup was — two different bugs with two different fixes.
Every response carries the same standing note: this is safety information, not a compliance or legal decision, and the official agency is the place to verify before acting. We publish that here rather than only in the payload, because a page that oversold this endpoint would be selling something we do not have.
What people build with Product Safety & Recall Match
The jobs this data is most often used for.
endpoints
credits per call
Marketplaces call product_match to flag or delist products that match an active safety recall.
Retail compliance tools use brand_check to monitor whether a supplier's brand has recalls.
Catalog pipelines use batch to screen an entire product feed against recall data.
What Product Safety & Recall Match data costs
The cheapest call here is 2 credits, so $15/mo (Pro) buys 5,000 of them — $1.50 per 1,000 credits. Credits roll over and never expire, and failed or blocked calls are not charged.
Full pricing →- 1,000 free credits on signup, no card
- One key, all 184 APIs, one credit pool
- Failed and blocked calls are never charged
- Credits roll over and never expire
Call it in two lines
Sign up, get 1,000 credits and one key that works on every engine. Then this is the whole protocol.
curl -X POST https://api.reefapi.com/enrich-recall/v1/product_match \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"gtin":"0860007279478"}'import requests
r = requests.post(
"https://api.reefapi.com/enrich-recall/v1/product_match",
headers={"x-api-key": REEF_KEY},
json={
"gtin": "0860007279478"
},
)
print(r.json()["data"])Have a question? We got answers.
The questions people actually ask before wiring up Product Safety & Recall Match.
Get a free key →Which one do I call, recall or enrich-recall?▾
If you have a keyword, a firm name or a recall number, call recall. If you have a product, meaning a barcode off a package or a title and brand from a listing, call enrich-recall, because it resolves the identity first and then scores every candidate instead of handing you a keyword result set. A measured GTIN 0860007279478 resolved to Sichuan Chili Crisp by Fly By Jing before any recall lookup ran.
The brand matched exactly, so why is the hit in possible[] and not recalls[]?▾
Brand alone is not enough. A GTIN lookup for Fly By Jing chili crisp found recall H-0854-2026 with components brand 1.0 and category 1.0 but title 0.265, scored it 0.706 and filed it under possible[] — correctly, because that recall was for the same brand's sesame noodles. Passing title Creamy Sesame Noodles with the same brand scored the identical recall 1.0 and moved it into recalls[]. Even brand exact plus a weak title token at 0.73 only reached 0.84 and stayed in possible[].
Why does brand_check for Peloton return zero recalls?▾
Because Peloton's recalls are CPSC recalls, and this engine covers the FDA registries plus EU Safety Gate. The measured call returned an empty recalls[], total 0, and a coverage_gaps[] list naming CPSC, NHTSA and per-country portals such as Health Canada and UK OPSS. An empty result means nothing was found in the sources searched, which is exactly what sources_searched[] and no_match_note exist to tell you.
meta.record_count says 0 but I got matches. Which count is real?▾
Use the counts object in the body. On measured product_match calls meta.record_count and meta.bytes were both 0 while data.counts read {recalls: 0, possible: 1, candidates_scored: 1}, and on another call {recalls: 2, possible: 25, candidates_scored: 80}. counts.candidates_scored also tells you how many recalls were actually compared, which is the number to watch when a result looks thin.
What does identity.source tell me?▾
How the product was resolved before matching started. A GTIN call returned source 'barcode (openfoodfacts, upcitemdb)' with resolved true and a filled-in title, brand and category. A title plus brand call returned source 'user-input', also resolved true, but nothing there was independently verified — the engine trusts what you sent. That matters because brand is the false-match guard, so a wrong brand string quietly costs you every real hit.
Does an empty recalls[] mean the product is safe?▾
No, and the response says so. no_match_note ships on every call and states that absence of a match is not a safety verdict, that coverage is FDA plus EU Safety Gate only, and that this is information rather than a compliance decision. Read coverage_gaps[] next to it before you show anyone a green check mark.
How does max_results interact with the two lists?▾
It caps each list separately, not the total. The same Coca-Cola query returned possible with 25 entries at the default and 50 at max_results 50, while candidates_scored stayed at 80 both times. Raising it does not widen the search, it only stops truncating what had already been scored.
What happens to a bad item inside batch?▾
Nothing sinks the batch. Up to 25 products go in and each result comes back with its own ok flag, an index and an echo of the input you sent, alongside the same identity, recalls, possible and counts payload a single product_match returns. The roll-up adds flagged_count, the number of items with at least one entry in recalls[], which is the field a pre-listing audit should alert on.
What is the Product Safety & Recall Match API?▾
Product Safety & Recall Match API is a ReefAPI endpoint group for product safety & recall match It returns live JSON through POST requests under /enrich-recall/v1.
Is the Product Safety & Recall Match API free to try?▾
Yes. ReefAPI starts with 1,000 free credits, no card required. Product Safety & Recall Match calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Product Safety & Recall Match login or account?▾
No login to Product Safety & Recall Match is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Product Safety & Recall Match data?▾
The page example is captured from a live product_match call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Product Safety & Recall Match API use?▾
Product Safety & Recall Match actions currently cost 2-3 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Product Safety & Recall Match from an AI assistant or MCP client?▾
Yes. Connect ReefAPI once through MCP and your assistant can call enrich-recall actions with the same key, credit pool and JSON envelope used by normal REST requests.
35 E-commerce & Marketplaces APIs on the same key
One key, one credit pool, one response envelope. If you are pulling Product Safety & Recall Match, you are one call away from the rest of the category — no second contract, no second integration.
Need something this API does not do?
Name the endpoint, the field, or a source we do not carry yet. We ship new APIs every week and you would be first to get the key. Real people read every message and reply the same day.
Try it on your own data before you pay anything
The call above is the real endpoint, not a recording. A free key gives you 1,000 credits, the other 183 APIs, and the same envelope everywhere.
Endpoints, parameters and credit costs on this page are read from the live catalog and cannot drift from what the API accepts. Field notes were captured on 2026-08-28.