FDA Recall Monitor API — Drug, Device & Food Recalls (JSON) avatar

FDA Recall Monitor API — Drug, Device & Food Recalls (JSON)

Pricing

from $4.00 / 1,000 recall records

Go to Apify Store
FDA Recall Monitor API — Drug, Device & Food Recalls (JSON)

FDA Recall Monitor API — Drug, Device & Food Recalls (JSON)

Monitor FDA drug, device & food recalls for internal QA/compliance workflows via the official openFDA API. Not for public recall alerts — see openFDA terms.

Pricing

from $4.00 / 1,000 recall records

Rating

0.0

(0)

Developer

Moose & Raven

Moose & Raven

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

0

Monthly active users

an hour ago

Last modified

Categories

Share

An internal monitoring, QA, and compliance tool for tracking FDA drug, device, and food recalls against the official openFDA API — no scraping, no account required. Filter by company, severity classification, keyword, date range, and status; run once or on a schedule with incremental monitor mode.

This is not a public-facing recall alert service. openFDA's terms of use prohibit using its data to issue public recall alerts to consumers; this actor is built and intended for internal use — feeding a company's own QA, compliance, or supply-chain monitoring workflows — not for redistribution as a consumer notification product. See openFDA's terms for the authoritative restriction.

1. What it does

Queries three separate openFDA/FDA data sources — drug enforcement, food enforcement, and the CDRH device recall database — and normalizes them into one consistent JSON schema. You choose which categories to check and how to filter (company, severity, keyword, date range, status); the actor fetches matching recalls, flags anything it couldn't verify (a failed request, a rate-limit hit, a capped/truncated pull), and — in monitor mode — remembers what it has already reported so scheduled runs only surface genuinely new recalls.

