openFDA Enforcement Recalls Scraper avatar

openFDA Enforcement Recalls Scraper

Pricing

$2.50 / 1,000 recall record returneds

Go to Apify Store
openFDA Enforcement Recalls Scraper

openFDA Enforcement Recalls Scraper

Drug, device and food recall and enforcement reports from the official openFDA API: classification, firm, reason, dates, distribution.

Pricing

$2.50 / 1,000 recall record returneds

Rating

0.0

(0)

Developer

Jason

Jason

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

17 days ago

Last modified

Share

Pulls drug, medical device and food recall records from the FDA's own openFDA enforcement-report API (api.fda.gov), the public feed behind the FDA's weekly Enforcement Report. No API key, no account, no browser automation. You get one flat JSON record per recall with its classification (Class I/II/III severity), status, recalling firm and address, product description, reason for recall, distribution pattern, quantity, and the full timeline (initiated, classified, reported, terminated) — filtered by product type, a report-date window, keywords matched against the product description and reason for recall, US state, or FDA's own recall classification. This is a compliance and recall-monitoring feed: legal, insurance, retail and supply-chain teams buy it to watch which firms and products the FDA is pulling off the market.

What you get

Every record contains:

FieldDescription
idThe FDA recall number as a string, e.g. "D-1285-2020" — globally unique across all three product types (prefixed D- drug, Z- device, F- food) — same value as recallNumber, present because every Actor in this series has an id
sourceopenfda, constant
urlA deep link into the openFDA API that returns this exact record (.../enforcement.json?search=recall_number:"..."). openFDA's enforcement reports have no stable public per-record web page — see Data source and licence
productTypedrug, device or food — which endpoint this record came from
recallNumberSame as id
eventIdFDA's internal recall-event id. Several recallNumbers (e.g. different lots or package sizes) can share one eventId when they were recalled together
classificationClass I (reasonable probability of serious harm or death), Class II (temporary/reversible harm) or Class III (unlikely to cause harm)
statusOngoing, Completed or Terminated
voluntaryMandatedVoluntary: Firm initiated or FDA Mandated
initialFirmNotificationHow the firm first notified the public/customers, e.g. "Letter", "Press Release"
recallingFirmLegal name of the firm that issued the recall
address1 / address2 / city / state / postalCode / countryRecalling firm's address. state is null for a non-US firm (FDA's own "N/A" sentinel is converted to null, not kept as the literal string)
distributionPatternWhere the product was distributed, FDA's own free text (can be a long list of countries for a device)
productDescriptionWhat was recalled
productQuantityQuantity recalled, e.g. "33,280 bottles", or null when FDA has no quantity on file ("N/A" in about 10% of records — converted to null, not kept as text)
reasonForRecallWhy it was recalled
codeInfo / moreCodeInfoLot numbers, expiry dates, UPCs and similar identifying codes, FDA's own free text
recallInitiationDateDate the firm began the recall, ISO YYYY-MM-DD
centerClassificationDateDate FDA classified the recall's severity
reportDateDate the recall was added to FDA's enforcement report — this is the date every input date filter matches against
terminationDateDate FDA terminated the recall, or null while status is Ongoing
openfdaBrandNames / openfdaGenericNames / openfdaManufacturerNames / openfdaSubstanceNames / openfdaProductNdcs / openfdaApplicationNumbersSupplementary drug identifiers openFDA sometimes links in from its own drug label/NDC databases (arrays, [] when not linked). Measured 5 October 2026 on a 1,000-record sample per endpoint: present on 16.8% of drug records, 0% of device and food records. None of these are GMDN codes — see Data source and licence
fetchedAtISO timestamp of the run that produced the record

