# Recall Monitor: FDA & CPSC New-Recall Feed (`upward_enterprises/recall-monitor`) Actor

New product recalls from every federal source in one only-new feed: FDA enforcement reports (food, drugs, devices) and CPSC consumer-product recalls, keyword and firm filtered, one normalized row per recall. Keyless public APIs. From $2 per 1,000.

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

## Pricing

from $1.20 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Recall Monitor: FDA & CPSC New-Recall Feed

**$2.00 per 1,000 new recalls ($1.20 on the Gold tier), no start fee, no API key.**

Product recalls are published in four different federal places, on four different schedules, and nobody reads all of them. The FDA posts weekly enforcement reports for food, drugs, and medical devices. The CPSC posts consumer-product recalls separately. A supplier's recall can get your listings pulled, your inventory quarantined, or your customers hurt, and the first most companies hear about it is a retailer notice days later.

Recall Monitor checks all four sources and delivers only the recalls you have not seen, as one normalized row each: firm, product, reason, hazard, FDA classification, status, distribution, remedy, and a link to the source record. Point it at your own company and your suppliers with the firm filter, or at product categories with keywords, run it on a schedule, and each recall reaches you once.

### What you get

**Recall rows** (`kind: "recall"`), one per new recall:

```json
{
  "kind": "recall",
  "id": "cpsc:11022",
  "source": "cpsc",
  "recallNumber": "27024",
  "recallDate": "2026-10-15",
  "firm": "3M Company",
  "product": "Scotch Thermal Laminators",
  "reason": "3M Company is recalling the thermal laminators because they can overheat…",
  "hazard": "The recalled thermal laminators can overheat, posing a fire hazard.",
  "classification": null,
  "status": null,
  "distribution": null,
  "remedy": "Consumers should stop using and unplug the recalled laminators immediately…",
  "units": "About 53,634",
  "url": "https://cpsc.gov/Recalls/2027/…"
}
```

FDA rows carry `classification` (Class I, II, or III), `status` (Ongoing, Completed, Terminated, Pending), and `distribution` instead of hazard/remedy; the shape is the same either way.

A free **per-source row** (`kind: "source"`) reports what each source returned, matched, and delivered, and a free **summary row** (`kind: "summary"`) closes every run.

### Sources

| Source | What it covers | Cadence |
|---|---|---|
| `openfda-food` | FDA food enforcement reports: foods, beverages, dietary supplements, cosmetics | Weekly reports |
| `openfda-drug` | FDA drug enforcement reports: prescription, OTC, compounded products | Weekly reports |
| `openfda-device` | FDA device enforcement reports: medical devices and diagnostics | Weekly reports |
| `cpsc` | CPSC SaferProducts: consumer products, appliances, toys, tools, furniture | As announced |

All four are official public APIs and need no key. An optional free openFDA API key input raises the per-IP daily cap.

### How the only-new memory works

The monitor keeps the IDs of recalls it has already delivered in a key-value store on your account, named `recall-monitor-<snapshot name>`. Give each schedule its own snapshot name. The first run of a snapshot fetches from your start date (default 30 days back); later runs overlap the previous 14 days so a recall posted late is still caught, and the seen set guarantees it is delivered exactly once. Turn **Only new** off to re-pull the whole window on every run.

### Inputs

| Input | Default | Notes |
|---|---|---|
| Sources | all four | Any subset of `openfda-food`, `openfda-drug`, `openfda-device`, `cpsc` |
| Keywords | none | Any-of, case-insensitive, matched against firm, product, reason, and hazard |
| Recalling firms | none | Any-of substring on the firm or manufacturer name; the supplier watch |
| Only new recalls | on | Off re-delivers the whole window every run |
| Snapshot name | `default` | One memory per schedule |
| First-run start date | 30 days back | `YYYY-MM-DD`; later runs pick up where the last left off |
| Max recalls per run | 500 | Anything past the cap stays unseen and arrives next run |
| openFDA API key | none | Optional; keyless works |

Worked example: a grocery compliance watch over all four sources typically sees 40 to 80 new recalls a month unfiltered, about **$0.08 to $0.16 a month** at the Free tier. A keyword watch on one product category costs less.

### Pricing

Pay per new recall delivered. Free $0.002, Bronze $0.0018, Silver $0.0015, Gold $0.0012 per recall. Recalls your snapshot has already delivered are never charged again. Source rows and the summary row are free.

### Notes and limits

- Data comes from openFDA enforcement reports and the CPSC SaferProducts API, both public US government data. FDA enforcement reports are weekly, so an FDA recall can be up to a week old in the report before it appears here; CPSC recalls appear as announced.
- An FDA recall that is later updated keeps its recall number, so it is delivered once, on first sight. Status changes after delivery are not re-sent.
- NHTSA vehicle recalls and USDA meat and poultry recalls are planned additions.

### Enjoying this Actor?

If it caught a recall before your retailer did, a review on the Store page helps other compliance folks find it, and it tells us to keep the source list growing. Questions and source requests are welcome on the Issues tab; we answer fast.

# Actor input Schema

## `firms` (type: `array`):

Optional. Deliver only recalls whose recalling firm or manufacturer name contains any of these, case-insensitive. Use it to watch your own company and your suppliers.

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

Optional. Deliver only recalls whose firm, product, reason, or hazard mentions any of these words or phrases, case-insensitive. Empty means every recall from the chosen sources.

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

Upper limit on new recalls delivered in one run. Anything past the limit stays unseen and is delivered on the next run.

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

On (default): each recall is delivered once per snapshot; later runs return only recalls you have not seen. Off: every run returns everything in the window.

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

Optional. The monitor works keyless; a free key from open.fda.gov raises the per-IP daily request cap, which helps on busy shared IPs.

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

YYYY-MM-DD. On the first run of a snapshot, fetch recalls reported on or after this date. Empty means 30 days back. Later runs pick up where the last run left off, with a 14-day overlap so late-posted recalls are not missed.

## `snapshotName` (type: `string`):

Which memory this monitor uses. Kept on your account as a key-value store named recall-monitor-<name>. Give each schedule its own name.

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

Which recall sources to check. openfda-food / openfda-drug / openfda-device are the FDA's weekly enforcement reports; cpsc is the Consumer Product Safety Commission's SaferProducts feed. Empty means all four.

## Actor input object example

```json
{
  "firms": [],
  "keywords": [],
  "maxItems": 500,
  "onlyNew": true,
  "openFdaApiKey": "",
  "sinceDate": "2026-10-01",
  "snapshotName": "default",
  "sources": [
    "openfda-food",
    "cpsc"
  ]
}
```

# Actor output Schema

## `all` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `recallColumns` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("upward_enterprises/recall-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": [
        "openfda-food",
        "openfda-drug",
        "openfda-device",
        "cpsc",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("upward_enterprises/recall-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": [
    "openfda-food",
    "openfda-drug",
    "openfda-device",
    "cpsc"
  ]
}' |
apify call upward_enterprises/recall-monitor --silent --output-dataset

```

## MCP server setup

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