# openFDA Scraper - Drug, Food & Device Recalls + Events (`gio21/openfda-scraper`) Actor

Queries all 6 openFDA endpoints (drug labels, drug/food/device recalls, drug/device adverse events) from one actor, no API key needed. Returns recall reasons, classifications, ingredients and MedWatch reports. $0.001 per record.

- **URL**: https://apify.com/gio21/openfda-scraper.md
- **Developed by:** [Gio](https://apify.com/gio21) (community)
- **Categories:** E-commerce, Developer tools, AI
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 record scrapeds

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

## openFDA Scraper - Drug, Food & Device Recalls + Events

Query all six of the FDA's open data endpoints from one Actor, no API key required: drug package inserts, drug adverse events, drug recalls, food recalls, device recalls and device adverse events. One input field switches endpoints, so you never need six different scrapers to cover FDA data.

### Why use this scraper

The FDA publishes openFDA, a genuinely open, free, government-run API, but it is six separate endpoints with six slightly different JSON shapes, pagination rules and search syntaxes. Building a pipeline that touches more than one of them (say, drug recalls and drug adverse events together) means writing and maintaining six integrations, handling the FDA's own rate limits, and re-learning openFDA's search query syntax every time you add an endpoint.

This Actor wraps all six behind one input schema and one consistent, flattened output shape. You pick the endpoint, optionally add an openFDA search expression, and get back structured records you can drop straight into a spreadsheet, a compliance dashboard or a safety-monitoring pipeline.

### Overview

- **Input:** which of the 6 openFDA endpoints to query, an optional openFDA search expression, how many records you want, and an optional free openFDA API key for higher rate limits.
- **Output:** one flattened JSON record per FDA entry, with endpoint-specific fields (recall details for the enforcement endpoints, adverse-event details for the event endpoints, label details for drug-label) plus common brand/generic/manufacturer/substance fields extracted from every record.
- **Speed:** the Actor pages through openFDA at 100 records per request (1,000 with a free API key), so a few hundred records typically return in well under a minute.
- **Cost:** pay per record actually returned; errors are never charged.

### Supported inputs

Six endpoints, selected with the `endpoint` field:

- **`drug-label`** - drug package inserts: indications, dosage and administration, warnings, purpose, route, product type, pharmacologic class.
- **`drug-event`** - adverse drug event reports (MedWatch): which drugs were involved, which reactions were reported, report type, country, serious flag.
- **`drug-enforcement`** - drug recalls: recall number, status, classification, reason, firm, distribution, dates.
- **`food-enforcement`** - food recalls: the same recall shape as drug-enforcement, applied to food products (allergen mislabeling, contamination, undeclared ingredients).
- **`device-enforcement`** - medical device recalls: same recall shape, applied to devices.
- **`device-event`** - medical device adverse events: report number, device class, brand name, manufacturer, product problem.

Every endpoint also accepts an optional `search` field using openFDA's own search syntax, e.g. `openfda.brand_name:tylenol` or a date range like `recall_initiation_date:[20240101+TO+20241231]`; leave it empty to pull everything available for that endpoint.

### Use cases

- **Pharma regulatory monitoring** - track new drug recalls or label changes for a portfolio of products or competitors, feed a compliance alert system.
- **Patient safety research** - pull adverse-event reports for a specific drug or device to study reaction patterns, severity trends or reporting volume over time.
- **Food safety compliance** - monitor food-enforcement recalls for allergen and contamination issues relevant to a retailer's or distributor's product catalog.
- **Healthtech product research** - device manufacturers and healthtech startups can check a competitor's or a component's recall and adverse-event history before integration decisions.
- **Insurance and legal research** - litigation and underwriting teams pulling historical recall and adverse-event records as supporting evidence or risk data.

### How it works

The Actor calls the matching openFDA REST endpoint directly with your `search` expression (when given) and pages through results using openFDA's own `skip`/`limit` pagination, stopping once it reaches your `maxItems`, runs out of records, or hits openFDA's own result-window ceiling. Each raw openFDA record is then flattened into a consistent shape: common fields (brand, generic, manufacturer, substance, all pulled from the `openfda` sub-object every endpoint returns) are extracted the same way regardless of which of the six endpoints you queried, and endpoint-specific fields are added on top of that common base.

### Input configuration

**Example 1 - Food recalls, no search filter:**

```json
{
  "endpoint": "food-enforcement",
  "maxItems": 100
}
```

**Example 2 - Drug recalls for a specific brand:**

```json
{
  "endpoint": "drug-enforcement",
  "search": "openfda.brand_name:tylenol",
  "maxItems": 50
}
```

**Example 3 - Device adverse events with a free openFDA API key for a bigger pull:**

```json
{
  "endpoint": "device-event",
  "maxItems": 2000,
  "apiKey": "YOUR_OPENFDA_KEY"
}
```

`endpoint` is required; `search`, `maxItems` (default 500, max 25,000) and `apiKey` are optional.

### Output sample

Real output from a live run against `food-enforcement`:

```json
{
  "endpoint": "food-enforcement",
  "brand": null,
  "generic": null,
  "manufacturer": null,
  "substance": null,
  "recallNumber": "F-0276-2017",
  "status": "Terminated",
  "classification": "Class II",
  "productDescription": "CytoDetox, Hydrolyzed Clinoptilolite Fragments, 1 oz./30 mL, OTC Non-Sterile. Dietary supplement.",
  "reason": "Recall initiated as a precautionary measure due to potential risk of product contamination with Burkholderia cepacia.",
  "date": "20160808",
  "terminationDate": "20240923",
  "distribution": "FL, MI, MS, and OH.",
  "quantity": "1,990 bottles",
  "codeInfo": "UPC No. 632687615989; Lot No. 30661601, Exp. Date 05/2018.",
  "recallingFirm": "Pharmatech LLC",
  "voluntaryMandated": "Voluntary: Firm initiated",
  "country": "United States",
  "state": "FL",
  "city": "Davie"
}
```

And a second record from the same run:

```json
{
  "endpoint": "food-enforcement",
  "recallNumber": "F-0609-2015",
  "status": "Terminated",
  "classification": "Class I",
  "productDescription": "Crema GuateLinda (Guatemalan Style Cream) in individually soft poly/plastic bags...",
  "reason": "Virginia State (VDACS) found Listeria monocytogenes in Lacteos Santa Martha Quesito Casero Fresh Curd...",
  "date": "20141010",
  "terminationDate": "20170328",
  "distribution": "FL, GA. NC, and TN",
  "recallingFirm": "Oasis Brands, Inc",
  "voluntaryMandated": "Voluntary: Firm initiated",
  "country": "United States",
  "state": "FL",
  "city": "Miami"
}
```

### Key output fields

| Field | Type | Description |
|---|---|---|
| `endpoint` | string | Which of the 6 openFDA endpoints this record came from. |
| `brand` / `generic` / `manufacturer` / `substance` | string | null | Common identity fields, extracted from `openfda.*` on every endpoint when present. |
| `recallNumber` | string | FDA recall identifier (enforcement endpoints only). |
| `status` | string | Recall status, e.g. `Ongoing`, `Terminated` (enforcement endpoints). |
| `classification` | string | FDA recall class (`Class I`, `II` or `III`), severity of the health risk (enforcement endpoints). |
| `reason` | string | FDA's own description of why the recall was issued (enforcement endpoints). |
| `recallingFirm` | string | Company that issued the recall (enforcement endpoints). |
| `distribution` | string | States/countries the recalled product reached (enforcement endpoints). |
| `safetyReportId` / `drugs` / `reactions` | string / array | MedWatch report id and the drugs/reactions it lists (`drug-event` only). |
| `deviceClass` / `brandName` / `productProblem` | string | Device classification and reported problem (`device-event` only). |
| `indicationsAndUsage` / `warnings` / `dosageAndAdministration` | string | Label text fields (`drug-label` only). |

### Pricing

Pay per record returned, no subscription:

| Event | Price |
|---|---|
| Actor start | $0.00005 (one-time per run) |
| Record scraped | $0.001 |

A 500-record run (the default) costs about $0.50; a 5,000-record run costs about $5.00.

### What makes this richer than the competition

Two other openFDA scrapers on the Apify Store, by real, verified numbers:

- **`scrapers_lat/openfda-food-recalls-scraper`** (3 users in the last 30 days) covers only `food-enforcement`, and its primary "result" event is priced from $0.008 to $0.01 per record depending on your Apify plan tier, roughly 8-10x this Actor's flat $0.001/record. This Actor covers food recalls plus five other endpoints (drug labels, drug/device recalls, drug/device adverse events) for a fraction of the price.
- **`hipersoft/openfda-scraper`** (3 users in the last 30 days) covers a similar endpoint range but charges per record scraped at $0.0008-$0.0016 depending on plan tier, plus a separate $0.0005 charge per API page fetched on top of that. This Actor's pricing is simpler: one flat $0.001 per record, with no separate per-page charge layered in.

### Notes & limitations

- Without a free openFDA API key, you are limited to 240 requests/minute and 1,000/day per IP, which openFDA enforces, not this Actor; pass your own key in `apiKey` to raise that ceiling.
- openFDA's own result window caps at `skip + limit <= 25000`; for a query with more matching records than that, narrow it with a tighter `search` expression (e.g. a date range) instead of raising `maxItems` further.
- Some records legitimately have no `brand`/`generic`/`manufacturer` value (the common fields above show `null` for several `food-enforcement` records in the sample output) because the FDA's own `openfda.*` enrichment only applies to drug products, not every enforcement record.
- This Actor returns openFDA's own fields as published; it does not independently verify a recall's current status or cross-reference it against any other agency's data.

### FAQ

**Do I need an openFDA account or API key?**
No. The Actor works with no key at all, limited to openFDA's public rate limit. A free key from open.fda.gov/apis/authentication raises that limit if you need higher volume.

**What's the difference between `drug-enforcement` and `drug-event`?**
`drug-enforcement` returns recalls (a product being pulled from the market); `drug-event` returns MedWatch adverse-event reports (a patient or provider reporting a reaction), which is a completely separate FDA dataset.

**Can I search by date range?**
Yes, using openFDA's own search syntax in the `search` field, e.g. `recall_initiation_date:[20240101+TO+20241231]` for `drug-enforcement` or `food-enforcement`.

**Why are some `brand`/`generic`/`manufacturer` fields `null`?**
Those come from the FDA's own `openfda` enrichment sub-object, which some records (particularly supplements and some enforcement records) do not have populated; that is the FDA's own data gap, not a scraping error.

**Can I monitor new recalls as they happen?**
Yes. Schedule the Actor daily on `drug-enforcement` or `food-enforcement` with no `search` filter (or a narrow one for your product category) and de-duplicate new runs against previous ones by `recallNumber`.

**Does this Actor cover vaccines or biologics recalls?**
openFDA's `drug-enforcement` endpoint includes some biologics recalls when the FDA itself classifies them there; there is no separate vaccine-specific endpoint on openFDA itself, so this Actor does not add one either.

### For AI Agents & LLM Apps

This Actor can be called directly from an LLM agent or application without writing a scraper integration by hand.

**Via MCP** (`mcp.apify.com`): add this Actor as a tool to any MCP-compatible client (Claude, Cursor, etc.) and call it with the same input shown above; the agent receives the dataset rows as its tool result.

**Via API**, using `run-sync-get-dataset-items` to get results back in the same request:

```
POST https://api.apify.com/v2/acts/gio21~openfda-scraper/run-sync-get-dataset-items?token=<YOUR_TOKEN>
Content-Type: application/json

{
  "endpoint": "drug-enforcement",
  "search": "openfda.brand_name:tylenol",
  "maxItems": 20
}
```

The response is the JSON array of scraped records, ready to feed straight into an agent's context, for example a compliance-monitoring agent that drafts a recall summary.

### Disclaimer

This Actor is an independent tool that queries the FDA's public openFDA API. It is not affiliated with, endorsed by, or sponsored by the U.S. Food and Drug Administration or any other U.S. government agency. All trademarks mentioned are the property of their respective owners.

### SEO Keywords

openFDA scraper, FDA recall scraper, drug recall API, food recall scraper, FDA adverse event scraper, MedWatch data scraper, drug label scraper, FDA device recall scraper, pharma compliance data.

# Actor input Schema

## `endpoint` (type: `string`):

Which openFDA endpoint: drug-label (drug package inserts), drug-event (adverse events), drug-enforcement (drug recalls), food-enforcement (food recalls), device-enforcement (device recalls), device-event (device adverse events).

## `search` (type: `string`):

openFDA search syntax (e.g. 'openfda.brand_name:tylenol', 'recall_initiation_date:\[20240101+TO+20241231]'). Empty = all.

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

Max records returned.

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

Get free key at open.fda.gov/apis/authentication. Default no key (limited to 240/min, 1000/day per IP).

## `debug` (type: `boolean`):

Verbose logging.

## Actor input object example

```json
{
  "endpoint": "drug-label",
  "search": "",
  "maxItems": 500,
  "apiKey": "",
  "debug": false
}
```

# Actor output Schema

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

Record per openFDA result; fields depend on endpoint.

# 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 = {
    "search": "",
    "maxItems": 500,
    "apiKey": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("gio21/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 = {
    "search": "",
    "maxItems": 500,
    "apiKey": "",
}

# Run the Actor and wait for it to finish
run = client.actor("gio21/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 '{
  "search": "",
  "maxItems": 500,
  "apiKey": ""
}' |
apify call gio21/openfda-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gio21/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/R0BXYN3iWeQEikFf7/builds/HJCNujPZgPkBh6JuX/openapi.json
