openFDA API: FDA Recalls, Drug Labels & Device Reports
Pricing
from $1.40 / 1,000 fda record returneds
openFDA API: FDA Recalls, Drug Labels & Device Reports
Query the official openFDA API in one actor: drug, device and food recall enforcement reports, SPL drug labels by generic or brand name, and device adverse-event (MAUDE) reports with detail links that open. Filter by keyword, recall class, status, state, event type and date. No API key.
Pricing
from $1.40 / 1,000 fda record returneds
Rating
0.0
(0)
Developer
Samat Makatov
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Pull four kinds of FDA record straight from the official openFDA API (api.fda.gov) as flat, ready-to-use rows: recall
enforcement reports for drugs, medical devices and food; structured product labels (SPL) for any brand or generic drug
name; medical device adverse event reports from MAUDE; and the export status of every openFDA dataset. Every row carries
the same handful of shared columns (recordId, title, organization, category, date, url), every date is UTC
ISO-8601, and every report links back to the exact openFDA record it came from. No API key, no proxy, no browser.
The data is US public-domain FDA data. It is not medical advice: a recall or an adverse event record is a report, not proof that a product caused harm, and the datasets can be incomplete or several weeks behind.
Use cases
- Pharmacovigilance and pharmacy compliance: a standing list of Class I drug recalls of the last 12 months, with the reason, the recalling firm and the distribution pattern, to check against your own stock lists.
- Food safety and retail QA: every food recall whose stated reason mentions an undeclared allergen or ingredient, so a grocery or private-label buyer can react the same week the recall is published.
- Hospital biomedical engineering and device distributors: the running list of device recall enforcement reports, filtered to a device family or a manufacturer, to match against installed equipment.
- Clinical and pharmacy app developers: the newest SPL label per generic name (set id, version, NDC codes, route, labeler, application number), which answers "which label do I show for this drug" without parsing SPL XML.
- Device safety, quality and product-liability research: MAUDE reports for one device type or product code, with device and patient problem codes and a detail link that actually opens the report on the FDA site.
- Data engineering:
mode: "datasets"answers "how fresh is FDA data right now" — export date and record count for each of the 30 openFDA datasets — before a pipeline run trusts them. - Monitoring:
sinceDaysplusonlyNewturns any of the above into a scheduled watchlist that emits only records it has not delivered before.
Input
Nothing is required. The defaults run a drug-recall search; query is the only field with a prefilled example.
| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
mode | string | recalls | recalls, drugLabels, deviceEvents, datasets — see Modes |
productArea | string | drug | drug, device, food. Which enforcement dataset recalls reads; ignored by other modes |
query | string | (prefill insulin) | Search words. Several words are searched as a phrase. Empty = everything matching the filters |
searchField | string | auto | Which field query is matched in — see Search fields |
classifications | string[] | all | Class I, Class II, Class III. recalls only — see Recall classes |
recallStatuses | string[] | all | Ongoing, Completed, Terminated, Pending. recalls only |
eventTypes | string[] | all | Death, Injury, Malfunction, Other, No answer provided. deviceEvents only |
productCode | string | — | Three-character FDA device product code, e.g. QFG. deviceEvents only — see Device product codes |
state | string | — | Two-letter US state of the recalling firm, e.g. NJ. recalls only |
country | string | — | Country of the recalling firm as FDA spells it, e.g. United States. recalls only |
sinceDays | integer | — | 1–3650. Keep only records whose main date falls in the last N days (UTC). Wins over fromDate/toDate |
fromDate | string | — | Earliest date, YYYY-MM-DD, inclusive |
toDate | string | — | Latest date, YYYY-MM-DD, inclusive |
onlyNew | boolean | false | Emit only records this input has not delivered in an earlier run |
sortOrder | string | newest | newest or oldest, by the mode's main date |
maxItems | integer | 25 | 1–5000 rows |
includeText | boolean | false | Add SPL section text (drugLabels) or the report narrative (deviceEvents) |
labelSections | string[] | purpose, indications and usage, warnings | Which SPL sections includeText adds — see Label sections |
fields | string[] | all | Keep only these output fields, in this order |
customSearch | string | — | A raw openFDA expression ANDed to everything above — see Raw search expressions |
Choice fields accept only the listed values (Apify rejects anything else before the run starts). Free-text fields are
normalised — query is trimmed, state and productCode are upper-cased — and an impossible combination (e.g.
productCode with mode: "recalls") fails the run with a clear message instead of quietly returning something wider.
Reference
Modes
mode | openFDA endpoint | What one row is | Filters that apply |
|---|---|---|---|
recalls | /{drug|device|food}/enforcement.json | One recall enforcement report | query, searchField, classifications, recallStatuses, state, country, dates |
drugLabels | /drug/label.json | One SPL label version | query, searchField, dates (effective_time), includeText, labelSections |
deviceEvents | /device/event.json | One MAUDE adverse event report | query, searchField, eventTypes, productCode, dates, includeText |
datasets | /download.json | One openFDA dataset (30 of them) | none — the date and search filters are ignored and reported as such |
The main date differs per mode: report_date for recalls, effective_time for labels, date_received for device
reports. sinceDays, fromDate, toDate and sortOrder all work on that field.
Recall classes
| Value | What FDA means by it |
|---|---|
Class I | Reasonable probability that the product causes serious harm or death |
Class II | May cause temporary or medically reversible harm; serious harm unlikely |
Class III | Unlikely to cause harm, but the product violates FDA regulations |
Recall statuses are Ongoing (the firm is still recalling), Completed, Terminated (FDA closed the recall) and
Pending (not yet classified).
Search fields
searchField picks the field query is matched in. A field an endpoint does not have falls back to auto, with a
warning in the log — the search is never silently widened.
searchField | recalls | drugLabels | deviceEvents |
|---|---|---|---|
auto | every field of the record | every field | every field |
product | product_description | package label text | device.generic_name |
reason | reason_for_recall | → auto | → auto |
firm | recalling_firm | → auto | manufacturer_name |
brandName | openfda.brand_name | openfda.brand_name | device.brand_name |
genericName | openfda.generic_name | openfda.generic_name | device.generic_name |
manufacturer | openfda.manufacturer_name | openfda.manufacturer_name | device.manufacturer_d_name |
productCode | not possible (see below) | → auto | device.device_report_product_code |
identifier | recall_number | set_id, SPL id or NDC | MAUDE report number or report key |
identifier looks up one exact record. When the id does not exist, the run writes a single row with found: false
instead of returning nothing.
Device recall records carry no product code — measured on 2026-09-19, 0 of 39,949 device enforcement records have
one and there is no field to fall back to. productCode (or searchField: "productCode") combined with
mode: "recalls" therefore fails with a message instead of returning an unfiltered list. Search device recalls by
product description or recalling firm instead.
Device product codes
A product code is FDA's three-letter key for a device type. Examples verified in the device classification data:
| Code | Device type | Class |
|---|---|---|
QFG | Alternate controller enabled insulin infusion pump | 2 |
QJY | Infusion pump, drug specific, pharmacy-filled | 2 |
DTE | Pulse generator, pacemaker, external | 2 |
DXY | Implantable pacemaker pulse generator | 3 |
PQF | Sensor, glucose, invasive, non-adjunctive | 3 |
DQO | Catheter, intravascular, diagnostic | 2 |
QOS | Continuous ventilator | — |
JDG | Prosthesis, hip, femoral component, cemented, metal | 2 |
To find the code for any other device, search FDA's classification list by name in a browser:
https://api.fda.gov/device/classification.json?search=device_name:"insulin pump"&limit=10. Each device event row also
carries the code that FDA filed it under, so one broad genericName run tells you which code to narrow to.
Label sections
includeText adds a sections object to drug label rows. labelSections picks which ones: purpose,
indicationsAndUsage, dosageAndAdministration, warnings, adverseReactions, contraindications,
activeIngredient, inactiveIngredient, description, howSupplied. Sections a particular label does not have are
left out. These fields can be tens of kilobytes each, which is why they are off by default.
Raw search expressions
customSearch is ANDed to everything the input already built, for people who know the openFDA field names. Useful
starting points:
- drug labels:
openfda.pharm_class_epc:"Nonsteroidal Anti-inflammatory Drug [EPC]",openfda.route:"ORAL",openfda.is_original_packager:true - enforcement:
voluntary_mandated:"Voluntary: Firm initiated",distribution_pattern:"Nationwide" - device events:
patient.patient_problems:"Death",device.device_class:3,source_type:"Consumer"
Multi-word values must be quoted. An expression openFDA cannot parse fails the run with the API's own message, naming
customSearch as the culprit.
Examples
Class I drug recalls of the last 12 months — pharmacovigilance and pharmacy compliance.
{ "mode": "recalls", "productArea": "drug", "classifications": ["Class I"], "sinceDays": 365, "sortOrder": "newest", "maxItems": 20 }
Food recalls for undeclared allergens — food safety and retail QA.
{ "mode": "recalls", "productArea": "food", "query": "undeclared", "searchField": "reason", "sinceDays": 180, "maxItems": 20 }
Device recalls of the last 6 months from one firm — hospital biomedical engineering.
{ "mode": "recalls", "productArea": "device", "query": "Medtronic", "searchField": "firm", "sinceDays": 180, "maxItems": 25 }
Newest drug labels for a generic name — clinical and pharmacy apps.
{ "mode": "drugLabels", "query": "ibuprofen", "searchField": "genericName", "sortOrder": "newest", "maxItems": 15 }
Adverse event reports for one device family — device safety and liability research.
{ "mode": "deviceEvents", "query": "infusion pump", "searchField": "genericName", "sinceDays": 180, "maxItems": 20 }
Device reports with a death outcome, as a daily watchlist — safety monitoring.
{ "mode": "deviceEvents", "eventTypes": ["Death"], "sinceDays": 180, "onlyNew": true, "maxItems": 15 }
How fresh is each FDA dataset — data engineering.
{ "mode": "datasets", "maxItems": 40 }
Output
One real row from a cloud run with the default input (mode: "recalls", productArea: "drug", query: "insulin"),
with the long codeInfo trimmed:
{"mode": "recalls","recordId": "D-0806-2026","title": "MYXREDLIN, Insulin Human in 0.9% Sodium Chloride Injection, 100 units/100 mL (1 unit/mL), Rx only, Baxter Healthcare Corporation, Deerfield, IL 60015 USA, NDC 0338-0126-12.","organization": "Baxter Healthcare Corporation","category": "Class II","date": "2026-09-09","status": "Ongoing","found": true,"recallNumber": "D-0806-2026","eventId": "99500","productArea": "drug","classification": "Class II","recallStatus": "Ongoing","productType": "Drugs","productDescription": "MYXREDLIN, Insulin Human in 0.9% Sodium Chloride Injection, 100 units/100 mL (1 unit/mL), Rx only, Baxter Healthcare Corporation, Deerfield, IL 60015 USA, NDC 0338-0126-12.","reasonForRecall": "CGMP Deviations","productQuantity": null,"codeInfo": "Lot # NC188630, NC188708, NC188791, NC188845, NC188876, NC188968, Exp Date: 12/31/2027; Lot # NC193719, NC193849, …","moreCodeInfo": null,"distributionPattern": "US Nationwide; Puerto Rico; and Chile.","recallingFirm": "Baxter Healthcare Corporation","firmAddress": "1 Baxter Pkwy","firmCity": "Deerfield","firmState": "IL","firmPostalCode": "60015-4625","firmCountry": "United States","voluntaryMandated": "Voluntary: Firm initiated","initialFirmNotification": "Letter","recallInitiationDate": "2026-08-06","centerClassificationDate": "2026-08-28","reportDate": "2026-09-09","brandNames": ["MYXREDLIN"],"genericNames": ["INSULIN HUMAN"],"manufacturerNames": ["Baxter Healthcare Corporation"],"productNdcs": ["0338-0126"],"url": "https://api.fda.gov/drug/enforcement.json?search=recall_number%3A%22D-0806-2026%22&limit=1","detailUrl": null,"fetchedAt": "2026-09-19T20:14:59.688Z"}
Shared fields, present in every mode:
| Field | Type | Meaning |
|---|---|---|
mode | string | Which job produced the row |
recordId | string | Recall number, SPL set id, MAUDE report number or dataset key |
title | string | Product description, brand (generic) name, device brand or dataset name |
organization | string | Recalling firm, labeler or device manufacturer |
category | string | Recall class, product type, event type or dataset category |
date | string | The mode's main date, YYYY-MM-DD UTC |
status | string | Recall status or MAUDE report type; empty for labels and datasets |
found | boolean | false only for an identifier lookup that matched nothing |
url | string | openFDA URL that returns exactly this record |
detailUrl | string | FDA or label page for the record, where one exists |
fetchedAt | string | Fetch time, ISO 8601 UTC |
recalls adds: recallNumber, eventId, productArea, classification, recallStatus, productType,
productDescription, reasonForRecall, productQuantity, codeInfo, moreCodeInfo, distributionPattern,
recallingFirm, firmAddress, firmCity, firmState, firmPostalCode, firmCountry, voluntaryMandated,
initialFirmNotification, recallInitiationDate, centerClassificationDate, reportDate, brandNames[],
genericNames[], manufacturerNames[], productNdcs[].
drugLabels adds: setId, splId, version, effectiveDate, brandName, brandNames[], genericName,
genericNames[], manufacturerName, substanceNames[], productNdcs[], packageNdcs[], routes[], productType,
rxcuis[], pharmClasses[], applicationNumbers[], isOriginalPackager, labelUrl, and sections{} with
includeText. The scalar brandName/genericName are the first entry of the matching array, so filters and
spreadsheets work on a string.
deviceEvents adds: reportNumber, mdrReportKey, eventType, dateReceived, dateOfEvent, dateReport,
sourceType, deviceBrandName, deviceGenericName, deviceManufacturer, productCode, modelNumber,
catalogNumber, lotNumber, udiDi, deviceName, deviceClass, medicalSpecialty, regulationNumber,
productProblems[], patientProblems[], deviceCount, patientCount, remedialAction, summaryReportFlag, and
narrative with includeText.
datasets adds: datasetKey, endpointCategory, endpointName, exportDate, totalRecords, partitionCount,
downloadSizeMb, downloadUrl.
Link fields: url is always an api.fda.gov query that returns this single record. For device reports detailUrl is
the MAUDE detail page built from mdr_report_key plus the product code — building it from the report number instead is
the usual reason such links do not open. For drug labels detailUrl and labelUrl point at the DailyMed page of the
SPL set; for datasets detailUrl is the openFDA documentation page. Recall enforcement records have no per-record page
on the FDA site, so detailUrl is empty there.
Dataset views: Overview (works for every mode), Recall enforcement reports, Drug labels (SPL), Device adverse events.
A SUMMARY record in the run's key-value store holds the endpoint and the exact search expression that was sent, the
dataset's last_updated date, the total number of matches, rows written, API requests used, how many rows were dropped
by a filter or as already seen, every input warning, and the FDA disclaimer.
Use it from code / agents
curl -X POST "https://api.apify.com/v2/acts/yadroo~openfda-records/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"mode":"recalls","productArea":"drug","classifications":["Class I"],"sinceDays":365,"maxItems":20}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('yadroo/openfda-records').call({mode: 'deviceEvents', query: 'infusion pump', searchField: 'genericName', sinceDays: 180, maxItems: 20,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("yadroo/openfda-records").call(run_input={"mode": "drugLabels", "query": "ibuprofen", "searchField": "genericName", "maxItems": 15,})items = client.dataset(run["defaultDatasetId"]).list_items().items
For LLM agents, fields keeps rows small enough to paste into a prompt:
{"mode":"recalls","classifications":["Class I"],"sinceDays":90,"fields":["recallNumber","productDescription","reasonForRecall","recallingFirm","reportDate"]}.
MCP: add https://mcp.apify.com to Claude / Cursor / any MCP client and call the yadroo/openfda-records tool with the
same JSON input.
Pricing
Pay per event: $0.001 per run start + $0.002 per dataset row. Nothing else is charged; there is no proxy and no browser in the run.
| Run | Cost |
|---|---|
| The default 25-row recall search | $0.051 |
| 50 rows (a typical monitoring run, ~40 s) | $0.101 |
| 500 rows | $1.001 |
Lower tiers on Apify reduce the per-row price automatically (Bronze −10 %, Silver −20 %, Gold and above −30 %).
Limits & FAQ
- Rate limit. The actor uses openFDA without an API key: 240 requests per minute and 1,000 per day per IP. A run needs one request per 1,000 recall rows (500 for labels and device reports) plus a retry budget, so a normal run costs 1–2 requests. 429 answers are retried with backoff.
- Paging depth. openFDA refuses to page past 25,000 records of one query.
maxItemsis capped at 5,000, which stays well inside that, but a query that would need to skip further stops with a message asking you to narrow the filters. - Freshness. FDA publishes in batches, roughly weekly. On 2026-09-19 the enforcement datasets were last updated
2026-09-09 and device events 2026-09-08, with the newest report received 2026-08-31. A
sinceDayswindow shorter than about 30 days can legitimately return nothing — that is the source, not the actor. - Empty results are not errors. openFDA answers "no matches" with HTTP 404; the actor reports zero rows with a
message that says what to widen. Only an
identifierlookup writes a row (found: false), so you can tell "id does not exist" from "run failed". openfdais often empty. Device and food recall records usually have noopenfdablock, sobrandNames,genericNamesandproductNdcsare empty arrays on those rows. Drug recalls and labels do have it.- Recall numbers can be missing. Food enforcement records are published before FDA assigns a recall number; those
rows fall back to
event-<eventId>asrecordIdand leaverecallNumberempty. - No personal data. MAUDE records contain patient age, sex, weight, race and ethnicity and a manufacturer contact person with phone and address. None of those are ever written to the dataset. Patient problem codes and the device and patient counts are kept, because that is what safety analysis needs. Company names and addresses in recall records stay — they are the subject of the record.
- What is not here. This actor covers four endpoints. openFDA has more (drug adverse events/FAERS, NDC directory,
510(k), UDI, Drugs@FDA, Orange Book, drug shortages, food and cosmetic events, tobacco reports);
mode: "datasets"lists them all with their freshness, but reading their records is not implemented. FDA guidance documents are not part of openFDA at all. - Is this medical advice? No. openFDA data is provided as-is. Adverse event and recall records are reports and do not establish that a product caused a problem. Do not use this data to make decisions about medical care.
- Licence. FDA data is US public domain and may be used commercially; FDA asks for credit and does not endorse any
product built on it. The actor calls only
api.fda.gov.
Made by Yadroo. Sibling actors: sec-edgar-filings, sanctions-screen, world-bank-indicators, openalex-works, public-holidays.