# openFDA Drug, Device & Recall Data (`grit-77/openfda`) Actor

openFDA data scraper for drug labels, adverse-event count buckets, device recalls and food enforcement records. Export JSON with source links and diagnostics for failed or empty queries.

- **URL**: https://apify.com/grit-77/openfda.md
- **Developed by:** [Grit](https://apify.com/grit-77) (community)
- **Categories:** AI, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 openfda records

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 Drug, Device & Recall Data

openFDA scraper returns drug labels, adverse event count buckets, device recalls, and food enforcement records from the public openFDA API. openFDA API queries return source links, dates, original records, and diagnostics for failed or empty queries.

### Input example

```json
{
  "endpoint": "drug_labels",
  "search": "openfda.brand_name:\"ADVIL\"",
  "maxItems": 20
}
```

The input keys and allowed values follow [input_schema.json](.actor/input_schema.json).

### Output fields

| Field | Type | Example or excerpt from sample |
|---|---|---|
| `endpoint` | string | `"drug_labels"` |
| `status` | string | `"ok"` |
| `sourceUrl` | string | `"https://api.fda.gov/device/recall.json?limit=2&skip=0"` |
| `sourceLastUpdated` | string | `"2026-10-02"` |
| `fetchedAt` | string | `"2026-10-05T12:20:00.231984+00:00"` |
| `recordId` | string | `"44e71018-211d-caf8-e063-6394a90a06c9"` |
| `title` | string | `"Advil Dual Action with Acetaminophen, Travel BASIX"` |
| `recordDate` | string or null | `"2025-12-01"` |
| `term` | string or null | `"OFF LABEL USE"` |
| `count` | integer or null | `84264` |
| `recallingFirm` | string or null | `"Sedecal USA, Inc"` |
| `recallStatus` | string or null | `"Terminated"` |
| `classification` | string or null | `"Class II"` |
| `reasonForRecall` | string or null | `"Wound dressing failed to meet sterility specifications."` |
| `brandNames` | array | `["JUNIOR STRENGTH ADVIL"]` |
| `genericNames` | array | `["IBUPROFEN, ACETAMINOPHEN TABLET, FILM COATED"]` |
| `manufacturerNames` | array | `["Lil' Drug Store Products, Inc."]` |
| `data` | object | `{"term": "OFF LABEL USE", "count": 84264}` |
| `error` | string or null | `null` |
| `queryIndex` | integer | `0` |
| `search` | string | `"openfda.brand_name:\"ADVIL\""` |
| `countField` | string or null | `"patient.reaction.reactionmeddrapt.exact"` |

### Output example

Trimmed from [sample_output.json](sample_output.json); omitted fields remain available in the dataset.

```json
{
  "endpoint": "drug_labels",
  "status": "ok",
  "sourceUrl": "https://api.fda.gov/drug/label.json?search=openfda.brand_name%3A%22ADVIL%22&limit=2&skip=0",
  "sourceLastUpdated": "2026-10-02",
  "fetchedAt": "2026-10-05T12:20:00.231984+00:00",
  "recordId": "44e71018-211d-caf8-e063-6394a90a06c9",
  "title": "Advil Dual Action with Acetaminophen, Travel BASIX",
  "error": null
}
```

### Available endpoints

| Input endpoint | Official source | What one result represents |
|---|---|---|
| `drug_labels` | [Drug labeling](https://open.fda.gov/apis/drug/label/) | One Structured Product Label record |
| `adverse_event_counts` | [Drug adverse events](https://open.fda.gov/apis/drug/event/) | One aggregate field value or date bucket with its report count |
| `device_recalls` | [Medical device recalls](https://open.fda.gov/apis/device/recall/) | One medical device recall record |
| `food_enforcement` | [Food enforcement](https://open.fda.gov/apis/food/enforcement/) | One food enforcement recall record |

`device_recalls` uses `/device/recall.json`; food enforcement uses `/food/enforcement.json`. Individual drug adverse event reports are not exported.

### Limits and data interpretation

- Count requests return at most 1,000 buckets in one request. Count buckets are not paginated, and the API's most frequent buckets are not a complete frequency distribution when more terms exist.
- Record queries are capped at 25,000 rows by this actor. [openFDA pagination](https://open.fda.gov/apis/paging/) has a maximum skip of 25,000; bulk downloads and search-after are outside this actor's scope. Split a broad query by date or narrow its filters.
- Requests are paced 0.3 seconds apart within one run. Multiple actor runs may share an IP or API key, so this does not guarantee avoidance of rate limits. [Published openFDA limits](https://open.fda.gov/apis/authentication/) are 240 requests/minute and 1,000/day without a key; with a key the daily limit is 120,000 requests.
- HTTP 408, 429, 500, 502, 503, 504 and transport failures are retried. An exhausted request becomes a query error row; records already emitted are preserved. A malformed result becomes an item error row. Invalid global settings fail the run with a clear message.
- Upstream records may change while pages are fetched; this is not a historical snapshot service. `recordDate` is the label effective date or recall initiation date, not a fetch date. Device recall classifications may be absent.
- Drug adverse event counts are reported occurrences, not incidence estimates or evidence that a drug caused an outcome. FDA's [source disclaimer](https://open.fda.gov/apis/) says not to rely on openFDA for medical care decisions. The actor does not validate clinical conclusions.
- Hosted billing and the Docker image have not been exercised in this local delivery. SDK charge calls and stop behavior are covered by integration tests; actual event prices require the owner's Store configuration.

### FAQ

**What are the output limits?** `maxItems` caps records and diagnostics; the API can return no matches or reject a search expression.

**Do I need a proxy or login?** Public HTTPS endpoints need no login or proxy. An openFDA API key is optional.

**How is this priced?** Proposed pay per event: $0.001 for each successful `record` row. Empty and error diagnostics are uncharged. These are proposals in [PRICING.md](PRICING.md); the owner must configure Store billing.

**How are rate limits handled?** The code spaces requests 0.3 seconds apart across the run and retries transient errors with backoff and `Retry-After`. `concurrency` controls simultaneous queries.

**How fresh is the data?** Official openFDA data may change after a run. `fetchedAt` records collection time; `sourceLastUpdated` reflects the source dataset when available. Adverse events are aggregate counts, not individual reports.

### Use with AI agents / MCP

After the actor is published, connect your agent to the [Apify MCP server](https://docs.apify.com/integrations/mcp) with an Apify API token. Use `call-actor` with the published actor identifier and the JSON input shown above. Read the returned dataset with `get-dataset-items`; the run response itself contains status and storage IDs, not the result rows. Alternatively, use the [Apify API](https://docs.apify.com/api/v2) to start a run and read its default dataset. Keep the actor input bounded with `maxItems`, and inspect source links and any error fields before using results in an answer.

# Actor input Schema

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

Single-query endpoint. Ignored when queries is provided. Adverse events are aggregate counts only.

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

Official openFDA search expression, such as openfda.brand_name:"ADVIL". Empty means no search filter. Ignored when queries is provided.

## `countField` (type: `string`):

For adverse_event_counts only. Blank defaults to patient.reaction.reactionmeddrapt.exact. Use receivedate:month for date buckets.

## `sort` (type: `string`):

Optional field:asc or field:desc. Example: effective_time:desc for labels. Not supported in count mode.

## `queries` (type: `array`):

Optional array of 1 to 100 objects overriding endpoint/search/countField/sort. Each object may include limit. Query errors are isolated; repeated identical queries are removed.

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

Global cap including successful records and error/empty diagnostics. With concurrent queries, first-ready results fill the cap.

## `pageSize` (type: `integer`):

Page size for labels and recalls. Count mode makes one request for at most 1000 buckets.

## `concurrency` (type: `integer`):

Maximum simultaneous queries. Requests are paced 0.3 seconds apart across this run.

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

Optional personal openFDA API key. Sent through HTTPS Basic Auth rather than included in source URLs. Do not put the key into search text.

## `maxRetries` (type: `integer`):

Retries for network failures, HTTP 408, 429, 500, 502, 503 and 504. Other errors become diagnostics.

## `retryDelaySecs` (type: `number`):

Exponential backoff base. Retry-After seconds are respected, with each wait capped at 60 seconds.

## `requestTimeoutSecs` (type: `number`):

Timeout for each HTTP request attempt.

## Actor input object example

```json
{
  "endpoint": "drug_labels",
  "search": "",
  "countField": "",
  "sort": "",
  "maxItems": 100,
  "pageSize": 100,
  "concurrency": 2,
  "maxRetries": 3,
  "retryDelaySecs": 1,
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

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

Open the default dataset containing normalized FDA records, aggregate count buckets and query diagnostics.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("grit-77/openfda").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("grit-77/openfda").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 '{}' |
apify call grit-77/openfda --silent --output-dataset

```

## MCP server setup

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

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/jTZnBrHTIpK4xieun/builds/rto1lgh5y1S9vErez/openapi.json
