# US Product Recalls Monitor — CPSC & FDA Food, Drug, Device (`oneshotventure/recalls-monitor`) Actor

Scheduled change feed of newly published US product recalls from CPSC and openFDA.

- **URL**: https://apify.com/oneshotventure/recalls-monitor.md
- **Developed by:** [Nick](https://apify.com/oneshotventure) (community)
- **Categories:** Agents, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.50 / 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## US Product Recalls Monitor — CPSC & FDA Food, Drug, Device

Get **newly published US product recalls since your last run** from the Consumer Product Safety
Commission and openFDA's food, drug and device enforcement reports. Four official sources, one stable
JSON schema, built for scheduled alerts and agent workflows.

### What does this Actor do?

It reads four separate official recall feeds and merges them into a single record shape. That merge
is the work: the CPSC and the FDA describe recalls in genuinely different vocabularies. The CPSC
publishes structured *hazards* and lists manufacturers, importers and distributors separately, but no
recall classification. openFDA publishes a Class I/II/III classification and a status, a single
recalling firm, and a free-text reason — but no link back to a recall page.

Rather than pretend those are the same thing, this Actor maps both onto one schema and leaves the
fields a given source genuinely does not publish as `null`. The complete original record is always
preserved in `raw`, so nothing is lost.

Records are de-duplicated across sources on source and recall ID, and keyword matching runs across
the title, description, reason and firm name together — so searching `listeria` catches it whether the
word appears in the product description or in the reason for the recall.

### Who is it for?

- **Retailers and e-commerce operators** who must pull affected products from shelves and listings
  quickly.
- **Quality and compliance teams** at manufacturers, importers and distributors tracking their own
  and their suppliers' recalls.
- **Food-safety and clinical teams** watching Class I events in their category.
- **Insurers and risk analysts** monitoring recall activity by firm or product type.
- **AI agent builders** who want a product-safety tool with a stable output schema.

### Use cases

- Alert the moment a recall names one of your suppliers, by watching their firm name as a keyword.
- Monitor a hazard type — `listeria`, `battery`, `laceration` — across every source at once.
- Track Class I drug and device enforcement reports as a regulatory-risk feed.
- Cross-check a product catalogue against new recalls on a daily schedule.
- Give an agent a tool that answers "has anything in this category been recalled recently?"

### What you get

One row per recall. Any value a source does not publish is `null` — the field is always present, and
the untouched source record stays in `raw`.

| Field | Type | Description |
|---|---|---|
| `source` | string | `cpsc`, `fda-food`, `fda-drug` or `fda-device` |
| `recallId` | string | The source's own recall identifier |
| `recallNumber` | string | The source's recall number |
| `title` | string | CPSC recall title; for openFDA sources, the first 200 characters of the product description |
| `description` | string | Product description |
| `reason` | string | CPSC hazards joined together, or openFDA's reason for the recall |
| `firm` | string | CPSC: the first named manufacturer, importer or distributor. openFDA: the recalling firm |
| `classification` | string | Recall class (`Class I`, `Class II`, `Class III`). **openFDA sources only** |
| `status` | string | Recall status. **openFDA sources only** |
| `action` | object | Source-specific remedy and stock-action details; its shape differs between CPSC and openFDA records (below) |
| `recallDate` | string | CPSC recall date, or openFDA's report date |
| `url` | string | Link to the CPSC recall page. **CPSC only** — openFDA publishes no per-record URL |
| `raw` | object | The complete original record from the source, unmodified |
| `newSinceLastRun` | boolean | Whether this recall is newer than the saved watermark |
| `retrievedAt` | string | ISO 8601 timestamp of when this row was fetched |

#### `action` shapes

`action` intentionally retains each source's distinct action vocabulary. All fields are nullable.

- **CPSC:** `{ "remedy", "consumerContact", "unitsAffected", "soldAt" }` — remedy names joined from `Remedies`, the consumer contact, the first non-empty `Products[].NumberOfUnits`, and `SoldAtLabel`.
- **openFDA:** `{ "status", "voluntaryMandated", "distributionPattern", "quantity", "codeInfo" }` — the enforcement status, whether it was voluntary or mandated, distribution pattern, product quantity, and `code_info` (limited to 300 characters).

#### Sample output record

An openFDA food enforcement record:

```json
{
  "source": "fda-food",
  "recallId": "F-1892-2026",
  "recallNumber": "F-1892-2026",
  "title": "Ready-to-eat chicken salad, 12 oz plastic containers, lot codes 226…",
  "description": "Ready-to-eat chicken salad, 12 oz plastic containers, lot codes 226 through 238.",
  "reason": "Product may be contaminated with Listeria monocytogenes.",
  "firm": "Example Foods Inc.",
  "classification": "Class I",
  "status": "Ongoing",
  "recallDate": "2026-08-18",
  "url": null,
  "raw": { "recall_number": "F-1892-2026", "…": "…" },
  "newSinceLastRun": true,
  "retrievedAt": "2026-08-23T09:20:03.551Z"
}
```

### How to use it

#### Watch a hazard across every source

```json
{
  "sources": ["cpsc", "fda-food", "fda-drug", "fda-device"],
  "keywords": ["listeria", "battery"],
  "maxResults": 100
}
```

#### Consumer products only

```json
{
  "sources": ["cpsc"],
  "maxResults": 50
}
```

#### Backfill from an explicit date

```json
{
  "sources": ["fda-device"],
  "sinceDate": "2026-07-01",
  "maxResults": 500
}
```

### Input parameters

| Input | Type | Description |
|---|---|---|
| `sources` | array | Sources to monitor: `cpsc`, `fda-food`, `fda-drug`, `fda-device`. Default: all four |
| `keywords` | array | Optional, case-insensitive. A recall matches if any keyword appears in its title, description, reason or firm name |
| `sinceDate` | string | Optional ISO 8601 date or timestamp that overrides the saved watermark as the window start |
| `maxResults` | integer | Maximum total records across all sources (1–1000). Default: `100` |

### How monitoring works (first run and scheduling)

The first run reads the **prior 30 days** and saves a watermark once records have been emitted
successfully. To get only-new records on every subsequent run, **schedule this Actor as a saved Task**
so runs share the same storage — the watermark lives in the task's key-value store. A fresh unsaved
run starts a fresh baseline.

A failed source does not emit partial results and does not advance the watermark, so a transient
outage cannot silently skip a window.

### Agent-friendly use

Schedule the Actor, read its default dataset, and alert only when `newSinceLastRun` is true. Filter
by `source`, `classification`, `firm` or keywords for a focused workflow.

### Honest limitations

- **`classification` and `status` are openFDA-only.** The CPSC does not publish a recall
  classification, so those fields are `null` on every `cpsc` record. Filtering on "Class I" therefore
  filters out all consumer-product recalls.
- **`url` is CPSC-only.** openFDA enforcement records carry no per-record web page, so `url` is `null`
  for the three FDA sources. Use `recallNumber` to look one up.
- **`firm` is one name, not the full chain.** The CPSC often lists several manufacturers, importers
  and distributors; the normalized field takes the first available. The complete list is in `raw`.
- **No vehicle recalls.** NHTSA is a separate agency with a separate feed and is not a source here.
- **`maxResults` is a total across sources**, consumed in the order sources are listed. If you cap at
  100 and the first source has 100 new recalls, later sources contribute nothing that run.
- **Reporting lag is the agencies'.** A recall announced today may enter the enforcement report
  several days later, especially for openFDA.

### Reliability

Per-source diagnostics are written to the `RUN_SUMMARY` key-value record — never into your dataset.
Failures carry structured reason codes and prevent the watermark from advancing.

Every record carries `retrievedAt` and keeps the untouched source record in `raw`, so you can check
any row against the agency yourself.

Maintained against the upstream APIs. If a source changes its schema or an endpoint moves, report it
through the Issues tab and it gets fixed.

### Integrations

Connect this Actor to Make, Zapier, n8n, Slack, Google Sheets, Airtable or any HTTP endpoint through
Apify integrations. A daily Task can post new Class I recalls into a compliance channel, or fire a
webhook so a catalogue check starts the moment a recall lands. Datasets export as JSON, CSV, Excel,
XML, RSS or HTML.

### API usage

```bash
curl -X POST "https://api.apify.com/v2/acts/oneshotventure~recalls-monitor/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sources": ["cpsc", "fda-food"], "keywords": ["listeria"], "maxResults": 100}'
```

Python, JavaScript, PHP and CLI clients are documented under
[Apify API clients](https://docs.apify.com/api/client).

### Use with AI agents (MCP)

This Actor is callable from any MCP-compatible client — Claude, Cursor, VS Code or your own agent —
through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp). The input schema is
fully described and every record uses one stable, flat JSON shape, so an agent can call it and read
the result without a parsing step. Because it reads official government APIs rather than a rendered
page, the answer an agent gets is the same one the source publishes.

### Frequently asked questions

#### Is there a free FDA recall API?

Yes — openFDA publishes food, drug and device enforcement reports as an open API, and the CPSC
publishes its own recall API. This Actor is the merging, filtering and change-feed layer over all
four.

#### How do I get alerts for new product recalls?

Set your `sources` and `keywords`, save the Actor as a Task, schedule it daily, and connect the Task
to Slack, email or a webhook through Apify integrations.

#### What is the difference between a Class I, II and III recall?

The FDA classifies by risk: Class I means a reasonable probability of serious harm or death, Class II
means temporary or reversible harm, Class III means unlikely to cause harm. The classification is
published by openFDA only — CPSC records have no equivalent field.

#### Does this cover vehicle recalls?

No. Vehicle and tyre recalls are published by NHTSA, which is not one of this Actor's sources.

#### Can I monitor recalls for a specific company?

Yes — put the firm name in `keywords`. Keyword matching runs across the firm name as well as the
title, description and reason.

#### Why is `url` empty on my FDA records?

Because openFDA enforcement reports do not include a per-record web page. CPSC records do, and carry
it in `url`. For FDA records, the `recallNumber` is the identifier to search on.

#### How fresh is the data?

Each run reads the agency APIs live. The lag that matters is the agencies' own: a recall announced
publicly can take days to appear in the enforcement report dataset.

### Related actors

- [Federal Register Monitor](https://apify.com/oneshotventure/fedreg-watch) — new federal rules,
  proposed rules and notices, including FDA and CPSC rulemaking.
- [ClinicalTrials.gov Monitor](https://apify.com/oneshotventure/trials-watch) — newly posted clinical
  studies by condition and sponsor.
- [NPI Registry Lookup](https://apify.com/oneshotventure/npi-lookup) — US healthcare provider search
  and verification.
- [SAM.gov Contract Opportunities & Awards Feed](https://apify.com/oneshotventure/sam-feed) — federal
  contract opportunities and awards.

### Disclaimer

Unofficial independent tool. Not affiliated with or endorsed by the CPSC or FDA. Data is public
government data retrieved from their official public APIs.

# Actor input Schema

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

Sources to monitor. Allowed values: cpsc, fda-food, fda-drug, fda-device.

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

Optional case-insensitive keywords. A recall matches if any keyword appears in its title, description, reason, or firm name.

## `sinceDate` (type: `string`):

Optional ISO-8601 date or timestamp that overrides the saved watermark as the window start.

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

Maximum total records across all sources (1-1000).

## Actor input object example

```json
{
  "sources": [
    "cpsc",
    "fda-food",
    "fda-drug",
    "fda-device"
  ],
  "keywords": [],
  "maxResults": 25
}
```

# Actor output Schema

## `records` (type: `string`):

Normalized recall records in the default dataset.

## `runSummary` (type: `string`):

Run diagnostics stored as RUN\_SUMMARY in the key-value store.

# 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"
    ],
    "keywords": [],
    "maxResults": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("oneshotventure/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 = {
    "sources": [
        "cpsc",
        "fda-food",
        "fda-drug",
        "fda-device",
    ],
    "keywords": [],
    "maxResults": 25,
}

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

```

## MCP server setup

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