# Product Recall Radar - FDA, CPSC & USDA Recalls API (`alessandrosir/product-recall-radar`) Actor

Know within the hour when a product you sell, stock, or insure is recalled. It monitors all US federal authorities (FDA, CPSC, USDA) and delivers unified records with a single severity scale, catalogue barcode matching, built-in change detection, and verified source delivery.

- **URL**: https://apify.com/alessandrosir/product-recall-radar.md
- **Developed by:** [Alessandro Sirsi](https://apify.com/alessandrosir) (community)
- **Categories:** E-commerce, Developer tools
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## 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

## Product Recall Radar — FDA, CPSC & USDA Recalls API

**Know within the hour when a product you sell, stock or insure is recalled.**

A single endpoint that monitors every US federal recall authority in real time and tells you — in one consistent format — what was recalled, how serious it is, and whether it touches your catalogue.

***

### The problem this removes

A recall becomes your problem the moment it is published, not the moment you happen to notice it. Between publication and detection sit the products still on your shelves, still in your listings, still shipping to customers.

Detection is hard for a boring reason: the information is scattered across four federal authorities that share no common format, no common severity scale and no common identifier. Building and *maintaining* that reconciliation layer is weeks of engineering, and it breaks quietly every time an agency changes a field.

This Actor is that layer, kept working for you.

***

### What you get

**One schema across every authority.** Food, pharmaceuticals, medical devices, consumer products, meat and poultry arrive as identical records. Write your integration once.

**Catalogue matching.** Send your UPC/EAN/GTIN codes and receive only the recalls that hit *your* products, each tagged with which of your codes matched. Barcode formats are normalized, so a UPC-12 in your system matches an EAN-13 in a government notice.

**A single severity scale.** Three agencies, three different classification systems, one comparable `severity` field — so one alerting rule covers all of them.

**Change detection built in.** Incremental mode remembers what it has already delivered. Schedule it hourly and each run returns only what is new. No diffing on your side, no duplicate alerts at 3am.

**Verified delivery.** Every run reports which authorities actually answered. Silence and "nothing was recalled" are different facts, and this feed never confuses the two.

**Traceability.** Every record links back to the authoritative government notice, so any decision you take from this feed can be evidenced.

***

### Quick start

```bash
curl -X POST "https://api.apify.com/v2/acts/alessandrosir~product-recall-radar/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "regions": ["US"],
        "daysBack": 7,
        "severities": ["high"],
        "onlyActive": true
      }'
```

**Watch your own catalogue:**

```bash
-d '{
      "regions": ["US"],
      "productCodes": ["0736659265018", "029361000000"],
      "incremental": true
    }'
```

Only recalls affecting those barcodes come back — each with a `matched_codes` field naming the product involved.

**Reproduce an exact reporting window:**

```bash
-d '{"regions":["US"],"dateFrom":"2026-01-01","dateTo":"2026-06-30"}'
```

***

### Who uses it

**Marketplaces and retailers** — flag or delist affected listings before a customer buys one.
**Grocery and food service** — know about a contamination notice the same hour it is issued.
**Compliance and quality teams** — a defensible, timestamped record of what was published and when you saw it.
**Insurers and risk analysts** — recall frequency by company as a measurable risk signal.
**AI agents and RAG pipelines** — current, citable safety data instead of a model's recollection.

***

### Coverage

| Source key | Authority | Scope |
| --- | --- | --- |
| `openfda_food` | FDA | Food & beverage |
| `openfda_drug` | FDA | Pharmaceuticals |
| `openfda_device` | FDA | Medical devices |
| `cpsc` | CPSC | Consumer products |
| `fsis` | USDA FSIS | Meat, poultry, eggs — see note |

Set `regions: ["US"]` to monitor all generally available authorities.

**On USDA FSIS:** the agency's endpoint and its public mirrors reject requests from datacenter IP ranges, so the source is unreachable from shared cloud infrastructure. It is fully implemented and enabled by adding `"sources": ["fsis"]` when you run from your own infrastructure or through a residential proxy. It is left out of the default region so a standard run never returns an incomplete result without saying so. European authorities (RASFF / Safety Gate) are in development — every record already carries `source_country`, so adding a region will not change your integration.

***

### Output schema

| Field | Type | Nullable | Description |
| --- | --- | --- | --- |
| `id` | string | no | Stable unique id. Safe to deduplicate and store as a key. |
| `source` | string | no | Issuing authority key. |
| `source_country` | string | no | ISO country code. |
| `category` | string | no | `food`, `drug`, `device`, `consumer_product`, `meat_poultry`. |
| `title` | string | yes | Readable headline, ~140 chars. |
| `product_description` | string | yes | Full product text when the title was shortened. |
| `reason` | string | yes | The hazard or defect. |
| `company` | string | yes | Recalling firm. |
| `brand_names` | string\[] | no | Brands named by the authority. |
| `classification` | string | yes | The authority's own wording, e.g. `Class I`. |
| `severity` | string | no | `high`, `medium`, `low`, `unknown`. |
| `severity_source` | string | no | `agency` when officially published, `none` when the authority publishes none. |
| `hazards` | string\[] | no | Hazard labels as published. |
| `status` | string | yes | e.g. `Ongoing`, `Terminated`, `Active`. |
| `recall_date` | string | yes | `YYYY-MM-DD`. |
| `distribution` | string | yes | Where the product was distributed. |
| `quantity` | string | yes | Units affected. |
| `url` | string | yes | Link to the authoritative notice. |
| `identifiers.codes` | string\[] | no | Product codes found in the notice. |
| `identifiers.matched_codes` | string\[] | when matching | Which of your codes this recall hits. |
| `retrieved_at` | string | no | ISO 8601 UTC. |
| `raw` | object | with `includeRaw` | Original payload. |

#### Example record

```json
{
  "id": "openfda_food:F-0890-2026",
  "source": "openfda_food",
  "source_country": "US",
  "category": "food",
  "title": "White Cheddar Seasoning, Net Wt. 1.6 oz (45g), UPC 7 36659-26501 8…",
  "product_description": "White Cheddar Seasoning packaged in the following sized containers: …",
  "reason": "Products may be potentially contaminated with Salmonella.",
  "company": "JCB Flavors, LLC",
  "brand_names": [],
  "classification": "Class I",
  "severity": "high",
  "severity_source": "agency",
  "hazards": [],
  "status": "Ongoing",
  "recall_date": "2026-05-01",
  "distribution": "KS, MN, NY, WI",
  "quantity": "5200 jars total",
  "url": "https://api.fda.gov/food/enforcement.json?search=recall_number:\"F-0890-2026\"",
  "identifiers": { "codes": ["736659265018"] },
  "retrieved_at": "2026-08-09T09:00:00+00:00"
}
```

***

### Setting up monitoring

1. Set `incremental: true`, `regions: ["US"]`, `onlyActive: true`.
2. Schedule the Actor hourly or daily.
3. Add a webhook on run success.

Each run then delivers only what is new. Narrow it further with `productCodes` to be alerted solely about your own products, or `severities: ["high"]` for life-safety events.

Changed your filters and want the window re-delivered? Run once with `resetState: true`, then switch it off.

***

### Run summary

Every run writes an `OUTPUT` object to the default key-value store:

```json
{
  "partial": false,
  "sources_ok": ["openfda_food", "openfda_drug", "openfda_device", "cpsc", "fsis"],
  "sources_failed": [],
  "total_emitted": 285,
  "new_records": 285,
  "high_severity": 61,
  "by_severity": { "high": 61, "medium": 162, "low": 4, "unknown": 58 },
  "by_source": { "openfda_device": 116, "openfda_drug": 57, "cpsc": 53, "openfda_food": 50, "fsis": 9 },
  "window_start": "2026-07-10",
  "window_end": null,
  "skipped_already_delivered": 0,
  "skipped_by_filters": 0,
  "only_active": false,
  "incremental": true,
  "finished_at": "2026-08-09T19:49:56Z"
}
```

**`partial` is the field to check before acting on a run.** An empty result can mean nothing was recalled — or that an authority was unreachable. Feeds that do not distinguish the two will let you report "all clear" on a day you simply failed to look. When `partial` is `true`, `sources_failed` names the authority and the reason. A single source failing never aborts the run.

***

### For AI agents

Recall status is exactly the question a language model should never answer from memory: it changes daily and a wrong answer has consequences.

- Every record carries a `url` to the authoritative notice, so answers are citable.
- `severity_source` distinguishes an official classification from an absent one, so the agent never presents a guess as an agency decision.
- `partial` lets the agent say "I could not reach the FDA just now" instead of "there are no recalls" — the difference between a cautious assistant and a dangerous one.

***

### Input reference

| Option | Type | Default | Purpose |
| --- | --- | --- | --- |
| `regions` | string\[] | `["US"]` | Jurisdictions to monitor. |
| `sources` | string\[] | — | Advanced: individual authorities. Ignored when `regions` is set. |
| `daysBack` | integer | 30 | Look-back window on a first run. |
| `dateFrom` | string | — | `YYYY-MM-DD`. Overrides `daysBack`; use for backfills. |
| `dateTo` | string | — | `YYYY-MM-DD`. Upper bound. |
| `incremental` | boolean | true | Deliver only records not seen in previous runs. |
| `resetState` | boolean | false | Clear the delivery memory once. |
| `keywords` | string\[] | — | Free-text filter across title, reason, company, brands. |
| `classifications` | string\[] | — | `Class I` / `II` / `III`. |
| `severities` | string\[] | — | `high` / `medium` / `low` / `unknown`. |
| `onlyActive` | boolean | false | Exclude terminated, completed and closed recalls. |
| `productCodes` | string\[] | — | Your barcodes. Returns only recalls affecting them. |
| `openFdaApiKey` | string | — | Free FDA key; raises the request ceiling. |
| `maxItems` | integer | 1000 | Cap per run. |
| `includeRaw` | boolean | false | Attach the original payload. |

***

### Reliability

- Requests retry with exponential backoff on connection failures, rate limits and gateway errors.
- Each authority is isolated: one failing never aborts a run.
- Records are deduplicated within a run and across runs.
- The USDA source tries the official API first and falls back to public government mirrors automatically. Records from a mirror carry fewer fields — no recall number or classification — and arrive with `severity: "unknown"`.
- An empty result is always explained. The run summary reports how many records were withheld because a previous run already delivered them (`skipped_already_delivered`) and how many your filters excluded (`skipped_by_filters`), so "nothing new" is never ambiguous.

### Notes

- Coverage is United States in this version.
- `severity` reflects only what the issuing authority published. CPSC does not publish a severity level, so those records carry `severity: "unknown"`; use the `hazards` field to apply your own rules. Severity is never inferred from wording.
- USDA publishes currently listed recalls rather than a deep archive, so a long look-back has limited effect there.
- This feed is monitoring infrastructure, not a substitute for an authority's own notification channels. Verify against the linked notice before acting on a specific case.

### Sources

Data originates from the official US federal recall authorities: FDA (openFDA), the Consumer Product Safety Commission, and USDA Food Safety and Inspection Service. No personal data is collected, processed or returned.

# Actor input Schema

## `regions` (type: `array`):

Simplest way to choose coverage. Pick a jurisdiction and every generally available source for it is queried. Overrides the advanced Sources list. Currently: US (EU coming).

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

Fine-grained source selection. Ignored when Regions is set. Note: 'fsis' (USDA meat & poultry) requires non-datacenter egress and is therefore not part of the default US region - enable it here only when running on your own infrastructure or through a residential proxy.

## `daysBack` (type: `integer`):

How many days of history to fetch. Ignored when incremental mode is on and a previous run exists.

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

Optional. YYYY-MM-DD. Use for backfills or for reproducing an exact reporting window.

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

Optional. YYYY-MM-DD. Leave empty for 'up to today'.

## `incremental` (type: `boolean`):

Return only records that previous runs have not already delivered. Turn this ON for scheduled monitoring - it is what makes each run an alert stream rather than a repeated dump. Left OFF by default so a one-off run always returns the full window.

## `resetState` (type: `boolean`):

Clears the record of what was already delivered, so the next run returns everything in the window again. Use once after changing filters, then switch off.

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

Optional. Only return recalls whose text matches at least one of these terms (case-insensitive). Example: your brand names or product categories.

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

Optional. Class I is the most serious. Leave empty for all.

## `severities` (type: `array`):

Optional. Normalized severity across all sources: high, medium, low, or unknown (CPSC publishes no severity). Leave empty for all.

## `onlyActive` (type: `boolean`):

Exclude recalls the agency has marked terminated, completed or closed. Recommended for live monitoring.

## `productCodes` (type: `array`):

Optional but powerful. Paste the barcodes from your own catalogue and the Actor returns only recalls that touch those products. Matching ignores spaces, hyphens and leading zeros.

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

Free key from open.fda.gov. Without it the shared rate limit is much lower.

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

Safety cap on the number of records returned per run.

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

Adds the original untouched record under the 'raw' field. Useful for debugging, heavier output.

## Actor input object example

```json
{
  "regions": [
    "US"
  ],
  "sources": [],
  "daysBack": 30,
  "incremental": false,
  "resetState": false,
  "keywords": [],
  "classifications": [],
  "severities": [],
  "onlyActive": false,
  "productCodes": [],
  "maxItems": 1000,
  "includeRaw": false
}
```

# Actor output Schema

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

Every record matching your filters, newest first.

## `highSeverity` (type: `string`):

Records the issuing authority classified at its most serious level.

## `productCodes` (type: `string`):

Records with the UPC/GTIN identifiers found in each notice.

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

Which authorities answered, counts by severity and source, and whether the run was partial. Check this before treating a run as complete.

## `downloadCsv` (type: `string`):

The same records in spreadsheet form.

## `totalRecords` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("alessandrosir/product-recall-radar").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("alessandrosir/product-recall-radar").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 '{}' |
apify call alessandrosir/product-recall-radar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,alessandrosir/product-recall-radar"
        }
    }
}

```

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/a8ZBW3Sh1cTQqauKK/builds/MN7Uqn8NETSTfjCz1/openapi.json
