# US Product Recalls API: CPSC + FDA recalls (openFDA) (`opendata-feeds/us-recalls-feed`) Actor

Product recalls from CPSC and FDA (openFDA food, drug and medical-device enforcement reports) in one clean schema: firm, brand, hazard, FDA class, status, dates, states, UPC/NDC/model/lot. Keyword filters, only-new mode for daily monitoring. $2 per 1,000 recalls.

- **URL**: https://apify.com/opendata-feeds/us-recalls-feed.md
- **Developed by:** [OpenData Feeds](https://apify.com/opendata-feeds) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 recall delivereds

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

### US Product Recalls API: CPSC recalls + FDA recalls (openFDA) in one feed

Get every new US product recall from **CPSC** (consumer products) and **FDA** (food, drugs and medical devices, from openFDA enforcement reports) in **one clean schema**. Filter by keyword, FDA class and date, and schedule it daily to receive only recalls you haven't seen.

- **4 recall sources in one Actor:** CPSC consumer product recalls, FDA food recalls, FDA drug recalls and FDA medical device recalls.
- **Official government APIs only.** The CPSC Recalls API (SaferProducts.gov) and the openFDA enforcement API. No HTML scraping, no browser, no proxies, no API key needed. A 30-day pull of all four sources takes seconds.
- **One schema for all agencies:** recall ID, title, product, firm, brand, reason / hazard, FDA class (I, II, III), status, recall date, published date, quantity, distribution and the US states it reached, identifiers (UPC, model or catalog number, NDC, GTIN, lot codes) and the official link.
- **Only-new mode for monitoring.** Schedule it daily, and it returns (and charges for) only recalls it hasn't delivered before.
- **$2.00 per 1,000 recalls.** You pay only for recalls delivered. Recalls removed by your filters, or skipped as already seen, cost nothing.

#### Who uses it

- **Retailers and marketplaces** checking their catalog against new product recalls (match on UPC, model or brand).
- **Product-safety, quality and compliance teams** watching recalls in their category or for their suppliers.
- **Food, pharmacy and medical-device distributors** tracking FDA Class I recalls.
- **Analysts and journalists** studying recall trends by firm, product type or hazard.

#### Input

| Field | What it does |
|---|---|
| `sources` | Any of `cpsc`, `fda-food`, `fda-drug`, `fda-device`. All four by default. |
| `keywords` | Keep recalls whose title, product, firm, brand or reason contains any of these words (`peanut`, `listeria`, `stroller`, `insulin pump`). Empty keeps all. |
| `classifications` | Keep only FDA recalls of these classes, for example `["Class I"]` (the most serious). CPSC recalls have no class, so they are dropped when this is set. |
| `publishedWithinDays` | Recalls published in the last N days (1 to 3650). Default 30. |
| `maxItemsPerSource` | Cap per source, newest first. 0 = no cap. |
| `maxItems` | Cap in total. 0 = no cap. |
| `onlyNew` + `stateKey` | Return only recalls not delivered by an earlier run of the same monitor. |
| `openFdaApiKey` | Optional, and not needed for normal use. See Limits. |

Example: daily check for serious food recalls involving allergens.

```json
{
  "sources": ["fda-food"],
  "keywords": ["undeclared", "allergen", "peanut", "milk"],
  "classifications": ["Class I"],
  "publishedWithinDays": 14,
  "onlyNew": true,
  "stateKey": "food-allergen-watch"
}
```

#### Output

One item per recall. Example (fictional firm and product):

```json
{
  "source": "fda-food",
  "agency": "FDA",
  "recallId": "F-0123-2027",
  "title": "Northwind Pantry Crunchy Trail Mix, 12 oz bag, UPC 0 12345 67890 5",
  "product": "Northwind Pantry Crunchy Trail Mix, 12 oz bag, UPC 0 12345 67890 5",
  "productType": "Food",
  "description": null,
  "firm": "Northwind Pantry Foods LLC",
  "brand": null,
  "reason": "Undeclared peanuts",
  "classification": "Class I",
  "status": "Ongoing",
  "recallDate": "2026-10-02",
  "publishedDate": "2026-10-21",
  "quantity": "4,800 bags",
  "distribution": "OR, WA, ID and online",
  "states": ["ID", "OR", "WA"],
  "manufacturedIn": [],
  "firmLocation": "Springfield, OR, United States",
  "identifiers": {
    "upc": ["012345678905"],
    "model": [],
    "ndc": [],
    "gtin": [],
    "lotCodes": "Lot 26-274A, best by 04/2027"
  },
  "remedy": null,
  "incidents": null,
  "consumerContact": null,
  "imageUrls": [],
  "voluntaryMandated": "Voluntary: Firm initiated",
  "classificationDate": "2026-10-15",
  "fdaEventId": "100999",
  "url": "https://api.fda.gov/food/enforcement.json?search=recall_number:%22F-0123-2027%22",
  "scrapedAt": "2026-10-30T10:20:00.000Z"
}
```

CPSC recalls fill `description`, `remedy` (refund, repair or replace), `incidents` (reported injuries), `consumerContact`, `manufacturedIn` and `imageUrls`, and link to the recall page on cpsc.gov. CPSC has no class or status, so those are `null`. For example: `"title": "Acme Home Recalls Tabletop Heaters Due to Fire Hazard"`, `"brand": "Acme Home"`, `"firm": "Acme Home Products Inc."`, `"identifiers": { "model": ["AH-200"] }`.

Export as JSON, CSV, Excel or HTML, or call it from the API, Make, Zapier, n8n or an AI agent (MCP). A `SUMMARY` record in the run's key-value store gives the counts per source: fetched, matched, already seen, delivered, and any source that was unavailable.

#### Pricing

**Pay per event: $0.002 per recall delivered ($2.00 per 1,000 recalls).**

| You run | Recalls (October 2026 volumes) | You pay |
|---|---|---|
| All 4 sources, last 30 days | ≈ 470 | ≈ $0.94 |
| All 4 sources, daily, only-new | ≈ 15 a day | ≈ $0.03 a day, ≈ $1 a month |
| FDA food, Class I only, daily, only-new | a few a week | cents a month |
| All 4 sources, full last year | ≈ 6,150 | ≈ $12.30 |

Set **Maximum cost per run** in the run options to cap spend. The Actor stops cleanly at your limit and says so in the log and the status message. Apify's free plan includes $5 of monthly usage, enough for about 2,500 recalls.

#### Limits

- **Freshness.** CPSC publishes recalls most Thursdays. openFDA updates enforcement reports weekly, and an FDA report usually appears a few weeks after the firm starts the recall (`recallDate` vs `publishedDate`).
- **FDA status is not live.** FDA says it does not update a recall's status after classification, so `status` is the status when the report was published.
- **openFDA request limits.** Without a key, openFDA allows 240 requests a minute and 1,000 a day per IP address. A normal run makes 1 request per FDA source (up to 1,000 recalls each). The Actor makes requests one at a time, pauses between pages, and retries politely. Very long windows (years of device recalls) take more requests; add your own free openFDA key if you run those often. openFDA returns at most 26,000 records per query window, so a window larger than that returns the newest and the run log tells you.
- **One source down does not fail the run.** The other sources are still delivered and `SUMMARY.sourceErrors` names the one that failed.
- **Not covered:** USDA FSIS (meat, poultry, eggs) and NHTSA (vehicles), and non-US agencies.

#### Personal data

Recall notices list the firm's consumer hotline and recall email. This Actor removes every email address and phone number from all text fields (`[phone removed]`, `[email removed]`), and leaves out the firm's street address (city, state and country stay). Website links stay. Product codes that look like phone numbers (lot, catalog and part numbers) are kept, because they are what you match recalls on.

#### Sources and terms

- **CPSC:** the [CPSC Recalls API](https://www.cpsc.gov/Recalls/CPSC-Recalls-Application-Program-Interface-API-Information) (SaferProducts.gov REST web services). CPSC states that information on its websites is public and may be freely distributed and copied, and asks that it be credited. Source: U.S. Consumer Product Safety Commission.
- **FDA:** the [openFDA](https://open.fda.gov) food, drug and device enforcement report APIs, public domain (CC0) under the [openFDA terms](https://open.fda.gov/terms/). Data provided by the U.S. Food and Drug Administration (https://open.fda.gov).
- **openFDA disclaimer:** "Do not rely on openFDA to make decisions regarding medical care. While we make every effort to ensure that data is accurate, you should assume all results are unvalidated." FDA also states: "This data should not be used as a method to collect data to issue alerts to the public, nor should it be used to track the lifecycle of a recall." For public-facing alerts, link to the official FDA or CPSC notice, which is the authority.
- This Actor is **not affiliated with, or endorsed by, CPSC, FDA or the U.S. government.**

#### FAQ

**How do I match recalls against my products?** Run it with `onlyNew` on a daily schedule, then join `identifiers.upc`, `identifiers.model`, `identifiers.ndc`, `brand` or `firm` against your catalog.

**Why is `brand` empty?** CPSC brands come from the recall title ("Acme Recalls ..."), and some titles don't name one. FDA publishes a brand only for some drugs.

**Why does a recall ID start with `EVENT-`?** openFDA sometimes publishes a report without a recall number. The Actor gives it a stable ID from the FDA event number, so only-new mode still works.

**Something broke or a field is wrong?** Open an issue on the Issues tab with the recall ID. Issues are answered within 24 hours.

# Actor input Schema

## `sources` (type: `array`):

cpsc = consumer products (CPSC). fda-food, fda-drug, fda-device = FDA enforcement reports via openFDA.

## `keywords` (type: `array`):

Keep recalls whose title, product, firm, brand or reason/hazard contains any of these words (case-insensitive). Empty keeps all.

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

Keep only FDA recalls of these classes, for example "Class I" (most serious). Empty keeps all. CPSC recalls have no class, so they are removed when this is set.

## `publishedWithinDays` (type: `integer`):

Recalls published (CPSC last publish date, FDA enforcement report date) in the last N days. 1 to 3650.

## `maxItemsPerSource` (type: `integer`):

Newest first. 0 means no limit.

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

0 means no limit. Use the run's maximum-cost setting to cap spend as well.

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

For scheduled runs: remember delivered recall IDs and return (and charge) only new ones.

## `stateKey` (type: `string`):

Name for the only-new memory, so different schedules keep separate lists.

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

Not needed. openFDA allows 1,000 requests a day per IP without a key; a free key from open.fda.gov raises that to 120,000. Add one only if you run very large windows many times a day.

## Actor input object example

```json
{
  "sources": [
    "cpsc",
    "fda-food",
    "fda-drug",
    "fda-device"
  ],
  "keywords": [],
  "classifications": [],
  "publishedWithinDays": 30,
  "maxItemsPerSource": 0,
  "maxItems": 0,
  "onlyNew": false,
  "stateKey": "default"
}
```

# Actor output Schema

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

Recalls, one item per charged event, in the default dataset.

## `summary` (type: `string`):

Counts for the run (found, delivered, skipped, errors, spending-limit stop) in the SUMMARY record.

# 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 = {
    "sources": [
        "cpsc",
        "fda-food",
        "fda-drug",
        "fda-device"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("opendata-feeds/us-recalls-feed").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 = { "sources": [
        "cpsc",
        "fda-food",
        "fda-drug",
        "fda-device",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("opendata-feeds/us-recalls-feed").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 '{
  "sources": [
    "cpsc",
    "fda-food",
    "fda-drug",
    "fda-device"
  ]
}' |
apify call opendata-feeds/us-recalls-feed --silent --output-dataset

```

## MCP server setup

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

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/OD7Crth3Wh9WfjXQO/builds/sOifPjsAERTrqhjOH/openapi.json
