# FDA Recalls Monitor – Food, Drug & Device Recalls API (`whitel1ght/fda-recalls`) Actor

Get FDA recalls and enforcement reports for food, drugs and medical devices from openFDA as clean JSON. Filter by recall class, firm, state and date, and monitor new recalls daily. Built for compliance teams and AI agents (MCP).

- **URL**: https://apify.com/whitel1ght/fda-recalls.md
- **Developed by:** [Dzmitry Mamyrau](https://apify.com/whitel1ght) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 recalls

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 Monitor – Food, Drug & Device Recalls API

**FDA Recalls Monitor** returns FDA recalls (enforcement report records) for **food, drugs and medical devices** as clean, normalized JSON. Data comes straight from the official [openFDA](https://open.fda.gov/apis/) enforcement API. Filter by keyword, recalling firm, recall class (Class I, II, III), status, state, country and date range, or switch on **only new** mode to monitor for recalls you have not seen before. No FDA API key or code required.

### What does FDA Recalls Monitor do?

- Searches the openFDA food, drug and device enforcement endpoints and returns one dataset item per recall.
- Normalizes fields: ISO dates (`YYYY-MM-DD`), `null` for missing values, and a direct link to the FDA Enforcement Report page.
- Remembers what it has already returned for a given `monitorId`, so scheduled runs deliver only new recalls.
- Handles openFDA paging limits and rate limits (retries with backoff) for you.

### Who is it for?

- **Compliance and regulatory teams** tracking recalls that affect their products or suppliers.
- **Quality assurance and food safety teams** watching for allergen, Listeria, Salmonella or contamination recalls.
- **Supply chain and procurement** monitoring supplier and manufacturer recall history.
- **E-commerce and retail** sellers who must pull recalled products fast.
- **Insurers, researchers and journalists** analysing recall trends.
- **AI agents and LLM workflows** that need structured recall data through the Apify MCP server.

### Features

- Food, drug and medical device recalls in one actor.
- Filters: `searchText`, `recallingFirm`, `classification`, `status`, `state`, `country`, `dateFrom`, `dateTo`.
- Choose the date used for filtering and sorting: FDA publication date (`reportDate`) or `recallInitiationDate`.
- Only-new monitoring with `onlyNew` and `monitorId`.
- Optional `includeRaw` adds the unmodified openFDA record.
- Results are newest first within each category.
- Export as JSON, CSV, Excel, XML or HTML, or read through the Apify API.

### Input

| Field | Type | Description |
| --- | --- | --- |
| `categories` | array | `food`, `drug`, `device`. Default: `["food"]`. |
| `searchText` | string | Keywords matched against product description and reason for recall. All words must appear. |
| `recallingFirm` | string | Company that initiated the recall (phrase match, case-insensitive). |
| `classification` | string | `Class I` (most severe), `Class II`, `Class III`. Empty for all. |
| `status` | string | `Ongoing`, `Completed`, `Terminated`, `Pending`. Empty for all. |
| `state` | string | Two-letter US state code of the recalling firm, e.g. `CA`. |
| `country` | string | Country name of the recalling firm as used by FDA, e.g. `Canada`. |
| `dateFrom` | string | Earliest date, `YYYY-MM-DD`, inclusive. |
| `dateTo` | string | Latest date, `YYYY-MM-DD`, inclusive. |
| `dateField` | string | `reportDate` (default) or `recallInitiationDate`. |
| `onlyNew` | boolean | Skip recalls already returned for the same `monitorId`. Default `false`. |
| `monitorId` | string | Name of the monitoring job. Required when `onlyNew` is true. |
| `maxResults` | integer | Maximum recalls to return. Default `10`. Also caps cost. |
| `includeRaw` | boolean | Add the raw openFDA record in `raw`. Default `false`. |
| `openFdaApiKey` | string | Optional free openFDA key for very large runs. |

Example input:

```json
{
    "categories": ["food"],
    "searchText": "undeclared peanut",
    "classification": "Class I",
    "dateFrom": "2025-01-01",
    "maxResults": 50
}
```

### Output

Each dataset item is one recall. Example:

```json
{
    "recallNumber": "F-0276-2017",
    "category": "food",
    "classification": "Class II",
    "status": "Completed",
    "reportDate": "2016-12-21",
    "recallInitiationDate": "2016-11-30",
    "recallingFirm": "Example Foods Inc.",
    "productDescription": "Roasted peanut butter, 16 oz jar",
    "reasonForRecall": "Product may contain undeclared milk.",
    "codeInfo": "Lot 1234, best by 2017-11-01",
    "distributionPattern": "Nationwide",
    "quantity": "1,200 jars",
    "city": "Austin",
    "state": "TX",
    "country": "United States",
    "voluntaryMandated": "Voluntary: Firm initiated",
    "eventId": "75000",
    "sourceUrl": "https://www.accessdata.fda.gov/scripts/ires/index.cfm?Event=75000"
}
```

The default dataset has a "Recalls" table view. Values above are illustrative.

### Pricing

**$5 per 1,000 recalls** ($0.005 per result), plus a tiny Apify start fee per run. You only pay for results returned, and `maxResults` caps your cost. Apify's free plan credits cover many test runs.

### How to use

#### Apify Console

Open the actor, keep the default input (10 newest food recalls) and click **Start**. Results appear in the dataset.

#### API

```bash
curl -X POST "https://api.apify.com/v2/acts/whitel1ght~fda-recalls/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"categories":["food","drug","device"],"classification":"Class I","maxResults":20}'
```

#### Monitor new recalls daily

Create a schedule in Apify Console and use input like:

```json
{
    "categories": ["food"],
    "searchText": "listeria",
    "onlyNew": true,
    "monitorId": "listeria-watch",
    "maxResults": 100
}
```

The first run returns everything that matches (the baseline). Each later run returns only new recalls. Add a webhook or an integration (Slack, email, Zapier, Make) to get alerted. Use a different `monitorId` for each set of filters.

#### AI agents and MCP

Use this actor from Claude, Cursor or any MCP client through the [Apify MCP server](https://mcp.apify.com): add `whitel1ght/fda-recalls` as a tool, then ask for example "Any new Class I drug recalls this week?". The typed input and output make it easy for agents to call and parse.

### FAQ

**Where does the data come from?** The official openFDA enforcement API (`food`, `drug`, `device` endpoints), which publishes FDA Enforcement Report data.

**How often is it updated?** FDA publishes Enforcement Reports weekly, and openFDA refreshes its data on a similar cadence. Schedule the actor daily or weekly.

**Are there limits?** openFDA allows 1,000 records per request and 25,000 skipped records. The actor splits large date ranges automatically. A single day with more than 25,000 matches is truncated. Without a key openFDA allows about 240 requests per minute per IP, which is enough for normal use.

**Do I need an openFDA API key?** No. A free key in `openFdaApiKey` only helps very large runs.

**Is this affiliated with the FDA?** No. It is an independent tool that uses public openFDA data and is not affiliated with or endorsed by the FDA. Do not use it for medical decisions; verify against the FDA source link in `sourceUrl`.

**Can I get every category at once?** Yes, set `categories` to `["food","drug","device"]`. Results are streamed per category, each newest first.

### Changelog

- **0.1** – Food, drug and device recalls, filters, only-new monitoring, raw record option, pay-per-result pricing.

# Actor input Schema

## `categories` (type: `array`):

Which FDA product categories to search: "food" (incl. dietary supplements, animal feed), "drug" (human and animal drugs), "device" (medical devices). Default is food only; add drug and device to search all three. Results are streamed per category in the order food, drug, device, each category newest first (not merged into one global order).

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

Free-text keywords matched against the product description and reason for recall. Multiple words must ALL appear (in either field), e.g. "listeria cheese" or "undeclared peanut". Leave empty for no keyword filter.

## `recallingFirm` (type: `string`):

Company that initiated the recall, matched as a phrase against the firm name (case-insensitive, word-based), e.g. "Pfizer" or "Trader Joe's".

## `classification` (type: `string`):

FDA recall class. Class I = reasonable probability of serious harm or death (most severe); Class II = temporary or reversible harm; Class III = unlikely to cause harm. Leave empty for all classes.

## `status` (type: `string`):

Current status of the recall: Ongoing, Completed, Terminated or Pending. Leave empty for all statuses.

## `state` (type: `string`):

Two-letter US state code of the recalling firm, e.g. "CA" or "NY". This is where the firm is located, not where the product was distributed.

## `country` (type: `string`):

Full country name of the recalling firm as used by FDA, e.g. "United States", "Canada", "Germany".

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

Earliest date to include, format YYYY-MM-DD (inclusive). Applies to the field chosen in "Date field". Leave empty for no lower bound.

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

Latest date to include, format YYYY-MM-DD (inclusive). Applies to the field chosen in "Date field". Leave empty for no upper bound.

## `dateField` (type: `string`):

Which date the date range and sort order use. "reportDate" = when FDA published the recall in its weekly Enforcement Report (best for monitoring new items). "recallInitiationDate" = when the firm started the recall.

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

If true, skip recalls already returned by a previous run that used the same Monitor ID, so you only receive new ones. Requires "Monitor ID". The first run returns everything that matches (up to Max results) and becomes the baseline.

## `monitorId` (type: `string`):

Short name for this monitoring job, e.g. "peanut-allergen-watch". Runs sharing the same Monitor ID share one list of already-seen recalls, so use a different ID for each distinct set of filters. Required when "Only new" is true.

## `maxResults` (type: `integer`):

Maximum number of recalls to return in total (across all categories, and counting only new items when "Only new" is on). Categories are read in order food, drug, device, each newest first, so a low limit may return only the first category's recalls. You are charged per returned item, so this also caps cost.

## `includeRaw` (type: `boolean`):

If true, each item also contains a "raw" field with the unmodified openFDA record (extra fields such as address, postal code, termination date, center classification date).

## `openFdaApiKey` (type: `string`):

Optional free key from https://open.fda.gov/apis/authentication/ . Without one openFDA allows about 240 requests/minute per IP, which is enough for normal use; a key raises the daily limit for very large runs.

## Actor input object example

```json
{
  "categories": [
    "food"
  ],
  "dateField": "reportDate",
  "onlyNew": false,
  "maxResults": 10,
  "includeRaw": false
}
```

# Actor output Schema

## `recalls` (type: `string`):

FDA recalls (overview view of the default dataset).

# 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 = {
    "categories": [
        "food"
    ],
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("whitel1ght/fda-recalls").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 = {
    "categories": ["food"],
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("whitel1ght/fda-recalls").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 '{
  "categories": [
    "food"
  ],
  "maxResults": 10
}' |
apify call whitel1ght/fda-recalls --silent --output-dataset

```

## MCP server setup

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

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/FwafPo9Tl7mSNqCsH/builds/jXxPRZEwyIIn3nufP/openapi.json
