openFDA Enforcement Recalls Scraper
Pricing
$2.50 / 1,000 recall record returneds
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
17 days ago
Last modified
Categories
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:
| Field | Description |
|---|---|
id | The 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 |
source | openfda, constant |
url | A 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 |
productType | drug, device or food — which endpoint this record came from |
recallNumber | Same as id |
eventId | FDA's internal recall-event id. Several recallNumbers (e.g. different lots or package sizes) can share one eventId when they were recalled together |
classification | Class I (reasonable probability of serious harm or death), Class II (temporary/reversible harm) or Class III (unlikely to cause harm) |
status | Ongoing, Completed or Terminated |
voluntaryMandated | Voluntary: Firm initiated or FDA Mandated |
initialFirmNotification | How the firm first notified the public/customers, e.g. "Letter", "Press Release" |
recallingFirm | Legal name of the firm that issued the recall |
address1 / address2 / city / state / postalCode / country | Recalling 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) |
distributionPattern | Where the product was distributed, FDA's own free text (can be a long list of countries for a device) |
productDescription | What was recalled |
productQuantity | Quantity 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) |
reasonForRecall | Why it was recalled |
codeInfo / moreCodeInfo | Lot numbers, expiry dates, UPCs and similar identifying codes, FDA's own free text |
recallInitiationDate | Date the firm began the recall, ISO YYYY-MM-DD |
centerClassificationDate | Date FDA classified the recall's severity |
reportDate | Date the recall was added to FDA's enforcement report — this is the date every input date filter matches against |
terminationDate | Date FDA terminated the recall, or null while status is Ongoing |
openfdaBrandNames / openfdaGenericNames / openfdaManufacturerNames / openfdaSubstanceNames / openfdaProductNdcs / openfdaApplicationNumbers | Supplementary 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 |
fetchedAt | ISO 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
| Input | Type | Default | Description |
|---|---|---|---|
productType | string (enum) | all | drug, device, food, or all to query all three endpoints. |
dateFrom / dateTo | string (YYYY-MM-DD) | none | Filters on report_date. Leave both empty to use daysBack instead. |
daysBack | integer | 14 | Used 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. |
keywords | string[] | none | Case-insensitive OR match against productDescription or reasonForRecall, e.g. ["lead","listeria"]. Several keywords are ORed. |
states | string[] | all | Two-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. |
classification | string (enum) | none | Class I, Class II or Class III. Empty = every classification. |
maxItems | integer | 20 | Stop 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. |
apiKey | string (secret) | none | Optional 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.jsonand.../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 thedevice/enforcement.jsonendpoint 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_datewindow 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_updatedread 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 newestreport_dateon file is not guaranteed to be within the last 7 calendar days. This is whydaysBackdefaults to 14 here rather than this series' usual 7 — see the Input table. - Pagination:
limitis capped at 1000,skipat 25000.limit=1001answers HTTP 400 "Limit cannot exceed 1000 results for search requests";skip=25001answers HTTP 400 "Skip value must 25000 or less" (both verified 5 October 2026). A page fetched at exactlyskip=25000still returns a fulllimit-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 — narrowdateFrom/dateTo,keywords,statesorclassificationto 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. stateand the keyword fields are case-insensitive, unlike some of this series' other registries:state:"ca"andstate:"CA"matched the identical 1,111 drug records in testing, andproduct_description/reason_for_recallbehaved 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, soSUMMARY.matchedis openFDA's own count of the full result set and you are never charged for a record the Actor fetched and discarded.SUMMARY.windowExhaustedistrueonly 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 tofalse, and the run log plusSUMMARY.perTypename 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.