Request a tool
All toolsAutomationsGuidesMCP serverRequest a toolPlatformsCategories
openFDA Drug & Device Records Scraper icon

openFDA Drug & Device Records Scraper

Pull FDA recalls, NDC drug products and 510(k) device clearances into a flat table. One dataset per run. No API key, no account, no browser.

60 runs on Apify $0.001 per record ($1 / 1,000)
Run this in the cloudRun on Apify →

Developer & Research Tools

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust dataset, search, dateFrom (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Pricing

$0.001 per record = $1 per 1,000

You are charged forWhenPrice
Record scrapedOne FDA record — a recall, a drug product or a device clearance. Queries that match nothing are never charged.$0.001

Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-20, and they are what you are actually charged.

Inputs

FieldWhat it doesType
datasetOne dataset per run. Each one has its own row shape, and the run returns only that shape. Recall datasets share a single shape across drugs, devices and food, so you can stack three runs into one table.string
searchFDA's own query syntax. Leave empty for the most recent records. Examples: recalling_firm:"Abbott" · classification:"Class I" · state:"CA"+AND+status:"Ongoing" · brand_name:"Tylenol" · device_name:"catheter" · applicant.exact:"Medtronic Inc." · "salmonella" on its own searches the whole record. Join clauses with +AND+ or +OR+, wrap phrases in double quotes, and add .exact to a field name to match the whole value instead of single words.string
dateFromEarliest date to include, as 2025-01-01 or 20250101. Applied to the date that matters for the dataset you picked: report date for recalls, marketing start date for drug products, decision date for device clearances.string
dateToLatest date to include, same format. Leave both dates empty to search the whole history.string
maxItemsHow many records to return. Records come back newest first, so a small number gives you the most recent ones. Hard ceiling 30,000 per run. In a test run, 1,000 device recalls came back in about five seconds. Keep it low while you are testing - you pay per record.integer

What you get

A structured dataset — each result includes fields like:

datasetrecordTyperecordIdrecallingFirmproductDescriptionclassificationreasonForRecallstatusreportDatebrandNamelabelerNamemarketingStartDatekNumberdeviceNameproductQuantitydistributionPatterncitystatecountryrecallInitiationDateterminationDategenericNamedosageFormrouteactiveIngredientsmarketingCategoryapplicationNumberdeaSchedulepackageCountmarketingEndDatelistingExpirationDateapplicantdeviceClassproductCoderegulationNumbermedicalSpecialtyadvisoryCommitteeclearanceTypedecisionCodedecisionDescriptiondecisionDate

Export every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.

Related tools in Developer & Research Tools

Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.

GitHub Scraper iconDeveloper & Research Tools

GitHub Scraper

Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.

18 use cases

Stack Overflow / Stack Exchange Scraper iconDeveloper & Research Tools

Stack Overflow / Stack Exchange Scraper

Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.

2 use cases

Package Registry Scraper (npm + PyPI) iconDeveloper & Research Tools

Package Registry Scraper (npm + PyPI)

Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.

2 use cases

arXiv Scraper iconDeveloper & Research Tools

arXiv Scraper

Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.

2 use cases

OpenAlex Scholarly Works Scraper iconDeveloper & Research Tools

OpenAlex Scholarly Works Scraper

Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.

2 use cases

Crossref Scholarly Works Scraper iconDeveloper & Research Tools

Crossref Scholarly Works Scraper

Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.

2 use cases

See all Developer & Research Tools →

openFDA Drug & Device Records Scraper: FDA recalls, NDC drug products and 510(k) clearances as rows

Pick one FDA dataset (drug, device or food recalls, the NDC drug directory or 510(k) device clearances), narrow it with FDA's own search syntax and a date range, and it reads the records from openFDA. You get one flat row per record with FDA's own id, the fields that dataset carries, and a link that returns the untouched original. The part to know first: FDA does not update a recall after it is classified, so status and terminationDate show the state on that day, not today, and FDA itself says these records should not drive public alerts.

InputOne dataset, an optional FDA search expression and a date range
OutputOne row per FDA record: a recall, a drug product or a device clearance
Ceiling30,000 records per run, one dataset per run
Account neededNone. No FDA API key
Price$1.00 per 1,000 records, flat on every plan

🔍 What openFDA Drug & Device Records Scraper does

Each run reads one dataset and returns one row shape. A recall, a marketed product and a premarket clearance share almost no fields, so they never land in the same table. The three recall datasets do share one field list, and three runs stack into a single spreadsheet, with the dataset column saying which run each row came from.

datasetOne row isThe date range applies toFDA refreshes it
drug-recallsone drug enforcement actionreport dateweekly
device-recallsone device enforcement actionreport dateweekly
food-recallsone food enforcement actionreport dateweekly
drug-productsone marketed product in the NDC directorymarketing start datedaily
device-clearancesone 510(k) premarket clearancedecision datemonthly

Records come newest first. FDA hands back roughly 1% of records twice on a deep run, and those repeats are dropped before you are billed.

When a search matches nothing, FDA gives the same answer whether nothing matched or a field name in your query does not exist. The run checks which one it was and tells you, on a free row.

📥 What you give it

FieldIf you leave it outWhat it does
datasetthe free sample rowdrug-recalls, device-recalls, food-recalls, drug-products or device-clearances. Empty, or any other value, returns the free sample row instead of records.
searchthe most recent recordsFDA's query syntax, such as recalling_firm:"Abbott", classification:"Class I", brand_name:"Tylenol" or device_name:"catheter". Join clauses with +AND+ or +OR+, quote phrases, and add .exact to a field name to match the whole value. A bare "salmonella" searches the whole record.
dateFromno start dateYear first: 2026-01-01, 20260101, 2026/1/5 or 2026.01.05. Anything else stops the run with a free BAD_INPUT row before FDA is asked.
dateTono end dateSame formats. A range that runs backwards stops the run the same way.
maxItems1001 to 30,000 records, newest first.
proxyConfigurationnot usedLeave it alone unless you want the traffic to go through servers of your own.
{
  "dataset": "drug-recalls",
  "search": "classification:\"Class I\"",
  "dateFrom": "2026-01-01",
  "maxItems": 100
}

📤 What you get back

A real row from a real run:

{
  "ok": true,
  "charged": true,
  "dataset": "drug-recalls",
  "recordType": "enforcement",
  "recordId": "D-0866-2026",
  "recallingFirm": "VITRUVIAS THERAPEUTICS INC",
  "productDescription": "Thyroid Tablets, USP, 1/2 Grain (30 mg), Each tablet contains: levothyroxine (T4) 19 mcg, liothyronine (T3) 4.5 mcg, 100 Tablets, Rx only, Distributed by: Vitruvias Therapeutics, Auburn, AL 36830, Product of USA, NDC 69680-166-00.",
  "classification": "Class I",
  "status": "Ongoing",
  "reasonForRecall": "Superpotent Drug",
  "productType": "Drugs",
  "eventId": "99709",
  "distributionPattern": "US Nationwide.",
  "productQuantity": "1955 bottles",
  "codeInfo": "Lot # 504950, Exp Date: 09/30/2026.",
  "voluntaryMandated": "Voluntary: Firm initiated",
  "initialFirmNotification": "Letter",
  "city": "Auburn",
  "state": "AL",
  "country": "United States",
  "postalCode": "36830-4318",
  "recallInitiationDate": "2026-08-21",
  "centerClassificationDate": "2026-09-22",
  "reportDate": "2026-09-23",
  "terminationDate": null,
  "apiUrl": "https://api.fda.gov/drug/enforcement.json?search=recall_number:%22D-0866-2026%22&limit=1",
  "scrapedAt": "2026-09-30T02:31:36.343Z"
}
FieldWhat it is
dataset, recordTypeThe dataset you asked for, and the grain of the row: enforcement, drug-product or device-clearance.
recordIdFDA's own id: a recall number, an NDC product code or a K number. Stable, so use it as the key across runs.
apiUrlThe FDA address that returns this one record untouched, including the fields left out here.
classificationFDA's hazard class. Class I is the serious end: a reasonable chance of serious harm or death.
status, terminationDateThe state when FDA classified the recall. terminationDate is null while it counts as open.
activeIngredients, routeDrug products only. Kits have no single strength or route, so both are null on kits.
packageNdcs, packageCountDrug products only. Every package code under the product, and how many there are.
deviceClassDevice clearances only. FDA's risk class, 1, 2 or 3, blank on older records.

🧾 Reading the output

Three kinds of row can share a dataset, and charged is the field that separates them.

RowHow to spot itCharged
A recordcharged: trueyes
The sample_sample: true, charged: falseno
A diagnostic_diagnostic: true, charged: false, an errorCodeno

The sample row also says recordType: "enforcement", so filter on charged == true, not on recordType.

CodeWhat it means
NO_RESULTSThe query was valid and FDA holds nothing matching it.
BAD_INPUTA field name in your search does not exist on that dataset, and the row names it. Also a date that could not be read, a backwards range, or a request FDA rejected.
SERVER_ERRORFDA refused the request several times over, which nearly always means the search expression could not be parsed. Check the brackets and quotes.
NETWORKFDA could not be reached.
TIME_BUDGETThe run hit its time limit. Records it never reached are not charged.

In the Console, switch the dataset to the view that matches your run: Recalls and enforcement, Drug products or Device clearances. The default Overview is only for a quick look.

▶️ How to run it

1. Open openFDA Drug & Device Records Scraper and click Try for free. 2. Pick a dataset in Which FDA records. The form starts on drug recalls. 3. Type a query into Search expression (optional), or clear it for the newest records. It arrives set to classification:"Class I". 4. Set From date (optional), To date (optional) and Maximum records. 5. Click Start, then open the dataset view for your run and download it as CSV, JSON or Excel.

💰 How much does it cost?

$1.00 per 1,000 records, which is $0.001 a record, flat on every Apify plan. The 100-record example above comes to ten cents.

You pay for record rows and nothing else per row. The sample row and every diagnostic row are free, and so are the repeats FDA hands back twice. A run stopped on an unreadable date asks FDA for nothing. If you set a maximum charge on the run, it stops at that figure instead of going past it.

💡 What people use it for

  • Watching for recalls that touch your own supply chain: drug-recalls on a schedule with a recalling_firm or product term, compared on recordId.
  • Keeping a formulary or product catalogue current with drug-products filtered by labeler_name.
  • Tracking who cleared which medical device and when, with device-clearances filtered by product_code or advisory_committee.
  • Food-safety monitoring for a distributor or a grocery buyer, with food-recalls narrowed to a state.

🚧 What it does not do

  • No adverse-event reports. Those describe individual patients, so they are left out on purpose.
  • No drug labelling text. The prescribing sections are a different shape and far larger per record.
  • No medical advice, and no alert service. FDA says these records should not be relied on for medical decisions or public alerting.
  • Recalls are not kept current by FDA. One shown as Ongoing may have ended months ago without the record changing.
  • FDA's refresh schedule, not ours. A clearance decided yesterday may not show up for weeks.
  • No product matching. A recall carries FDA's free-text productDescription, so tying it to your own SKU is still your job.
  • US FDA only. No other regulator is included.

🧭 Which public records scraper do you need?

If you wantUse
FDA recalls, NDC drug products and 510(k) clearancesThis one
Safety recalls and specs for a vehicle, by VINVIN Decoder & Recalls
Patents by keyword, inventor or assigneeGoogle Patents Search Scraper
Federal court cases, such as product liability suitsUS Federal Court Cases Scraper

❓ Questions people ask

Do I need an FDA API key? No. Nothing to sign up for, and the run never asks for a credential.

How many records can one run return? Up to 30,000. For more, split the date range across runs.

What happens if my search matches nothing? One free row. NO_RESULTS means FDA genuinely holds nothing. BAD_INPUT means a field name in your query does not exist on that dataset, and the row names it.

Can I combine the three recall datasets? Yes. They share one field list, so three runs concatenate into one table.

Can I use this to send recall alerts to customers or patients? No. FDA says so about the source itself: the records are not updated after classification. Use them for research and monitoring, and FDA's own announcements for anything that warns people.

Where does the data come from? From openFDA, the FDA's public API, which places these records in the public domain. FDA does not endorse this actor.

🆘 If something breaks

Open the Issues tab on the actor page and send the run ID with the input you used. The errorCode and details on a diagnostic row usually name the reason already.