# FDA Recalls, Adverse Events & 510(k) Scraper (openFDA) (`deanko/openfda-scraper`) Actor

Export FDA drug, food & device recalls, FAERS adverse events, 510(k) clearances and drug labels from the official openFDA API. No API key, no 25k record cap.

- **URL**: https://apify.com/deanko/openfda-scraper.md
- **Developed by:** [dean ko](https://apify.com/deanko) (community)
- **Categories:** Lead generation, Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## FDA Recalls, Adverse Events & 510(k) Scraper (openFDA API)

Export **FDA recalls, FDA adverse events (FAERS), 510(k) clearances and drug labels** from **openFDA**, the U.S. Food and Drug Administration's official public API. You get clean, flat rows ready for Excel, Google Sheets, BI tools or your database. One Actor covers six of the most-used FDA datasets, with no API key, no proxies and no 25,000-record cap.

| Dataset (`dataset` value) | What it contains | Records in openFDA |
|---|---|---|
| **Drug recalls** (`drug/enforcement`) | FDA enforcement reports for drug recalls | 18,000+ |
| **Food recalls** (`food/enforcement`) | Food, beverage and dietary supplement recalls | 29,000+ |
| **Medical device recalls** (`device/enforcement`) | Medical device recalls | 40,000+ |
| **Drug adverse events** (`drug/event`) | FAERS reports of side effects and medication errors | 20 million+ |
| **Device 510(k) clearances** (`device/510k`) | Premarket notifications and FDA decisions | 176,000+ |
| **Drug labels** (`drug/label`) | Structured prescribing information and package inserts (SPL) | 260,000+ |

### Who it's for and use cases

- **Recall monitoring and food safety teams.** Schedule a daily run for new Class I recalls (`dateFrom` = yesterday) and send alerts to Slack or email with webhooks.
- **Pharmacovigilance and drug safety.** Pull FAERS adverse event reports for a drug. Each row shows suspect vs. concomitant drugs, seriousness flags (death, hospitalization, life-threatening) and MedDRA reaction terms.
- **Medical device market intelligence.** List every 510(k) clearance for a competitor or product code, and measure time from submission to decision.
- **Supplier risk and compliance.** Check whether a manufacturer or supplier has a recall history and how severe it was.
- **Regulatory affairs and consulting.** Build datasets of labels, boxed warnings and indications across a drug class.
- **Research, journalism and legal work.** Analyse recall trends, adverse event patterns or a company's history.

### Why this Actor

- **Six FDA datasets, one input form.** Drug, food and device recalls, FAERS adverse events, 510(k) clearances and drug labels. You don't need a separate Actor for each one.
- **No 25,000-record ceiling.** openFDA's `skip` parameter stops at 25,000 records. This Actor follows openFDA's `search_after` links instead, so a single run can export a full dataset.
- **Same leading columns for every dataset.** `dataset`, `recordId`, `date`, `company`, `title` and `url` always come first, so you can combine recalls, clearances and labels in one sheet.
- **Decoded, analysis-ready fields.** FAERS numeric codes become readable values (`patientSex: "Female"`, `patientAgeUnit: "Year"`, `reporterQualification: "Physician"`, drug role `Suspect`/`Concomitant`). Seriousness flags are booleans. All dates are ISO `YYYY-MM-DD`.
- **Links included.** 510(k) rows link to the FDA 510(k) database, and drug label rows link to DailyMed.
- **Privacy by design.** 510(k) contact-person names and adverse event reporter or sender details are dropped.
- **Low, predictable price.** $0.70 per 1,000 records. The run stops automatically when your maximum charge is reached.
- **Clear failures.** Invalid openFDA queries fail with openFDA's error message instead of returning an empty "successful" run. Temporary HTTP errors are retried automatically, with a longer wait each time.

### How to use

1. Click **Try for free** (or **Start**) on this page.
2. Choose a **Dataset**, for example *Food recalls*.
3. Optionally add **Search text** (`listeria`), **Company names** (`Medtronic`), a **date range** or a **recall class**.
4. Set **Max records**. The default is 20, which costs about $0.01.
5. Click **Start**, then download the results as JSON, CSV, Excel, XML or HTML from the **Output** tab.

Different filters are combined with **AND**. Several company names or recall classes are combined with **OR**. With the default input, the Actor returns the 20 newest FDA drug recalls in a few seconds.

**Example: Class I Listeria food recalls in 2024**

```json
{
  "dataset": "food/enforcement",
  "searchText": "listeria",
  "classifications": ["Class I"],
  "dateFrom": "2024-01-01",
  "dateTo": "2024-12-31",
  "maxItems": 1000
}
```

**Example: FDA adverse event reports for Ozempic since 2024**

```json
{
  "dataset": "drug/event",
  "searchText": "patient.drug.medicinalproduct:\"OZEMPIC\"",
  "dateFrom": "2024-01-01",
  "maxItems": 5000
}
```

**Example: Medtronic 510(k) clearances since 2023**

```json
{ "dataset": "device/510k", "firmNames": ["Medtronic"], "dateFrom": "2023-01-01", "maxItems": 500 }
```

#### Input fields

| Field | Description |
|---|---|
| `dataset` | `drug/enforcement` (default), `food/enforcement`, `device/enforcement`, `drug/event`, `device/510k` or `drug/label` |
| `searchText` | Free-text search across all fields, e.g. `listeria`. Also accepts openFDA syntax, e.g. `reason_for_recall:undeclared` |
| `firmNames` | Company names (OR-ed): recalling firm, 510(k) applicant, or manufacturer |
| `dateFrom` / `dateTo` | `YYYY-MM-DD`. Uses the report date (recalls), receive date (adverse events), decision date (510(k)) or effective date (labels) |
| `classifications` | Recalls only: `Class I`, `Class II`, `Class III` |
| `sortOrder` | `desc` (newest first, default) or `asc` |
| `maxItems` | Maximum number of records (default 20) |
| `apiKey` | Optional free openFDA API key. You only need it for very large exports |

### Output sample

Every row starts with `dataset`, `recordId`, `date`, `company`, `title`, `url` and ends with `scrapedAt`. Dataset-specific columns come in between.

**Drug recall** (real record from the default input):

```json
{
  "dataset": "drug/enforcement",
  "recordId": "D-0879-2026",
  "date": "2026-10-07",
  "company": "SV Labs Toronto Corporation",
  "title": "Benzoyl Peroxide Wash, USP, 10%, packaged in a)5 FL OZ (148mL) bottles ...",
  "url": null,
  "recallNumber": "D-0879-2026",
  "status": "Ongoing",
  "classification": "Class II",
  "reasonForRecall": "Microbial contamination of topical product.",
  "distributionPattern": "IL only",
  "voluntaryMandated": "Voluntary: Firm initiated",
  "recallingFirm": "SV Labs Toronto Corporation",
  "city": "Brampton",
  "country": "Canada",
  "recallInitiationDate": "2026-09-01",
  "reportDate": "2026-10-07",
  "terminationDate": null,
  "scrapedAt": "2026-10-19T08:23:05.442Z"
}
```

**Drug adverse event (FAERS):**

```json
{
  "dataset": "drug/event",
  "recordId": "26940200",
  "date": "2026-06-30",
  "company": "Novo Nordisk Pharmaceutical Industries, LP",
  "title": "Monoplegia, Back pain, Product use in unapproved indication",
  "serious": true,
  "seriousnessDeath": false,
  "seriousnessHospitalization": true,
  "reporterQualification": "Consumer or non-health professional",
  "patientAge": 45,
  "patientAgeUnit": "Year",
  "patientSex": "Female",
  "reactions": ["Monoplegia", "Back pain", "Product use in unapproved indication"],
  "suspectDrugs": ["OZEMPIC"],
  "drugs": [{ "name": "OZEMPIC", "role": "Suspect", "indication": "Obesity", "activeSubstance": "SEMAGLUTIDE" }]
}
```

#### Columns by dataset

| Dataset | Main columns |
|---|---|
| All datasets | `dataset`, `recordId`, `date`, `company`, `title`, `url`, `scrapedAt` |
| Recalls (drug, food, device) | `recallNumber`, `eventId`, `status`, `classification`, `productType`, `productDescription`, `reasonForRecall`, `productQuantity`, `codeInfo`, `distributionPattern`, `voluntaryMandated`, `initialFirmNotification`, `recallingFirm`, `address`, `city`, `state`, `postalCode`, `country`, `recallInitiationDate`, `centerClassificationDate`, `reportDate`, `terminationDate`, `brandNames`, `genericNames` |
| Drug adverse events (FAERS) | `safetyReportId`, `receiveDate`, `receiptDate`, `serious`, `seriousnessDeath`, `seriousnessHospitalization`, `seriousnessLifeThreatening`, `seriousnessDisabling`, `seriousnessCongenitalAnomaly`, `seriousnessOther`, `occurCountry`, `reporterCountry`, `reporterQualification`, `patientAge`, `patientAgeUnit`, `patientSex`, `patientWeightKg`, `reactions`, `suspectDrugs`, `drugs`, `manufacturers` |
| Device 510(k) | `kNumber`, `deviceName`, `applicant`, `decisionDate`, `dateReceived`, `decisionCode`, `decisionDescription`, `clearanceType`, `productCode`, `deviceClass`, `regulationNumber`, `genericDeviceName`, `advisoryCommittee`, `thirdPartyReview`, `expeditedReview`, `statementOrSummary`, `city`, `state`, `postalCode`, `countryCode` |
| Drug labels | `splId`, `setId`, `effectiveDate`, `version`, `brandNames`, `genericNames`, `manufacturers`, `productType`, `routes`, `substances`, `productNdcs`, `applicationNumbers`, `pharmClasses`, `boxedWarning`, `indicationsAndUsage`, `dosageAndAdministration`, `contraindications`, `warnings`, `adverseReactions`, `drugInteractions`, `activeIngredient`, `purpose` |

### Pricing

This Actor uses **pay per event**. Platform usage is included, so you pay only for:

- **$0.0007 per record** saved to the dataset ($0.70 per 1,000 records), and
- **$0.00005 per run start** per GB of memory. At the default 4 GB that is $0.0002 per run.

**Worked example.** You export 10,000 Ozempic adverse event reports in one run: 10,000 × $0.0007 = $7.00, plus $0.0002 for the start, for about **$7.00** in total. The default input (20 drug recalls) costs about **$0.01**. If you set a maximum total charge for a run, the Actor stops as soon as the next record would exceed it.

### Integrations and API

Run the Actor from your own code with the official `apify-client` package. Replace `<YOUR_APIFY_TOKEN>` with your token from **Console > Settings > API & Integrations**.

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("deanko/openfda-scraper").call(run_input={
    "dataset": "device/enforcement",
    "classifications": ["Class I"],
    "dateFrom": "2026-01-01",
    "maxItems": 200,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["date"], item["company"], item["reasonForRecall"])
```

**JavaScript / Node.js**

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('deanko/openfda-scraper').call({
    dataset: 'drug/event',
    searchText: 'patient.drug.medicinalproduct:"METFORMIN"',
    maxItems: 500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
```

You can also connect the Actor to Make, Zapier, n8n, Google Sheets, Slack, webhooks or the Apify MCP server. Use **Schedules** to run it daily for recall or 510(k) monitoring.

### FAQ

**Where does the data come from?**
From the official [openFDA API](https://open.fda.gov/apis/) (`api.fda.gov`), published by the U.S. Food and Drug Administration. The Actor does not scrape web pages and doesn't need proxies.

**Do I need an openFDA API key?**
No. Without a key, openFDA allows 240 requests per minute and 1,000 requests per day per IP address. Each request returns up to 1,000 records. A free key raises the daily limit to 120,000 requests. Add one in `apiKey` only for very large or frequent exports. The Actor waits between pages to stay under the per-minute limit.

**How do I search a specific field?**
Use openFDA query syntax in `searchText`, e.g. `reason_for_recall:undeclared`, `openfda.generic_name:"metformin"` or `patient.reaction.reactionmeddrapt:"nausea"`. See the [openFDA query syntax docs](https://open.fda.gov/apis/query-syntax/).

**How fresh is the data?**
Every run queries openFDA live, so you get whatever openFDA has published at that moment. openFDA updates each dataset on its own schedule (FAERS, for example, is quarterly). The [openFDA dataset pages](https://open.fda.gov/apis/) show each dataset's last update date.

**Why did my search return nothing?**
openFDA matches whole words. Try a shorter term, a different spelling, or field syntax. Company names match words inside the name, so `Pfizer` finds "Pfizer Laboratories Div Pfizer Inc".

**Is an adverse event report proof that a drug caused the reaction?**
No. FAERS reports are submitted voluntarily, can be duplicated or incomplete, and do not establish causation.

### Limitations

- Only the six datasets listed above are supported. Device adverse events (MAUDE), food and animal adverse events, NDC and Drugs@FDA are not included yet.
- Output columns differ by dataset after the six common columns.
- Free-text search matches whole words only. It does not do fuzzy matching.
- Without an API key, openFDA's 1,000-requests-per-day limit applies per IP. Very large exports (over about 1 million records a day) need your own key.
- openFDA data is not validated by the FDA and must not be used to make medical decisions. This Actor is not affiliated with or endorsed by the FDA.

### Legal and privacy

The data comes from the public openFDA API and is in the public domain. Adverse event reports are de-identified by the FDA. The Actor does not collect reporter or sender details, and it drops 510(k) contact-person names.

### Support and feedback

Found a bug, need another openFDA dataset or a new field? Open an issue on the **Issues** tab.

# Actor input Schema

## `dataset` (type: `string`):

Which official openFDA dataset to export: drug, food or medical device recalls (enforcement reports), FAERS drug adverse events, device 510(k) clearances or drug labels (SPL). Every row has the common columns dataset, recordId, date, company, title and url, followed by dataset-specific columns.

## `searchText` (type: `string`):

Optional. Free-text search across all fields, e.g. metformin, listeria, insulin pump. Whole words match. Also accepts openFDA query syntax for precise filters, e.g. reason_for_recall:undeclared or patient.drug.medicinalproduct:"OZEMPIC". Leave empty for all records.

## `firmNames` (type: `array`):

Optional. Only records from these companies (any of them). Matches the recalling firm for recalls, the applicant for 510(k), and the manufacturer name for adverse events and labels. Words inside the name match, so Pfizer finds "Pfizer Laboratories Div Pfizer Inc". Example: Pfizer, Medtronic.

## `dateFrom` (type: `string`):

Optional. Only records on or after this date (YYYY-MM-DD). Uses the report date for recalls, the FDA receive date for adverse events, the decision date for 510(k) and the effective date for labels.

## `dateTo` (type: `string`):

Optional. Only records on or before this date (YYYY-MM-DD). Same date field as "Date from".

## `classifications` (type: `array`):

Optional, recall datasets only. Class I = dangerous (reasonable probability of serious harm or death), Class II = may cause temporary or reversible harm, Class III = unlikely to cause harm. Leave empty for all classes. Ignored for adverse events, 510(k) and labels.

## `sortOrder` (type: `string`):

Order of records by the dataset's date field. Newest first is best for monitoring; oldest first for building a full history.

## `maxItems` (type: `integer`):

Stop after this many records. You pay per record, so start small (20 records = $0.03) and raise it for full exports. There is no upper cap from openFDA: pagination continues past the 25,000-record skip limit.

## `apiKey` (type: `string`):

Optional free key from open.fda.gov/apis/authentication. Not needed for normal use. Without a key openFDA allows 240 requests per minute and 1,000 per day per IP; a key raises the daily limit to 120,000. Each request returns up to 1,000 records.

## Actor input object example

```json
{
  "dataset": "drug/enforcement",
  "searchText": "",
  "firmNames": [],
  "dateFrom": "",
  "dateTo": "",
  "classifications": [],
  "sortOrder": "desc",
  "maxItems": 20
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "dataset": "drug/enforcement",
    "searchText": "",
    "firmNames": [],
    "dateFrom": "",
    "dateTo": "",
    "classifications": [],
    "sortOrder": "desc",
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("deanko/openfda-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "dataset": "drug/enforcement",
    "searchText": "",
    "firmNames": [],
    "dateFrom": "",
    "dateTo": "",
    "classifications": [],
    "sortOrder": "desc",
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("deanko/openfda-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "dataset": "drug/enforcement",
  "searchText": "",
  "firmNames": [],
  "dateFrom": "",
  "dateTo": "",
  "classifications": [],
  "sortOrder": "desc",
  "maxItems": 20
}' |
apify call deanko/openfda-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deanko/openfda-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/IB8ptJyyahcATrCmV/builds/toyU1ApBlqWegWNnR/openapi.json
