# openFDA recalls monitor (drug, device, food) (`keepable_injera/fda-recalls-monitor`) Actor

Monitor drug, device, and food enforcement recalls from openFDA.

- **URL**: https://apify.com/keepable_injera/fda-recalls-monitor.md
- **Developed by:** [Chee Chiaw Lim](https://apify.com/keepable_injera) (community)
- **Categories:** Business, Automation
- **Stats:** 2 total users, 1 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 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

## openFDA recalls monitor (drug, device, food)

This Apify Actor returns drug, device, and food enforcement recalls reported within a UTC date window. It supports classification filters and a recalling firm watchlist.

### Coverage and source

The Actor queries the [openFDA enforcement APIs](https://open.fda.gov/apis/). Teams can use the output to track recent reports and recurring firms in one dataset. Report date determines inclusion, rather than recall initiation date.

The endpoints are `https://api.fda.gov/{drug,device,food}/enforcement.json`. No API key or proxy is required.

### Input

| Field | Default | Meaning |
| --- | --- | --- |
| `productTypes` | `["drug", "device", "food"]` | Product types to query, in this order. |
| `sinceDays` | 30 | Integer from 1 to 3650. Reports from midnight UTC N days ago through today UTC, inclusive. |
| `classifications` | `[]` | Classifications such as Class I or Class II. Empty selects all. |
| `firmWatchlist` | `[]` | Recalling firm names. Empty selects all firms. |
| `onlyNew` | false | Skip recall IDs already emitted to the state store. |
| `maxItems` | 100 | Integer from 1 to 10000. Maximum source records examined across all types before duplicate and onlyNew filtering. |
| `stateStoreName` | `fda-recalls-monitor-state` | Named store for cross-run history. |

Values within each filter are alternatives. Classification and firm filters combine with AND. Firm queries use openFDA quoted phrase matching. `matchedFirm` is the first watchlist entry found as a case-insensitive substring in the returned firm name, or null if none matches literally.

Results sort by report date descending within each product type. Types run sequentially, so an earlier type can consume the entire maxItems budget. The output is not globally sorted across types.

### Output

Each record contains recall identifiers, classification, status, firm and location, report and recall dates, distribution, product details, recall reason, and source attribution. Missing fields are null. Dates use YYYY-MM-DD.

Example from the supplied drug fixture follows.

```json
{
  "recallNumber": "D-0041-2027",
  "productType": "drug",
  "classification": "Class II",
  "status": "Ongoing",
  "recallingFirm": "Houston Rx LLC",
  "city": "Houston",
  "state": "TX",
  "country": "United States",
  "reportDate": "2026-10-21",
  "recallInitiationDate": "2026-09-25",
  "centerClassificationDate": "2026-10-13",
  "voluntaryMandated": "Voluntary: Firm initiated",
  "initialFirmNotification": "Two or more of the following: Email, Fax, Letter, Press Release, Telephone, Visit",
  "distributionPattern": "Nationwide in the USA.",
  "productDescription": "Sermorelin, 1 mg/mL, 10 ml vial, Sterile injectable solution, RX Only, Houston RX LLC, 15430 Vantage Parkway E STE 220, Houston, TX 77032.",
  "productQuantity": "7 vials",
  "reasonForRecall": "Lack of process control",
  "codeInfo": "Lot# SER-081726-1-10, Beyond Use Date: 10/01/2026.",
  "eventId": "99990",
  "matchedFirm": null,
  "source": "U.S. Food and Drug Administration (openFDA)",
  "sourceUrl": "https://api.fda.gov/drug/enforcement.json?search=recall_number:%22D-0041-2027%22",
  "licence": "CC0 1.0 (https://open.fda.gov/terms/)",
  "disclaimer": "Data provided by the U.S. Food and Drug Administration (https://open.fda.gov). Do not rely on openFDA to make decisions regarding medical care. Not endorsed by FDA.",
  "generatedId": "drug:D-0041-2027",
  "changeType": "new",
  "scrapedAt": "2026-10-30T00:00:00.000Z"
}
```

The dataset has a table view of key fields. `RUN_SUMMARY` in the default key-value store records timestamps, fetched, eligible, and pushed counts, charge-limit status, and any error.

### State and pricing

Pricing is $0.002 per result, no start fee. Each emitted record uses the `result` billing event. Records skipped by onlyNew do not incur a result charge. Platform compute and storage charges may apply separately.

The state store keeps generated IDs under `SEEN_RECALL_IDS`. Only successful pushes enter the set. An unseen ID gets changeType new; a stored ID gets seen. With onlyNew false, seen records appear again. This tracks recall identity, not changes to existing recalls. Use a separate named store for independent monitors. If named-store access returns 403, the Actor warns, uses the default store, and disables onlyNew for that run.

### Licence and disclaimer

Source is U.S. Food and Drug Administration (openFDA). Licence is CC0 1.0 (https://open.fda.gov/terms/). Each record includes an API query URL for its recall number.

Data provided by the U.S. Food and Drug Administration (https://open.fda.gov). Do not rely on openFDA to make decisions regarding medical care. Not endorsed by FDA.

### Limits

The API allows 1,000 requests per day per IP without a key and 240 requests per minute. The Actor waits at least one second between request starts and retries HTTP 429 and 5xx responses up to three times. Other failures stop the run and appear in RUN_SUMMARY. HTTP 404 with NOT_FOUND means zero results.

Pages contain up to 100 records. Paging stops at the reported total, maxItems, an empty page, or the openFDA maximum skip of 25000. Duplicate and onlyNew filtering can reduce the emitted count. The API can change during paging, and equal report dates can affect page boundaries. Source reports can lag events or contain omissions.

### Local verification

Run `npm test` for offline tests. Run the real Actor entry point with supplied fixtures and all network calls blocked using a fresh local storage directory.

```sh
CRAWLEE_STORAGE_DIR=/tmp/fda-check APIFY_LOCAL_STORAGE_DIR=/tmp/fda-check NODE_OPTIONS="--import ./scripts/offline-fixture.js" npm start
node scripts/verify-output.js /tmp/fda-check
```

The preload supplies saved API responses and does not simulate date or firm filtering. This verifies execution and output, not live API availability.

# Actor input Schema

## `productTypes` (type: `array`):

Product types.

## `sinceDays` (type: `integer`):

Days since report.

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

Examples: Class I, Class II, Class III. Empty selects all.

## `firmWatchlist` (type: `array`):

Recalling firm names. Empty selects all firms.

## `onlyNew` (type: `boolean`):

Only new recalls.

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

Maximum records examined across all types before duplicate and onlyNew filtering.

## `stateStoreName` (type: `string`):

State store name.

## Actor input object example

```json
{
  "productTypes": [
    "drug",
    "device",
    "food"
  ],
  "sinceDays": 30,
  "classifications": [],
  "firmWatchlist": [],
  "onlyNew": false,
  "maxItems": 100,
  "stateStoreName": "fda-recalls-monitor-state"
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "productTypes": [
        "drug",
        "device",
        "food"
    ],
    "sinceDays": 30,
    "classifications": [],
    "firmWatchlist": [],
    "onlyNew": false,
    "maxItems": 100,
    "stateStoreName": "fda-recalls-monitor-state"
};

// Run the Actor and wait for it to finish
const run = await client.actor("keepable_injera/fda-recalls-monitor").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 = {
    "productTypes": [
        "drug",
        "device",
        "food",
    ],
    "sinceDays": 30,
    "classifications": [],
    "firmWatchlist": [],
    "onlyNew": False,
    "maxItems": 100,
    "stateStoreName": "fda-recalls-monitor-state",
}

# Run the Actor and wait for it to finish
run = client.actor("keepable_injera/fda-recalls-monitor").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 '{
  "productTypes": [
    "drug",
    "device",
    "food"
  ],
  "sinceDays": 30,
  "classifications": [],
  "firmWatchlist": [],
  "onlyNew": false,
  "maxItems": 100,
  "stateStoreName": "fda-recalls-monitor-state"
}' |
apify call keepable_injera/fda-recalls-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,keepable_injera/fda-recalls-monitor"
        }
    }
}
```

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/887gvZch271EztH8z/builds/hatcjseeFGzLHDbx3/openapi.json
