Looking for the overview — what this API returns, what it costs, and a call you can run without a key? See the Product Safety & Recall Match API page →
E-commerce & Marketplaces

Product Safety & Recall Match API

The Product Safety & Recall Match API matches a product to safety recalls as clean JSON.

3 actionsLive JSON1,000 free credits$0.67–$1.50 / 1,000 creditsMCP-ready
Get a free keyOpen in playground

🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.

The primary product_match endpoint resolves a product's identity (title, brand, category, GTIN) and returns matching recalls with a confidence score and what they matched on, and you can run a brand_check and batch many products. It is built for e-commerce, marketplace safety and compliance workflows that need to flag recalled products without maintaining recall feeds. One ReefAPI key, one shared credit pool, the standard envelope.

Reference

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.

recallenrich-recall
What you passkeyword, firm name or a recall_numbergtin, or title plus brand, or a listing_url
Recall id fieldrecall_numberrecall_id, same value: H-0854-2026 in both
Date fieldreport_date as 20260610 (YYYYMMDD)date as 2026-06-03 (ISO)
Product textproduct_descriptionproduct
Reason textreason_for_recallhazard, plus a separate remedy field
Scoringnone, you decide what is relevantmatch_confidence 0-1 with components{brand,title,category,exact_id} and matched_on[]
Result shapeone recalls[] listrecalls[] for confident hits, possible[] for near misses
Coverage statementa meta.sources stringcoverage_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.

Live example

Real request and response JSON

Captured from the indexed primary action, product_match, on .

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/enrich-recall/v1/product_match",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "gtin": "0860007279478"
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "enrich-recall",
    "endpoint": "product_match",
    "mode": "live",
    "latency_ms": 9641,
    "record_count": 0,
    "bytes": 0,
    "cache_hit": false,
    "subcalls": [
      {
        "api": "barcode",
        "action": "lookup",
        "status": "ok",
        "latency_ms": 2442.6
      },
      {
        "api": "recall",
        "action": "search",
        "status": "ok",
        "latency_ms": 1151.2
      },
      {
        "api": "recall",
        "action": "by_company",
        "status": "ok",
        "latency_ms": 1157.4
      }
    ],
    "candidates_scored": 1
  },
  "data": {
    "identity": {
      "resolved_title": "Sichuan Chili Crisp",
      "brand": "Fly By Jing",
      "category": "food",
      "gtin": "[redacted-phone]",
      "source": "barcode (openfoodfacts, upcitemdb)",
      "resolved": true
    },
    "recalls": [],
    "possible": [
      {
        "match_confidence": 0.706,
        "matched_on": [
          "brand:exact:1.0",
          "category:match"
        ],
        "components": {
          "brand": 1,
          "title": 0.265,
          "category": 1,
          "exact_id": false
        },
        "agency": "U.S. FDA (openFDA RES)",
        "source": "openFDA",
        "recall_id": "H-[redacted-phone]",
        "category": "food",
        "classification": "Class II",
        "severity_score": 75,
        "recalling_firm": "Fly By Jing Inc.",
        "product": "Fly By Jing (branded) Creamy Sesame Noodles; 3.8 oz packets. Sold in single and 4 packs. Single UPC: [redacted-phone] pack UPC: [redacted-phone]",
        "hazard": "Possible cross-contamination resulting in undeclared allergen ingredient (Peanut)",
        "remedy": "Recall (Voluntary: Firm initiated)",
        "status": "Ongoing",
        "date": "[redacted-phone]",
        "url": null
      }
    ],
    "counts": {
      "recalls": 0,
      "possible": 1,
      "candidates_scored": 1
    },
    "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."
  }
}
Actions

What the Product Safety & Recall Match API does

ActionDescriptionConcrete use caseKey params
product_matchResolve a product (GTIN ‖ title+brand ‖ listing_url) and match it against FDA + EU recalls with transparent confidence. recalls[] = high-confidence (brand independently matched); possible[] = lower-confidence candidates.Pricing teams call product_match to resolve a product (GTIN ‖ title+brand ‖ listing_url) and match it against FDA + EU recalls wi….gtin, title, brand, category, listing_url, ...
brand_checkAll recalls for a brand / recalling-firm (normalized passthrough of the recall engine's by_company), newest first — the seller-watch / due-diligence use case.Marketplace operators call brand_check to get all recalls for a brand / recalling-firm (normalized passthrough of the recall engine's by_co….brand, category, date_from, date_to, limit
batchScan up to 25 catalog products in one call (seller pre-listing audit). Each item is product_match'd; per-item ok/error + a roll-up summary.Catalog enrichment teams call batch to scan up to 25 catalog products in one call (seller pre-listing audit).items, max_results
Code samples

Call product_match from your stack

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"}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.enrich-recall.product_match with {"gtin":"0860007279478"}.
Use cases

Who uses this API and why

  • 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.
FAQ

Questions developers ask before integrating

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.

docs / enrich-recall

Product Safety & Recall Match

Product Safety & Recall Match

base /enrich-recall/v13 endpoints
post/enrich-recall/v1/product_match3 credits

Resolve a product (GTIN ‖ title+brand ‖ listing_url) and match it against FDA + EU recalls with transparent confidence. recalls[] = high-confidence (brand independently matched); possible[] = lower-confidence candidates.

ParameterAllowed / rangeDescription
gtinoptionalProduct barcode (GTIN-8/12/13/14, UPC, EAN or ISBN). The engine resolves it to {title, brand, category} via the barcode engine, then matches recalls. Provide this OR (title + brand) OR listing_url.
titleoptionalProduct title/name. Use with `brand` when you have no barcode. Ignored if `gtin` resolves a product.
brandoptionalProduct brand / manufacturer. STRONGLY recommended with `title` — brand is the independent false-match guard (a title match with a different brand is never asserted).
categoryoptionalOptional product category hint (food / drug / device / toy / apparel …) — improves category-fit scoring. Auto-derived from the barcode when available.
listing_urloptionalA marketplace product URL (amazon/ebay/…). MINIMUM support: the host is recorded and you must still pass title (+brand) for matching. Full URL→product resolution is the enrich-url engine's job.
max_results = 25optional1–50Max recalls[] + max possible[] returned (each list capped at this).
Try in playground →
post/enrich-recall/v1/brand_check2 credits

All recalls for a brand / recalling-firm (normalized passthrough of the recall engine's by_company), newest first — the seller-watch / due-diligence use case.

ParameterAllowed / rangeDescription
brandrequiredBrand / recalling-firm name to list recalls for.
categoryoptionalall · food · drug · deviceFDA recall category scope (passed through to the recall engine). Default all.
date_fromoptionalFilter recalls with report date from this YYYY-MM-DD.
date_tooptionalFilter recalls with report date to this YYYY-MM-DD.
limit = 25optional1–100Max recalls to return for the brand.
Try in playground →
post/enrich-recall/v1/batch2 credits

Scan up to 25 catalog products in one call (seller pre-listing audit). Each item is product_match'd; per-item ok/error + a roll-up summary.

ParameterAllowed / rangeDescription
itemsrequiredUp to 25 products (seller-catalog scan). Each item = a {gtin} OR {title, brand[, category]} object (or a bare barcode string). Per-item ok/error; one bad item never sinks the batch.
max_results = 25optional1–50Max recalls[] + max possible[] returned (each list capped at this).
Try in playground →