# US Recalls Monitor - FDA Food, Drug, Device & NHTSA Recalls (`borasipahi/us-recalls-monitor`) Actor

One normalised feed of US product recalls: FDA food, drug and device enforcement reports plus NHTSA vehicle, tyre, equipment and child-seat recalls, ranked by severity with phone numbers and e-mail addresses stripped.

- **URL**: https://apify.com/borasipahi/us-recalls-monitor.md
- **Developed by:** [Bora Sipahi](https://apify.com/borasipahi) (community)
- **Categories:** Business, News, Lead generation
- **Stats:** 2 total users, 1 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/actors/running/actors-in-store.md#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

US product recalls are announced by two agencies that share nothing: the **FDA**, which publishes enforcement reports for food, drugs and medical devices with a formal hazard class, and **NHTSA**, which publishes vehicle, tyre, equipment and child-seat campaigns with no class at all. Different schemas, different date formats, different vocabulary for the same idea. If you monitor recalls — for a supply chain, a retail catalogue, a compliance dashboard, an insurance book or a newsroom — you either watch four feeds or you buy four scrapers.

This Actor reads all four and returns one flat record per recall: who is recalling, what, why, how serious, how many units, when, and where the official record lives. One schema, one date format, one severity scale.

### What makes it different

- **Four registers, one row shape.** FDA food, FDA drug, FDA device and NHTSA in a single dataset, deduplicated on agency plus recall id, interleaved round-robin so a `maxItems` cap never starves the register that happens to be fetched last.
- **One severity scale across agencies.** FDA grades hazard as Class I, II or III. NHTSA grades nothing, but does flag do-not-drive campaigns and park-outside fire risks. Both are mapped onto `high` / `medium` / `low`, so `urgentOnly` gives you every FDA Class I plus every do-not-drive and fire-risk campaign in one filter. The agency's own `classification` is kept alongside, never overwritten.
- **Contact details stripped by default.** Almost every NHTSA corrective-action paragraph embeds a manufacturer hotline, and free text occasionally carries an individual's e-mail address. Both are replaced with placeholders before anything is saved. Recall-number strings, model years and campaign codes are left intact.
- **Server-side date filtering.** Both registers filter on date at source, so a narrow window costs one request per register rather than a scan.
- **Polite.** One connection, sequential, with backoff, honouring the `Crawl-delay: 1` that `data.transportation.gov` advertises.
- **No browser.** Plain `fetch` against public, keyless JSON APIs. Runs in 512 MB.

### How to use it

1. Pick **Sources**. All four are on by default.
2. Set **Posted within days** (default 30), or give an explicit **Published from / to** window.
3. Narrow with **Search query**, **Firms**, **Product types**, **FDA classifications**, **Severity** or **US states**.
4. For an alert feed, switch on **Urgent only**.
5. Run, then download JSON, CSV or Excel, or read the dataset through the API.

For a daily monitor, turn on **Incremental mode**, add a Schedule, and connect a Slack, e-mail or webhook integration. Only recalls not delivered before are charged and returned.

**One caveat worth knowing before you schedule it:** FDA publishes enforcement reports with roughly a one-to-two-week lag, while NHTSA lands within a few days. A window under 14 days will often return NHTSA rows only. That is the source's cadence, not a gap in the Actor.

### Output fields

| Field | Meaning |
| --- | --- |
| `recallId` | `recall_number` at FDA, `nhtsa_id` at NHTSA. Unique within its source |
| `source`, `sourceDataset`, `agency` | `fda` or `nhtsa`; the exact feed (`fda-food`, `fda-drug`, `fda-device`, `nhtsa`); the agency name |
| `sourceAttribution` | The attribution and disclaimer the register asks for, carried on every row |
| `firm` | Recalling firm (FDA) or manufacturer (NHTSA) |
| `productCategory`, `productType` | `Food`, `Drugs`, `Devices`, `Vehicles`; and the agency's own label (`Vehicle`, `Tire`, `Equipment`, `Child Seat`, …) |
| `productDescriptionSnippet`, `reasonSnippet` | First 300 characters of what was recalled and why |
| `classification` | `Class I`, `Class II`, `Class III` — FDA only, `null` for NHTSA |
| `severity` | `high`, `medium`, `low` — normalised across both agencies |
| `status` | `Ongoing`, `Completed`, `Terminated` — FDA only |
| `doNotDrive`, `fireRiskWhenParked` | Booleans — NHTSA only, `null` for FDA |
| `quantityText`, `unitsAffected` | Quantity as published, plus a parsed integer where one can be read |
| `reportDate`, `initiatedDate`, `classifiedDate`, `terminatedDate` | ISO `YYYY-MM-DD` dates; the last three are FDA only |
| `country`, `state`, `city` | Recalling firm's location. FDA only; NHTSA publishes no address |
| `url` | The official record: the NHTSA recall page, or the openFDA query that returns exactly this enforcement report |
| `scrapedAt` | Run timestamp |
| `productDescription`, `reason`, `consequence`, `correctiveAction`, `codeInfo`, `distributionPattern`, `component`, `mfrCampaignNumber`, `voluntaryMandated`, `initialFirmNotification`, `eventId` | Only when **Include full details** is on |

### Pricing

- `recall-record`, once per recall row saved. Filtered-out, duplicate and already-delivered recalls are never charged.
- `recall-detail`, once per row when **Include full details** is on: untruncated description and reason, the health consequence, the corrective action, affected lot and code ranges, distribution pattern, component and campaign number.

Set **Maximum recalls** to cap a run. No platform usage is passed on to you.

### Input example

```json
{
    "sources": ["fda-food", "fda-drug", "fda-device", "nhtsa"],
    "postedWithinDays": 30,
    "urgentOnly": true,
    "includeFullDetails": true,
    "maxItems": 200,
    "incremental": true
}
```

### Output example

```json
{
    "recallId": "26V583000",
    "source": "nhtsa",
    "sourceDataset": "nhtsa",
    "agency": "NHTSA",
    "firm": "Daimler Trucks North America, LLC",
    "productCategory": "Vehicles",
    "productType": "Vehicle",
    "classification": null,
    "severity": "medium",
    "status": null,
    "country": "United States",
    "state": null,
    "city": null,
    "quantityText": "2404",
    "unitsAffected": 2404,
    "reportDate": "2026-09-09",
    "doNotDrive": false,
    "fireRiskWhenParked": false,
    "url": "https://www.nhtsa.gov/recalls?nhtsaId=26V583000",
    "sourceAttribution": "Source: U.S. Department of Transportation / NHTSA recall database (https://data.transportation.gov/resource/6axg-epim), public domain.",
    "productDescriptionSnippet": "Modulator Valve Corrosion May Cause Brake Pull",
    "reasonSnippet": "Daimler Trucks North America, LLC (DTNA) is recalling certain 2020-2023 Freightliner Cascadia and 2022 Western Star 49X vehicles previously repaired incorrectly under recall numbers 23V073 or 22V817...",
    "correctiveAction": "Dealers will replace the brake modulator valves, free of charge. Owners may contact DTNA customer service at [phone removed].",
    "scrapedAt": "2026-09-13T06:15:13.681Z"
}
```

### Data sources and reuse

| Source | Endpoint | Licence |
| --- | --- | --- |
| FDA enforcement reports | `api.fda.gov/{food,drug,device}/enforcement.json` | Public domain, CC0 1.0 Universal — explicitly cleared for commercial reuse |
| NHTSA recalls | `data.transportation.gov/resource/6axg-epim.json` | Public Domain, U.S. Government |

Both are keyless public APIs. No account, no key, nothing behind a login.

> **FDA disclaimer, reproduced as openFDA asks:** *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.*

Two deliberate exclusions, both on licensing and privacy grounds rather than difficulty:

- **The openFDA device classification and registration endpoints are not used.** They carry GMDN® terms, which are licensed from The GMDN Agency rather than public domain and require a separate licence for commercial reuse. Enforcement reports contain none, and this Actor consumes nothing else.
- **CPSC `saferproducts.gov` complaints are not used.** They contain consumer-submitted narratives and reporter details. This Actor returns recall notices about companies and products, never reports about people.

`data.transportation.gov/robots.txt` permits `/resource/` and asks for a one-second crawl delay, which this Actor honours. `api.fda.gov` publishes no robots.txt.

### Related Actors

- **EU & UK Public Tenders Scraper** — procurement notices from TED, Find a Tender and Contracts Finder in one schema.
- **ATS Jobs Scraper** — every open job from any Greenhouse, Lever or Ashby board.
- **Remote Jobs Scraper** — a deduplicated remote-jobs feed from RemoteOK, Remotive and Himalayas.

### FAQ

**Why does a 7-day window return no FDA rows?**
FDA publishes enforcement reports on a one-to-two-week lag. Use 30 days, which is the default, or read `reportDate` to see the real cadence.

**Why is `severity` sometimes `null`?**
An FDA enforcement report that has not yet been classified carries no class, so there is nothing to map. It fills in once the FDA classifies the recall.

**Can I filter NHTSA by state?**
No. NHTSA publishes no location for a recall campaign, because a vehicle recall is national. The **US states** filter applies to the FDA recalling firm's address, so setting it drops NHTSA rows.

**How do I get only the dangerous ones?**
Switch on **Urgent only**. That is every FDA Class I recall plus every NHTSA do-not-drive or park-outside fire-risk campaign.

**Does the `url` for FDA rows open a web page?**
It opens the openFDA API query that returns exactly that enforcement report. FDA does not publish a stable per-record web page for enforcement reports, so the API permalink is the honest link.

**Will it return the same recall twice on a schedule?**
Not with **Incremental mode** on. Delivered ids are remembered for a year, which comfortably outlasts how long a recall stays open.

# Actor input Schema

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

Which recall registers to read. The three FDA feeds are enforcement reports from the food, drug and device centres; NHTSA covers vehicles, tyres, equipment and child seats.

## `postedWithinDays` (type: `integer`):

How far back to look, counted from today. Ignored when you set an explicit date window below. Note that FDA publishes enforcement reports with roughly a one to two week lag, so windows under 14 days often return NHTSA rows only.

## `publishedFrom` (type: `string`):

Earliest report date, YYYY-MM-DD. Overrides Posted within days.

## `publishedTo` (type: `string`):

Latest report date, YYYY-MM-DD. Defaults to today.

## `searchQuery` (type: `string`):

Comma-separated keywords. A recall is kept if any keyword appears in the firm, product type, product description, reason or consequence.

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

Keep only recalls whose recalling firm or manufacturer name contains one of these, e.g. \["Ford", "Nestle"]. Case-insensitive substring match.

## `productTypes` (type: `array`):

Keep only these product types, matched as substrings against the agency's own label: Food, Drugs, Devices, Vehicle, Tire, Equipment, Child Seat.

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

FDA hazard classes. Class I means a reasonable probability of serious harm or death, Class II temporary or reversible harm, Class III unlikely to cause harm. NHTSA does not publish classes, so setting this drops NHTSA rows.

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

One ranking across both agencies. FDA Class I is high, Class II medium, Class III low. An NHTSA recall is high when it carries a do-not-drive warning or a park-outside fire risk, and medium otherwise.

## `urgentOnly` (type: `boolean`):

Shortcut for severity = high: FDA Class I recalls plus NHTSA do-not-drive and park-outside fire-risk campaigns.

## `states` (type: `array`):

Two-letter state codes matched against the recalling firm's state, e.g. \["CA", "NY"]. FDA rows only; NHTSA does not publish a state, so setting this drops NHTSA rows.

## `includeFullDetails` (type: `boolean`):

Add the untruncated product description and reason, plus the health consequence, the corrective action, the affected lot and code ranges, the distribution pattern, the component and the manufacturer's campaign number. Charged as recall-detail on top of recall-record.

## `stripContactDetails` (type: `boolean`):

Replace e-mail addresses and phone numbers in free text with placeholders. Recall notices routinely embed manufacturer hotlines and occasionally an individual's address. Leave this on unless you have your own reason not to.

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

Stop after this many rows. Bounds your cost.

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

Remember every recall already delivered and skip it next time. Use with a daily schedule to get an alert feed instead of a full dump.

## `incrementalStoreName` (type: `string`):

Key-value store that holds delivered recall ids.

## Actor input object example

```json
{
  "sources": [
    "fda-food",
    "fda-drug",
    "fda-device",
    "nhtsa"
  ],
  "postedWithinDays": 30,
  "firms": [],
  "productTypes": [],
  "classifications": [],
  "severities": [],
  "urgentOnly": false,
  "states": [],
  "includeFullDetails": false,
  "stripContactDetails": true,
  "maxItems": 100,
  "incremental": false,
  "incrementalStoreName": "us-recalls-monitor-seen"
}
```

# Actor output Schema

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

No description

## `recallsCsv` (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": [
        "fda-food",
        "fda-drug",
        "fda-device",
        "nhtsa"
    ],
    "postedWithinDays": 30,
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("borasipahi/us-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": [
        "fda-food",
        "fda-drug",
        "fda-device",
        "nhtsa",
    ],
    "postedWithinDays": 30,
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("borasipahi/us-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": [
    "fda-food",
    "fda-drug",
    "fda-device",
    "nhtsa"
  ],
  "postedWithinDays": 30,
  "maxItems": 100
}' |
apify call borasipahi/us-recalls-monitor --silent --output-dataset

```

## MCP server setup

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