API documentation
REST/JSON. No account or API key: pay per check with x402. Machine-readable: OpenAPI 3.1, llms.txt, coverage, pricing.
What is checked
The request is routed to the responsible US authority: consumer products → CPSC; vehicles → NHTSA; food, drugs and medical devices → FDA (openFDA enforcement reports). Without a category, a VIN or model year routes to NHTSA, an NDC to FDA drugs, a UDI to FDA devices, and product words decide otherwise; when unclear, all non-vehicle authorities are queried. Not covered (422, never charged): Meat, poultry and processed egg products (USDA FSIS); Vehicle equipment, tires and child car seats (NHTSA equipment campaigns); Cosmetics; Recreational boats (USCG).
1. Ask (free)
POST https://productrecallcheck.online/api/v1/check
Content-Type: application/json
{
"brand": "Example Brand", // brand / manufacturer / (vehicles) make
"product_name": "Example Blender",
"model": "XB-200", // as printed; separators normalised, digits never
"upc": "012345678905", // UPC/EAN/GTIN, check digit validated, leading zeros kept
"category": "consumer_product", // optional: consumer_product | vehicle | food | drug | medical_device | auto
"jurisdiction": "US" // optional, default US
}
// Other shapes: {"vin":"1HGCV1F30JA012345"} · {"make":"Honda","model":"Accord","model_year":2018}
// {"ndc":"0409-4888-20","lot":"…"} · {"brand":"Jif","product_name":"peanut butter","upc":"…","lot":"…"}
// More identifiers: sku, serial, lot/batch, date_code, manufacture_date, expiration_date/best_by, udiIdentifiers are validated and normalised locally (UPC check digit, VIN check digit, NDC shape, dates). Invalid values return 400; unsupported categories or jurisdictions return 422. A valid request returns 402 with the x402 requirement (header PAYMENT-REQUIRED), the price (0.018 USDC) and the official sources that will be queried. Nothing is stored and no source is contacted.
2. Pay and check
Resend the identical body with PAYMENT-SIGNATURE (base64 x402 v2 payload, exact scheme, USDC on Base mainnet) and an Idempotency-Key (16–128 characters of A–Z a–z 0–9 _ -). The authorization is verified, the sources are queried live, and the payment is settled only for a determinate result. The settlement is returned in PAYMENT-RESPONSE.
{
"check_id": "prc_…",
"result": "RECALL_FOUND",
"confidence": "HIGH",
"summary": "Official recall found for …",
"no_match_meaning": "NO_MATCH_FOUND means only that no matching recall was found in the checked official sources under the supplied identifiers. It does not mean the product is safe, free of defects, or will never be recalled.",
"product": { "brand": "…", "model": "…", "matched_identifiers": { "brand_match": true, "model_match": true, "upc_match": null, … } },
"recalls": [{
"match_state": "RECALL_FOUND", "confidence": "HIGH",
"match_explanation": { "brand_match": true, "model_match": true, "upc_match": null,
"lot_match": null, "serial_range_match": null, "date_range_match": null, … },
"restrictions": [{ "kind": "date_code", "evaluation": "matched", "parse_confidence": "high",
"text_original": "date codes of 2510 (Oct-2025), 2511 …", "detail": "…" }],
"recall": { "recall_id": "25214", "authority": "CPSC", "title": "…", "published_at": "2025-04-03",
"hazard_original": "…verbatim…", "hazard_normalized": ["INJURY"],
"consumer_action_original": "…verbatim…", "affected_products": [ … ],
"original_description": "…verbatim…", … },
"source": { "url": "https://www.cpsc.gov/Recalls/…", "api_url": "https://www.saferproducts.gov/…",
"checked_at": "…", "source_type": "OFFICIAL_RECALL_SOURCE" }
}],
"excluded_candidates": [ { "recall_id": "…", "reasons": ["The recall lists specific models and … is not one of them."] } ],
"routing": { "categories": ["consumer_product"], "routing_basis": "explicit", "sources_checked": ["us-cpsc-recalls"], … },
"verification": { "checked_at": "…", "cache": { "cache_used": false, "cache_status": "LIVE", "source_version": { … } },
"sources": [{ "request_urls": ["https://www.saferproducts.gov/…"], "outcome": "ok", … }] },
"next_steps": [ … ], "unknowns": [ … ], "limitations": [ … ],
"language": { "source_languages": ["en"], "output_language": "en", "original_values_preserved": true },
"payment": { "charged": true, "status": "settled", "amount": "0.018", "transaction": "0x…" }
}- Retrying with the same key returns the stored result (
cache_statusIDEMPOTENT_REPLAY, originalchecked_at) and never charges again. - The same key with a materially different product →
409 idempotency_conflict. A payment authorization can be used for one check only →409 payment_replay. - If the result was not charged (SOURCE_UNAVAILABLE, UNABLE_TO_VERIFY) you may retry later with the same key; the unsettled authorization is released.
3. Retrieve later (free)
GET https://productrecallcheck.online/api/v1/checks/{check_id}
Authorization: Bearer <Idempotency-Key>Returns the stored evidence package with cache_status: STORED and its original checked_at — it is not re-checked. Evidence is kept 90 days.
Results
RECALL_FOUND | An exact published identifier (UPC/GTIN, NDC, model, SKU, lot or serial within a listed range) ties the product to an official recall, and no stated restriction is left unverified. Charged. |
POSSIBLE_MATCH | A recall may apply, but a restriction is unverified (e.g. only some serial numbers, lot codes not supplied, vehicle VIN population not public) or the identity match is at model-family / brand+name level. Charged. |
AMBIGUOUS | Several different recalls (or only weak name similarities) relate to the product; the identifiers cannot tell which applies. Candidates are returned. Charged. |
NO_MATCH_FOUND | The relevant official source(s) answered and nothing matched sufficiently specific identifiers. NO_MATCH_FOUND means only that no matching recall was found in the checked official sources under the supplied identifiers. It does not mean the product is safe, free of defects, or will never be recalled. Charged. |
SOURCE_UNAVAILABLE | An official source could not be queried. Never reported as "no recall". Not charged; retry with the same Idempotency-Key. |
UNABLE_TO_VERIFY | The sources answered but the evidence cannot support a determination (identifiers too weak for a no-match, vehicle model not recognised, too many candidates, or a relevant authority that is not covered, e.g. USDA FSIS for meat). Not charged. |
How matching works
- Evidence strength, strongest first: exact UPC/GTIN, NDC, lot or serial inside a listed range → exact model or SKU → model family (listed model differs only by a letter suffix) or brand + full product name → name-only similarity. Name-only similarity is never reported as a match (
other_candidates). - Model numbers compare with separators removed but digits intact:
XB-200=XB200, whileXR-10≠XR-100. When a recall lists models and yours is not among them, the recall is returned inexcluded_candidateswith the reason. - Restrictions (lot/batch codes, serial ranges, date codes, manufacture dates, best-by dates) are read from the official text. A product is excluded only when a list or range was read with high confidence and your value lies outside it; an unclear list leaves the restriction
unverifiedand the result POSSIBLE_MATCH. - Vehicles: NHTSA maps campaigns to make/model/year but does not publish affected VINs, so every campaign is POSSIBLE_MATCH with a VIN confirmation step. A VIN is decoded with NHTSA vPIC and masked in all output.
- Confidence is evidence-based: HIGH = exact published identifier and nothing left open; MEDIUM = exact model / family / brand + name or an open restriction; LOW = weak.
- Hazards:
hazard_originalis verbatim;hazard_normalizedis metadata (FIRE, BURN, CHOKING, ELECTRIC_SHOCK, CRASH, INJURY, CONTAMINATION, ALLERGEN, OTHER, UNKNOWN). - Freshness: every check queries the sources live (
cache_used: false).source_versioncarries the dataset date a source reports (openFDA).
Errors
Every error is {"error":{"code","message","details?"}}. Codes: invalid_request, invalid_json, missing_identifier, insufficient_identifiers, invalid_gtin, invalid_vin, invalid_ndc, invalid_date, invalid_model_year, conflicting_identifiers, unknown_category, category_not_covered, jurisdiction_not_supported, payment_required, invalid_payment, payment_rejected, payment_expired, payment_wrong_*, idempotency_key_required, idempotency_conflict, payment_replay, in_progress, settlement_failed, rate_limited, not_configured, payment_provider_unavailable, service_unavailable.