The dataset has four views: Overview (date, product type, classification, firm, product, status, state, link), Recall reason (recall #, product, reason, quantity, distribution), Recalling firm (full address, voluntary/mandated, notification method) and Timeline and status (initiated/classified/reported/terminated dates, event id).

Input

InputTypeDefaultDescription
productTypestring (enum)alldrug, device, food, or all to query all three endpoints.
dateFrom / dateTostring (YYYY-MM-DD)noneFilters on report_date. Leave both empty to use daysBack instead.
daysBackinteger14Used only when dateFrom/dateTo are empty. Defaults to 14, not this series' usual 7, because openFDA's data updates weekly but can lag the calendar date by over a week — measured 5 October 2026, the newest report_date available was 12 days behind "today". A 14-day window reliably spans at least one update.
keywordsstring[]noneCase-insensitive OR match against productDescription or reasonForRecall, e.g. ["lead","listeria"]. Several keywords are ORed.
statesstring[]allTwo-letter USPS codes for the recalling firm's state, e.g. ["CA"]. A foreign firm has no US state and is excluded by this filter.
classificationstring (enum)noneClass I, Class II or Class III. Empty = every classification.
maxItemsinteger20Stop after this many records; you are charged per record delivered. Defaults low (20, not this series' usual 500) because the default 14-day/all-types window already matches roughly 150 records nationwide (measured below), and a Store "Try it" run should be cheap and fast.
apiKeystring (secret)noneOptional free openFDA key, raising your daily quota from 1,000 to 120,000 requests (the per-minute cap, 240, is identical either way). Get one at https://open.fda.gov/apis/authentication/. Falls back to the OPENFDA_API_KEY environment variable. The Actor works perfectly with no key at all.

Bad input fails fast: an unparseable date, a reversed date range, an unknown 2-letter state code, or a classification/productType outside the allowed values stops the run with the validation message as its status, before any request is made.

Example — every Class I drug recall in the last 90 days:

{ "productType": "drug", "daysBack": 90, "classification": "Class I", "maxItems": 200 }

Example — food recalls mentioning listeria or salmonella, any time in the last year:

{ "productType": "food", "daysBack": 365, "keywords": ["listeria", "salmonella"], "maxItems": 500 }

Example — every device recall from California firms in the last 90 days:

{ "productType": "device", "daysBack": 90, "states": ["CA"], "maxItems": 500 }

Output example

A real record from a live run on 5 October 2026 ({"maxItems": 2, "daysBack": 14}), unedited:

{
"id": "D-0845-2026",
"source": "openfda",
"url": "https://api.fda.gov/drug/enforcement.json?search=recall_number%3A%22D-0845-2026%22",
"productType": "drug",
"recallNumber": "D-0845-2026",
"eventId": "99654",
"classification": "Class II",
"status": "Ongoing",
"voluntaryMandated": "Voluntary: Firm initiated",
"initialFirmNotification": "Letter",
"recallingFirm": "Safecor Health, LLC",
"address1": "317 New Boston St",
"address2": null,
"city": "Woburn",
"state": "MA",
"postalCode": "01801-6231",
"country": "United States",
"distributionPattern": "Nationwide in the USA.",
"productDescription": "Fluphenazine HCl Elixir, USP, 15 mg per 3 mL, Delivers: 10 mL, Oral Elixir, PAI, Alcohol 14% by volume, For Oral Use Only, Rx Only, Pkg by: Safecor Health, Woburn, MA 01801. NDC: 00121-0653-04",
"productQuantity": null,
"reasonForRecall": "Failed Stability Specifications",
"codeInfo": "Lot#:26080483; Exp :03/13/2027 Lot#:25310327; Exp :08/12/2026 Lot#:25330414; Exp :08/26/2026 Lot#:25430340; Exp :11/22/2026",
"moreCodeInfo": null,
"recallInitiationDate": "2026-08-13",
"centerClassificationDate": "2026-09-14",
"terminationDate": null,
"reportDate": "2026-09-23",
"openfdaBrandNames": [],
"openfdaGenericNames": [],
"openfdaManufacturerNames": [],
"openfdaSubstanceNames": [],
"openfdaProductNdcs": [],
"openfdaApplicationNumbers": [],
"fetchedAt": "2026-10-05T03:01:43.323Z"
}

A drug record with openFDA's supplementary enrichment looks the same but carries non-empty openfdaBrandNames, openfdaManufacturerNames, openfdaSubstanceNames and openfdaProductNdcs (for example Teva's D-1285-2020, openfdaApplicationNumbers: ["ANDA040422"]). A terminated recall carries a real terminationDate instead of null. A foreign recalling firm (for example a Mexican hand-sanitizer recall, D-0080-2021) carries state: null, postalCode: null and country set to the real country.

Every run also writes a SUMMARY record to the key-value store with the filters used, how many recalls openFDA matched per product type, the API requests spent, and the windowExhausted completeness flag described below.

Pricing

Pay per result: US$0.0025 per recall record delivered (US$2.50 per 1,000 records). You are charged only for records pushed to the dataset; a run that returns nothing, or that fails validation before its first request, costs nothing. Runs on the Apify free plan are limited by the platform's free-tier usage allowance.

Data source and licence

  • Source. GET https://api.fda.gov/drug/enforcement.json, .../device/enforcement.json and .../food/enforcement.json — the three openFDA enforcement-report endpoints operated by the US Food and Drug Administration, behind its weekly Enforcement Report. Documentation: https://open.fda.gov/apis/drug/enforcement/, https://open.fda.gov/apis/device/enforcement/ and https://open.fda.gov/apis/food/enforcement/ (read 5 October 2026). No key or account is required; every request in this Actor's testing and its smoke test succeeded unauthenticated.
  • Licence. openFDA's own licence page states: "Unless otherwise noted, the content, data, documentation, code, and related materials on openFDA is public domain and made available with a Creative Commons CC0 1.0 Universal dedication" and "You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission" (https://open.fda.gov/license/, read 5 October 2026).
  • GMDN caveat (does not apply to this feed). The same licence page carries a separate restriction: "Any data/information suppliers who wish to extract GMDN Content from this site in order to use it as a basis for creating and supporting commercial services ... must first obtain an appropriate license from The GMDN Agency. No such activity is permitted without a license." GMDN (Global Medical Device Nomenclature) codes live in a different openFDA endpoint (device/recall.json), not the device/enforcement.json endpoint this Actor uses. Verified by inspecting 1,000 live device-enforcement records (5 October 2026): no GMDN field of any kind appears anywhere in this endpoint's response shape, on any product type, so no GMDN content is extracted or republished by this Actor.
  • Attribution. No specific wording is mandated by the CC0 dedication. Cite it as "Source: openFDA" and link to https://open.fda.gov.
  • Not medical advice. openFDA's own disclaimer applies to every response: "Do not rely on openFDA to make decisions regarding medical care ... you should assume all results are unvalidated." This Actor republishes the data as-is; it is a recall-monitoring and lead-generation feed, not a clinical or safety-and-soundness determination.
  • Personal data. This dataset is about firms and products, not individuals — there are no named officers, employees or customers in the enforcement-report endpoints sampled while building this Actor.

Limits and freshness

  • Volumes, measured 5 October 2026 against the live API: 17,988 total drug enforcement records, 40,057 device, 29,463 food (all-time, unfiltered). A 7-day report_date window on the most recent data then available (17–23 September 2026) held 11 drug, 88 device and 49 food recalls nationwide (148 total).
  • Data lags "today" by more than a week. Each endpoint's meta.last_updated read 2026-09-23 when queried on 2026-10-05 — a 12-day gap. openFDA's own API pages for all three endpoints state "Frequency of API updates: Weekly", but the newest report_date on file is not guaranteed to be within the last 7 calendar days. This is why daysBack defaults to 14 here rather than this series' usual 7 — see the Input table.
  • Pagination: limit is capped at 1000, skip at 25000. limit=1001 answers HTTP 400 "Limit cannot exceed 1000 results for search requests"; skip=25001 answers HTTP 400 "Skip value must 25000 or less" (both verified 5 October 2026). A page fetched at exactly skip=25000 still returns a full limit-sized page, so the deepest record reachable by plain offset paging is index 25999 — 26,000 records per query (per product type, per run). A query matching more than that cannot be read to its end this way; this Actor detects the case (SUMMARY.perType.<type>.ceilingHit) and warns rather than silently truncating. In practice this only matters for a very wide, unfiltered window — narrow dateFrom/dateTo, keywords, states or classification to stay well under it.
  • A zero-match query answers HTTP 404, not an empty 200 page ({"error":{"code":"NOT_FOUND","message":"No matches found!"}}, verified 5 October 2026). This Actor treats that as a legitimate empty result, not a failure: a keyword, state or classification matching nothing costs nothing and exits cleanly.
  • Rate limits (https://open.fda.gov/apis/authentication/, verified 5 October 2026): 240 requests/minute and 1,000 requests/day per IP unauthenticated; 240/minute and 120,000/day per key with the free optional key. The per-minute ceiling is identical either way, so this Actor paces every request at least 270ms apart (about 220/minute) regardless of whether a key is supplied, and retries 429/5xx responses with exponential back-off via fetchWithRetry.
  • state and the keyword fields are case-insensitive, unlike some of this series' other registries: state:"ca" and state:"CA" matched the identical 1,111 drug records in testing, and product_description/reason_for_recall behaved the same way.
  • Every filter is applied by openFDA, not after the fact. The date window, state, classification and keywords are all sent as search= query-string clauses, so SUMMARY.matched is openFDA's own count of the full result set and you are never charged for a record the Actor fetched and discarded. SUMMARY.windowExhausted is true only when every queried product type's full matched set was actually delivered; maxItems, the openFDA pagination ceiling, or the Apify spend limit each set it to false, and the run log plus SUMMARY.perType name which one.

Use cases

  • Recall and compliance monitoring for legal, insurance and risk teams. Filter by keyword or classification to watch for Class I recalls in a product category a client manufactures, distributes or insures.
  • Retail and supply-chain due diligence. Filter by state or firm name before onboarding a supplier, or cross-check an existing vendor list against recent drug/device/food recalls.
  • Consumer-safety and journalism research. Pull a keyword- or date-bounded slice (e.g. a specific contaminant or product category) to track how often and how severely a given issue recurs across firms.

Support

Open an issue on the Actor's Issues tab on Apify Store. Replies within 14 days. Bug reports that name the run id and the input used are fixed fastest.