2. How it works

  1. Choose which categories to check (drug, device, food — default all three).
  2. Optionally filter by companies (recalling-firm name), classification (Class I/II/III severity — drug/food only), keywords (matches product_description or reason_for_recall), dateFrom/dateTo (recall-initiation date range), and/or status (the recall's current status — field name and value vocabulary differ by category, see Output fields below).
  3. Every filter is applied server-side, against openFDA's own search API — this actor never fetches an entire category and filters it locally, which would over-fetch and slow down a narrow query.
  4. Optional incremental monitor mode: remembers which recalls it has already delivered per category (by the source's own stable ID, in a persistent named key-value store) and only reports genuinely NEW recalls on later runs — schedule it daily/weekly and get exactly the delta.
  5. A confirmed zero-match result is treated as a real, good outcome, not an error. openFDA returns a distinguishable 404 with a specific NOT_FOUND error code for a well-formed query with no matches (e.g. "this company has a clean recall record") — separate from a genuinely broken request (500). This actor tells these apart explicitly.
  6. A failed fetch is never silently treated as "nothing new." Each category's failure pushes an explicit fetch-failed (or rate-limited) status record — one category's problem doesn't hide results from the others.
  7. maxRecordsPerCategory (default 500) caps each category independently; an optional maxRecords caps the total across every category combined. If a category's real total exceeds what was fetched, an explicit category-truncated record surfaces the true count — a capped pull is never silently reported as a complete one.

3. Input

FieldTypeNotes
categoriesarraydrug, device, food — default all three
companiesarrayRecalling-firm name(s) — tokenized phrase match, not exact-string
classificationarrayClass I / Class II / Class III — drug/food only, no effect on device
keywordsarrayServer-side text search against product_description/reason_for_recall
dateFrom / dateTostringYYYYMMDD — filters by recall-initiation date
statusarrayCurrent recall status — field name AND value vocabulary differ by category
monitorModebooleanPersistent incremental "new since last run" tracking
maxRecordsPerCategoryintegerPer-category cap, default 500
maxRecordsintegerOptional aggregate cap across ALL categories combined, in addition to the per-category cap
apiKeystring (secret)Optional free openFDA key — raises the daily rate limit, not required

See the Input tab for the full form with inline descriptions.

4. Output fields

Each dataset item has a recordType of "recall", "category-truncated", "category-skipped", "fetch-failed", or "rate-limited". A "recall" record:

FieldTypeNotes
categorystringdrug, device, or food
recordIdstring | nullrecall_number (drug/food) or cfres_id (device)
recallingFirm, productDescription, reasonForRecallstring | null
classificationstring | nullClass I/II/III — always null for device (no such field exists)
statusstring | nulldrug/food: status field; device: recall_status field — different field names for the same concept, mapped automatically
city, state, country, address1, address2, postalCodestring | nullFirm location — country not present in device records
distributionPatternstring | null
voluntaryMandatedstring | nulldrug/food only — always null for device (no comparable field)
initiationDate, reportDate, terminationDatestring | nullCross-category field-name mapping applied automatically (e.g. device's event_date_terminated vs. drug/food's termination_date)
classificationDatestring | nulldrug/food only (center_classification_date) — null for device
productQuantitystring | null
codeInfostring | nullFree text, exactly as FDA publishes it (e.g. "Lot #: 072915, Exp 10/29/2015") — not a structured field, do not parse assuming a fixed format
ndcCodes, packageNdcarray of string | nullNational Drug Code(s), from openFDA's nested openfda enrichment
brandName, genericName, substanceName, manufacturerNamearray of string | nullFrom openfda
applicationNumber, route, rxcui, unii, openfdaProductTypearray of string | nullFrom openfda
fetchedAtstring (ISO datetime)When this actor fetched the record
sourceApistringWhich openFDA endpoint this record came from

Important, honest limitation on the openfda-derived fields: openFDA's nested openfda enrichment object is not always populated — confirmed live, only ~18% of drug enforcement records (3,206 of 17,793 at last check) have a non-empty openfda object; the rest return an empty {}. When empty, every openfda-derived field above is set to an explicit null — never a guess, never an empty array standing in for "unknown." When populated, every subfield is a real array in the source data (even single-value fields), mapped as arrays here to avoid silently dropping a recall with multiple NDC codes.

Status records (category-truncated, category-skipped, fetch-failed, rate-limited) each carry category, message, and detectedAt — read the message, it explains exactly what happened and what it means for that category's completeness this run.

Real sample output

Live-fetched from api.fda.gov/food/enforcement.json (input: {"categories":["food"],"keywords":["listeria"],"status":["Terminated"],"dateFrom":"20200101","dateTo":"20241231"}) — not a fabricated example:

{
"recordType": "recall",
"category": "food",
"recordId": "F-0757-2022",
"recallingFirm": "Dole Fresh Vegetables Inc",
"productDescription": "Marketside 12oz Classic Salad UPC:6-81131-32894-4 SKU: 3107",
"reasonForRecall": "Harvest equipment used in harvesting raw iceberg lettuce was tested by Dole and found to contain Listeria monocytogenes.",
"classification": "Class I",
"status": "Terminated",
"city": "Monterey",
"state": "CA",
"country": "United States",
"address1": "2959 Salinas Hwy",
"address2": "",
"postalCode": "93940-6400",
"distributionPattern": "Canada: AB, BC, NB, ON, QC, Sk; United States: AL, AZ, CA, CO, CT, FL, GA, HI, IA, ID, IL, IN, KS, KY, LA, MA, MD, MI, MN, MO, MS, NC, ND, NE, NV, NY, OH, OK, OR, PA, SC, TN, TX, UT, VA, WA, WI",
"voluntaryMandated": "Voluntary: Firm initiated",
"initiationDate": "20220106",
"reportDate": "20220302",
"terminationDate": "20220322",
"classificationDate": "20220222",
"productQuantity": "",
"codeInfo": "Product codes beginning with B - Best by Dates of December 23, 2021 through January 8, 2022.",
"ndcCodes": null,
"packageNdc": null,
"brandName": null,
"genericName": null,
"substanceName": null,
"applicationNumber": null,
"manufacturerName": null,
"route": null,
"rxcui": null,
"unii": null,
"openfdaProductType": null,
"fetchedAt": "2026-07-17T22:22:57.008Z",
"sourceApi": "api.fda.gov/food/enforcement"
}

Note the openfda-derived fields are all null here — food enforcement records don't carry the NDC/brand/generic drug-identity enrichment (that's a drug-specific dataset feature); this is the honest, expected shape for a food record, not a missing-data bug.

5. Use cases

  • Pharma compliance — track recalls affecting your own NDC codes, or a competitor's/ supplier's product line, by company or keyword.
  • Hospital/health-system procurement — monitor device and drug recalls for everything on formulary or in the supply chain, bundled in one feed instead of three separate lookups.
  • Insurance & underwriting — screen a manufacturer's or distributor's recall history (severity mix, frequency, resolution time via initiationDateterminationDate) as part of risk assessment.
  • Legal / M&A due diligence — pull a target company's full FDA recall history by firm name, with real classification and status, before closing.
  • Healthcare software / EHR & supply-chain platforms — feed a normalized, deduplicated recall stream into an existing alerting or dashboard system via monitor mode.

6. Setting up recall alerts (Apify Schedules + your own notification channel)

This actor produces structured data; wiring that into an email/Slack/Teams alert is done with Apify's own platform features, not actor-specific code:

  1. Create an Apify Schedule pointing at this actor, with monitorMode: true and your filters set (e.g. companies or keywords for what you care about). Daily or weekly is typical for compliance monitoring.
  2. Attach a Webhook to the schedule/actor for the ACTOR.RUN.SUCCEEDED event. Apify's webhook payload includes the run's dataset ID, so your webhook target can fetch the new items.
  3. Route the webhook to Slack/Teams via their native "incoming webhook" URL, or to email via a lightweight relay (e.g. a Zapier/Make webhook-to-email step, or your own small function) — since monitorMode only delivers genuinely new recalls, every triggered notification represents real new information, not a repeat.
  4. Because the dataset always distinguishes recall records from category-skipped/ fetch-failed/rate-limited status records, your alert logic can (and should) treat the latter as "monitoring degraded this run" rather than silently trusting an empty result.

7. Classification guide (real examples from live FDA data)

FDA assigns one of three severity classes to drug and food recalls (device recalls have no classification field — see Limitations):

  • Class I — reasonable probability that use of the product will cause serious adverse health consequences or death. Real example: CareFusion 213, LLC's recall of BD ChloraPrep Clear antiseptic applicators — reason: "Non-Sterility: Due to presence of Aspergillus penicillioides."
  • Class II — use of the product may cause temporary or medically reversible adverse health consequences, with remote probability of serious harm. Real example: Mars Snacking's recall of MorningStar Farms Buffalo Chik'n Nuggets — reason: "Potential contamination with plastic material."
  • Class III — use of the product is not likely to cause any adverse health consequence. Real example: Produce Innovations' recall of a roast beef & cheddar sandwich — reason: "Incorrect Product Label; Roast Beef and Swiss closed face sub sandwich labeled as Turkey and Cheddar closed face sub sandwich."

8. Pricing

Pay per event (current live pricing — confirmed via the Apify API at time of writing):

  • recall-record$0.004 per new recall delivered
  • monitor-run$0.05 per scheduled monitor-mode run, in addition to per-record charges

monitor-run is billed only when nothing was compromised (no fetch failures, no rate-limit hits) — a disclosed truncation/skip still bills normally (it's a known, surfaced cap, not a blind spot), but a run that couldn't reliably confirm what it checked does not.

See the actor's Pricing tab for the authoritative, current numbers.

9. Use from Claude, Cursor, and other AI agents (MCP)

This actor can be called as a tool by any MCP-compatible AI client (Claude Desktop, Cursor, VS Code, etc.) via Apify's hosted MCP server, without any actor-specific integration code:

  1. Point your MCP client at https://mcp.apify.com, authenticated with your Apify API token (as a bearer header or via OAuth — see Apify's MCP docs for client-specific config).
  2. Expose this actor specifically with the tools query parameter: https://mcp.apify.com?tools=mooseandraven/openfda-recall-monitor
  3. Your agent can then call it directly — e.g. "check for any new Class I drug recalls from Pfizer in the last 30 days" maps naturally onto companies, classification, dateFrom. The actor's own input schema (this page's Input tab) is what the MCP tool schema is built from, so any filter documented here is available to the agent.

10. FAQ

How fresh is the data? openFDA's enforcement and device-recall datasets are refreshed weekly (per openFDA's own documentation) — this actor queries live at run time, so results are as current as openFDA's own weekly refresh, not cached or delayed further by this actor.

How far back does the data go? Per openFDA's own documentation: drug enforcement and food enforcement both cover 2004–present; device recalls cover 2002–present. In practice, the earliest genuinely dense, non-anomalous data in each dataset starts a few years into that window — and a small number of records carry visibly corrupted dates (e.g. a handful of device records with a garbled 4-digit year) that are a real, openFDA-side data-quality artifact, not something this actor introduces or can clean up.

Do I need an openFDA API key? No — openFDA works with no key at all (240 requests/minute, 1,000/day per IP). An optional free key (apiKey input) only raises the daily cap to 120,000, useful only for high-volume scheduled monitoring across many filters.

What's the pricing/subscription model? Pay-per-event, not a flat subscription — you're charged per recall record actually delivered plus a per-monitor-run fee, so cost scales with how much genuinely new data the actor finds, not a flat rate regardless of activity. See Pricing above.

Why do I sometimes get a category-skipped or category-truncated record instead of just recalls? This actor deliberately surfaces its own limits instead of hiding them — see Output fields and How it works above. A flat "just the recalls" actor that never emits these is not necessarily more reliable; it may simply not be telling you when a pull was capped or a category couldn't be checked.

11. What it does NOT do / Limitations

  • Not a public consumer alert service — see the ToS note at the top. Build internal workflows on top of this, not a public-facing notification product.
  • device recalls come from a genuinely different openFDA dataset than drug/food. drug and food both use openFDA's "enforcement" endpoints — the same underlying data model. device uses the separate CDRH recall database, with its own field names (confirmed live) and no classification concept. This actor's field mapping is fully consistent with that distinct source, but "device" isn't just "drug/food's data filtered to devices."
  • Device recalls have no severity classification field at all. The classification filter has no effect on the device category, and every device recall record's classification field is null — not "unknown," genuinely nonexistent in FDA's device dataset.
  • status field name AND value vocabulary differ by category — drug/food's confirmed real values are "Ongoing"/"Completed"/"Terminated"; device's confirmed real values are "Open, Classified"/"Completed"/"Terminated" (not "Ongoing"). A status value drawn from one category's vocabulary has no effect on another category's results.
  • companies/classification/status filters use TOKENIZED PHRASE matching, not a literal exact-string match and not fuzzy matching — a misspelled or very differently-phrased name will not match.
  • The openfda enrichment fields (NDC codes, brand/generic/substance names, etc.) are genuinely absent on most records, not an integration gap — see Output fields above.
  • codeInfo (lot/expiry info) is free text, not a structured field — do not assume a fixed format when parsing it downstream.
  • No fuzzy/phonetic company-name matching, no cross-category correlation (e.g. flagging that one firm has recalls in multiple categories) — each category is filtered and reported independently.

Local development

npm install
npm test # recorded-fixture unit tests, no network needed
npx apify-cli run # local end-to-end run — works immediately, no key/account needed

Support

Built and maintained by Moose & Raven. Questions or issues: support@mooseandraven.com.