# Product Recall Feed — FDA, NHTSA & CPSC Recalls API (`owlabee/product-recall-feed`) Actor

US recalls from FDA, NHTSA and CPSC, plus opt-in Canada, UK and EU Safety Gate, as one normalized JSON record with jurisdiction and hazard categories. Filter by date, keyword, firm or vehicle; get only new recalls since the last run by webhook. Pay per recall record.

- **URL**: https://apify.com/owlabee/product-recall-feed.md
- **Developed by:** [Owlabee](https://apify.com/owlabee) (community)
- **Categories:** Automation, News
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 recall records

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

## Product Recall Feed — FDA, NHTSA & CPSC Recalls API

**Product recall feed** for the US: FDA drug, device and food recalls, NHTSA vehicle recalls and CPSC consumer product recalls from the agencies' official APIs, in one normalized JSON record per recall. Opt in to **Canada** (Health Canada, with CFIA food and Transport Canada), the **UK** (OPSS, MHRA, FSA) and the **EU Safety Gate**, all in the same record with a jurisdiction, product category and hazard categories. Filter by date, keyword, firm or vehicle, and turn on **Only new recalls since last run** for a daily recall alert by webhook.

### What it does

This Actor pulls **US product recalls** from the three federal agencies that publish them and writes every recall into **one normalized JSON record**: agency, recall id, date, title, product, product type, hazard or reason, classification and severity, recalling firm, distribution, remedy, units affected and a link to the official notice. The untouched agency fields can be attached as a nested `raw` object.

- **FDA** (openFDA enforcement reports): drugs, medical devices, and food and cosmetics, with the Class I / II / III classification.
- **NHTSA**: vehicle, equipment, tire and child-seat recall campaigns, with "do not drive" and "park outside" advisories, plus every recall on a specific make, model and year.
- **CPSC**: consumer product recalls (toys, appliances, furniture, e-bikes, batteries...), with hazards, injuries, remedies and retailers.

Those five feeds are the default. These run only when you pick them in **Agencies** or choose their country in **Jurisdictions**:

- **FDA device recall database** (`fda-device-recall`, openFDA): device recalls with the root cause FDA assigned, the firm's corrective action, product codes and 510(k) numbers.
- **FDA recall press releases** (`fda-rss`): the company announcements FDA posts, often weeks before the enforcement report classifies the recall (the feed holds the newest 20).
- **Health Canada** (`health-canada`): every Canadian recall and safety alert in Health Canada's daily open data, including CFIA food recalls, Transport Canada vehicle recalls, health products and consumer products.
- **UK** (`uk-opss`, `uk-mhra`, `uk-fsa`): OPSS product safety reports and recalls, MHRA drug and device alerts, and Food Standards Agency allergy alerts and recall notices.
- **EU Safety Gate** (`eu-safety-gate`): the European Commission's weekly reports of dangerous non-food products notified by EU/EEA authorities, with barcodes, batch and model numbers and product photos.

Every record, whatever its source, also carries `jurisdiction` (US, CA, GB or EU), `recordType` (recall, alert or announcement), `productCategory` and `hazardCategories` from one shared taxonomy, so one filter works across countries.

It calls the agencies' official public APIs directly — no browser, no proxies, no login — so runs take seconds. Filter by date window, keyword, firm or manufacturer, product type, firm state and FDA class, and turn on **Only new recalls since last run** to monitor: schedule it daily and only recalls not seen before are written, charged and sent to your webhook. Results are available as JSON, CSV, Excel, via the Apify API, webhooks and the Apify MCP server.

Built for compliance and quality teams, retailers and marketplaces checking their catalog, pharmacies and hospitals, fleet managers and dealers, insurers, product-safety lawyers, and journalists who track recalls across agencies instead of one agency at a time.

### Why this Actor

| | This Actor | Typical recall scraper in this category | Do it yourself |
|---|---|---|---|
| Data sources covered | FDA drugs, devices and food, NHTSA campaigns and vehicle lookups, and CPSC by default; opt-in Health Canada, UK OPSS, MHRA and FSA, EU Safety Gate, the FDA device recall database and FDA press releases. All official, verified live 2026-11-01 and 2026-11-07 | One agency (FDA or NHTSA or CPSC) per Actor | Three APIs with three query languages, three date formats and three pagination schemes |
| Modes | One-off feed with date, keyword, firm, type, state and class filters; changes mode that emits only recalls not seen before | Search or export; new-only is rare | Keep your own seen-set per agency |
| Output fields | One normalized record across agencies (severity included), plus the raw agency fields on request | The agency's own fields, different per Actor | Whatever you map |
| Pricing unit | Per recall record and per agency API request; an error item costs the same as a recall | Per result, one agency per run | Your time plus compute; the APIs are free |
| Alerts | Webhook with the new recalls and counts per agency and severity, sent only when something is new | Rare | You build it |
| Proxies or browsers needed | None | Sometimes browser automation of the agency website | None |
| Agent / MCP ready | Yes: pay-per-event, limited permissions, pin link, prompts and a call example below | Sometimes | You write and host the tool definition |

- One schedule replaces three: a daily run over all five feeds costs a few cents when nothing is new.
- `severity` puts FDA classes and NHTSA advisories on one scale, and the webhook carries `highestSeverity`, so a single field decides whether to page someone.
- Every API request is a visible charge, and an agency outage becomes one charged error item instead of a failed run.
- The quick-start input is the Store's daily automated test against all three agencies, so breakage is caught the same day.

### How to use

1. Pick the **Agencies** (the five US feeds by default) and optionally add **Jurisdictions** (CA, GB, EU), then set **Recalls from the last N days** or a **Date from / Date to** range.
2. Optionally narrow with a **Keyword** (`salmonella`, `airbag`, `lithium battery`), a **Firm or manufacturer**, a **Product type**, a **Firm state** or **FDA classifications**, or add **Vehicles** as `make, model, year`.
3. Click **Start**. Recalls appear in the dataset as each agency is paged. For alerts, schedule the Actor with **Only new recalls since last run** and a **Webhook URL**.

#### Quick start input

The prefilled input (the Store's daily test) returns the 5 newest recalls from each feed of the last 30 days in about 3 seconds for about $0.07:

```json
{
  "agencies": ["fda-drug", "fda-device", "fda-food", "nhtsa", "cpsc"],
  "daysBack": 30,
  "maxResultsPerSource": 5
}
```

#### Ready-to-run inputs

Daily Class I food and drug recall alert (schedule daily; the first run is the baseline):

```json
{
  "agencies": ["fda-drug", "fda-food"],
  "daysBack": 14,
  "fdaClassifications": ["Class I"],
  "onlyNewSinceLastRun": true,
  "webhookUrl": "https://example.com/hooks/recalls"
}
```

Every recall on a fleet's vehicles:

```json
{
  "agencies": [],
  "vehicles": ["Ford, F-150, 2021", "Toyota, Camry, 2020", "Honda, Accord, 2019"]
}
```

Canada, the UK and the EU only, last 14 days, as a daily changes-mode alert:

```json
{
  "agencies": [],
  "jurisdictions": ["CA", "GB", "EU"],
  "daysBack": 14,
  "onlyNewSinceLastRun": true,
  "webhookUrl": "https://example.com/hooks/recalls"
}
```

FDA device recalls with root cause and corrective action, next to the enforcement reports:

```json
{
  "agencies": ["fda-device", "fda-device-recall"],
  "daysBack": 60,
  "keyword": "software"
}
```

Lithium battery hazards across agencies over the last year:

```json
{
  "agencies": ["cpsc", "nhtsa", "fda-device"],
  "daysBack": 365,
  "keyword": "lithium",
  "maxResultsPerSource": 500
}
```

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `agencies` | string\[] | no | Default feeds `fda-drug`, `fda-device`, `fda-food`, `nhtsa`, `cpsc` (default all five). Opt-in feeds `fda-device-recall`, `fda-rss`, `health-canada`, `uk-opss`, `uk-mhra`, `uk-fsa`, `eu-safety-gate`. Empty means the default five, or only the chosen `jurisdictions`. |
| `jurisdictions` | string\[] | no | `US`, `CA`, `GB`, `EU`: adds every feed of each (`US` adds the two opt-in FDA feeds to the default five). Leave `agencies` empty to query only these. |
| `daysBack` | integer | no | Window ending today or on `dateTo` (1–3650). Default 30. |
| `dateFrom` / `dateTo` | string | no | Explicit window, `YYYY-MM-DD`, inclusive. `dateFrom` overrides `daysBack`. |
| `keyword` | string | no | Product, description or hazard text. Searched by the FDA, NHTSA and GOV.UK APIs, matched locally for the other feeds. |
| `firm` | string | no | Recalling firm, manufacturer, importer or distributor contains this text. |
| `productType` | string | no | NHTSA recall type or component (`Tire`, `Child Seat`, `Air Bags`); CPSC product category (`Bicycles`); the agency's category for the other non-FDA feeds (`Toys and games`, `Cosmetics`). Ignored for FDA. |
| `state` | string | no | Two-letter state of the recalling firm. FDA enforcement and device recall feeds only (the other agencies do not publish it). |
| `fdaClassifications` | string\[] | no | `Class I`, `Class II`, `Class III`. FDA enforcement feeds only (the device recall database and press releases publish no class). |
| `vehicles` | string\[] | no | NHTSA lookups, one per line: `make, model, year`. Each returns every recall on that vehicle, whatever the window and filters. |
| `maxResultsPerSource` | integer | no | Cap per feed, newest first (changes mode: new recalls). Default 200, max 25,000. |
| `maxSecondsPerSource` | integer | no | Time budget per feed (default 300, 30–1800); see "Limits and fair use". |
| `onlyNewSinceLastRun` | boolean | no | Changes mode: emit only recalls not seen in earlier runs, flagged `isNew: true`. |
| `stateStoreName` | string | no | Named key-value store holding the seen-sets. Default `product-recall-feed-state`. |
| `notifyOnlyOnNew` | boolean | no | Skip the webhook when nothing was emitted. Default true. |
| `includeRaw` | boolean | no | Attach the agency's own fields as `raw`. |
| `fdaApiKey` | string (secret) | no | Free openFDA key; only raises rate limits. |
| `webhookUrl` | string | no | POST target for the run summary and the emitted recalls (max 1,000). |

### Output example

```json
{
  "agency": "CPSC",
  "source": "cpsc",
  "recallId": "27055",
  "date": "2026-10-29",
  "title": "Bodum USA Recalls 5.9 Million French Press Coffee Makers with Replaceable Glass Carafes Due to Risk of Serious Burn Injury",
  "product": "Bodum French Press coffee makers with 8-cup and 12-cup replaceable glass carafes (Bistro, Brazil, Caffettiera, Chambord, Crema, Java, Jesper, and Kenya models)",
  "productType": "Unpowered Coffee Makers or Teapots",
  "description": "This recall involves Bodum 8-cup and 12-cup Bistro, Brazil, Caffettiera, Chambord, Crema, Java, Jesper, and Kenya model French Press coffee makers with replaceable glass carafes. ...",
  "hazard": "The carafe can unexpectedly break during use if the glass carafe is damaged or cracked, including by use of a metal spoon to stir the coffee, posing a risk of serious burn injuries.",
  "classification": "Repair",
  "severity": null,
  "firm": "Bodum USA, Inc., of New York, New York",
  "distribution": "Target, Walmart, and coffee and home goods stores nationwide and online at Bodum.com, Amazon.com, Target.com, Walmart.com and other online retailers from 1974 through October 2026 for about $15 to $65.",
  "remedy": "Consumers should stop using metal spoons or utensils to stir coffee in the glass carafe immediately and contact Bodum to receive a plastic Bodum stirring spoon and warning label. ...",
  "status": null,
  "quantity": "5.9 million",
  "state": null,
  "country": "Portugal; Thailand",
  "url": "https://cpsc.gov/Recalls/2027/Bodum-USA-Recalls-5-9-Million-French-Press-Coffee-Makers-with-Replaceable-Glass-Carafes-Due-to-Risk-of-Serious-Burn-Injury",
  "fetchedAt": "2026-11-01T12:55:01.288Z"
}
```

| Field | FDA | NHTSA | CPSC |
|---|---|---|---|
| `recallId` | recall number (`D-0041-2027`) | campaign number (`26V714000`) | recall number |
| `date` | report date | report received date | recall date |
| `hazard` | reason for recall | consequence summary | hazards |
| `classification` | Class I / II / III | "Do not drive", "Park outside" | remedy options (Refund, Repair, Replace) |
| `severity` | Class I high, II medium, III low | high with an advisory, else medium | null (CPSC publishes no class) |
| `remedy` | null (not in enforcement reports) | corrective action | remedies |
| `distribution` | distribution pattern | null | retailers ("sold at") |
| `url` | openFDA record query | nhtsa.gov recall page | cpsc.gov recall page |

#### Fields shared by every source

Added on 2026-11-07 to every record, including the five default feeds (no existing field changed meaning). An EU Safety Gate alert:

```json
{
  "agency": "EC",
  "source": "eu-safety-gate",
  "recallId": "SR/03088/26",
  "date": "2026-11-04",
  "title": "Tablecloth: Solei d'Ocre Nappe Rectangulaire 140x300 cm, Anti-tâches, imperméable",
  "productType": "Decorative articles",
  "hazard": "Chemical: The product has an excessive concentration of DEHP (measured value: 27 % by weight). ...",
  "classification": "Serious risk",
  "severity": "high",
  "firm": "Solei d'Ocre",
  "distribution": "Notified by Luxembourg",
  "remedy": "Type of economic operator to whom the measure(s) were ordered: Retailer; Category of measure(s): Withdrawal of the product from the market; ...",
  "country": "People's Republic of China",
  "url": "https://ec.europa.eu/safety-gate-alerts/screen/webReport/alertDetail/10118629",
  "jurisdiction": "EU",
  "language": "en",
  "recordType": "alert",
  "productCategory": "consumer-product",
  "productSubcategory": "other",
  "hazardCategories": ["chemical", "regulatory-violation"],
  "identifiers": { "gtin": ["3418178586009"], "lot": ["86NG589"], "model": ["052025, Crystal transparent 100 microns"] },
  "imageUrls": ["https://ec.europa.eu/safety-gate-alerts/public/api/notification/image/11240816"],
  "fetchedAt": "2026-11-07T18:11:40.084Z"
}
```

| Field | Values |
|---|---|
| `jurisdiction` | `US`, `CA`, `GB` or `EU` (the publishing authority) |
| `language` | `en`; FDA's Spanish press releases are `es` (text is never machine-translated) |
| `recordType` | `recall`; `alert` for a safety report or advisory (OPSS safety reports, MHRA notices, FSA food alerts for action, Health Canada warnings, Safety Gate); `announcement` for an FDA press release |
| `productCategory` | `food`, `drug`, `medical-device`, `cosmetic`, `tobacco`, `vehicle`, `vehicle-equipment`, `consumer-product` (or null when the source gives too little to tell) |
| `productSubcategory` | Second level where it can be read: food (`meat-poultry`, `dairy`, `produce`, `infant`, `supplements`, `animal-food`), vehicle (`car`, `truck`, `bus`, `motorcycle`, `rv`), equipment (`tire`, `child-seat`, `parts`), consumer products (`toys-children`, `electrical-electronics`, `batteries-e-mobility`, `furniture`, `appliances`, `clothing-textiles`, `sports-recreation`, `household-chemicals`, `jewelry`, `other`) |
| `hazardCategories` | One or more of `injury`, `burns`, `fire`, `electric-shock`, `choking`, `suffocation`, `strangulation`, `entrapment`, `cuts`, `drowning`, `fall`, `crash`, `loss-of-control`, `hearing`, `sight`, `chemical`, `carbon-monoxide`, `explosion`, `pathogen`, `undeclared-allergen`, `foreign-matter`, `contamination`, `spoilage-process-failure`, `sterility`, `potency`, `impurity`, `device-malfunction`, `software`, `mislabeling`, `regulatory-violation`, `environment`, `other`. Read by a fixed keyword table from the agency's own text (no AI), plus Safety Gate's published risk types; the agency's words stay in `hazard` |
| `pathogens` | Named pathogens when `hazardCategories` has `pathogen`: `listeria`, `salmonella`, `e-coli`, `clostridium-botulinum`, ... |
| `identifiers` | Where published: `gtin` (barcodes: Safety Gate, CPSC UPCs), `lot`, `model`, `productCode` and `kNumber` (FDA device recall database) |
| `imageUrls` | Product photos (Safety Gate, CPSC) |
| `rootCause` | FDA device recall database only: FDA's root cause category |

How the opt-in feeds fill the existing fields:

| Feed | `recallId` | `date` | `classification` → `severity` | `firm` |
|---|---|---|---|---|
| `fda-device-recall` | product recall number (`Z-0326-2027`, same as the enforcement report) | date posted | none (null) | recalling firm |
| `fda-rss` | page slug | press release date | none (null) | from the title |
| `health-canada` | Health Canada id | last updated (the only date the open data publishes; items without one are skipped) | Class 1 / Type I high, 2 / II medium, 3 / III low; advisories null | not published (null) |
| `uk-opss` | OPSS reference (`2610-0149`) | alert date | risk level: serious and high = high, medium, low | not published (null) |
| `uk-mhra` | alert reference (`EL(26)A/45`, `DSI/2026/013`) | issued date | Class 1 high, 2 medium, 3 and 4 low; device notices null | from the title |
| `uk-fsa` | FSA notation (`FSA-AA-51-2026`) | created | FAFA and AA (allergy) high, PRIN medium | reporting business |
| `eu-safety-gate` | case number (`SR/03088/26`) | alert publication date | Serious risk high, other levels medium | brand |

Dates are ISO `YYYY-MM-DD`. In changes mode every emitted recall carries `isNew: true`. A feed that cannot be queried produces one error item instead of failing the run: `{ "agency": "CPSC", "source": "cpsc", "error": "FETCH_FAILED", "errorMessage": "...", "fetchedAt": "..." }` (codes: `FETCH_FAILED`, `SOURCE_ERROR`, `TIME_BUDGET`, `INVALID_VEHICLE`).

If the output saves you time, a review on the Store page helps other buyers find this Actor; the Store invites you to leave one after you have run it a few times.

### Alerts and delivery

Changes mode is built for a daily schedule; this is how new recalls reach your inbox, Slack or ticketing system.

- **Schedule in the Console.** Save the input (with `onlyNewSinceLastRun: true`) as a Task and attach it to a daily schedule under **Schedules**. FDA publishes enforcement reports weekly and NHTSA and CPSC daily, so keep `daysBack` at 14 or more so late-published recalls still fall inside the window. Use one `stateStoreName` per schedule when different filters run side by side.
- **The Actor's own webhook.** Set `webhookUrl` and the Actor POSTs one JSON document at the end of a run that emitted recalls (every run when `notifyOnlyOnNew` is false; 10 second timeout, two retries; a failed delivery is recorded as `webhookDelivered: false` in the `SUMMARY` record and never fails the run). The body is `{ actor, runId, mode, window, filters, sources, totalRecalls, byAgency, bySeverity, highestSeverity, charges, recalls: [...], recallsTruncated }`.
- **Apify run webhooks.** On the Actor's **Integrations** tab add a webhook for `ACTOR.RUN.SUCCEEDED` or `ACTOR.RUN.FAILED`; Apify POSTs the run id and dataset id to your URL.
- **Slack, Make, Zapier, Google Sheets, email.** The same **Integrations** tab lists Apify's ready-made integrations: a Slack message per run, Make and Zapier modules, an export to a Google Sheet, and email notifications.
- **API and dataset endpoints.** Start a run with `POST https://api.apify.com/v2/acts/owlabee~product-recall-feed/runs` and read the newest results with `GET https://api.apify.com/v2/acts/owlabee~product-recall-feed/runs/last/dataset/items?format=json` (also `csv` or `xlsx`).

### Sources

| Source id | Agency feed | Official API | Freshness |
|---|---|---|---|
| `fda-drug` | FDA drug enforcement reports | `api.fda.gov/drug/enforcement.json` (openFDA) | Weekly |
| `fda-device` | FDA medical device enforcement reports | `api.fda.gov/device/enforcement.json` | Weekly |
| `fda-food` | FDA food and cosmetics enforcement reports | `api.fda.gov/food/enforcement.json` | Weekly |
| `nhtsa` | NHTSA recall campaigns (vehicles, equipment, tires, child seats) | DOT open data `datahub.transportation.gov` dataset `6axg-epim`, the NHTSA recalls database | Daily |
| `vehicles` input | NHTSA recalls for a make, model and year | `api.nhtsa.gov/recalls/recallsByVehicle` | Daily |
| `cpsc` | CPSC consumer product recalls | `www.saferproducts.gov/RestWebServices/Recall` | Daily |
| `fda-device-recall` (opt-in) | FDA medical device recall database | `api.fda.gov/device/recall.json` (openFDA) | Weekly |
| `fda-rss` (opt-in) | FDA recalls, market withdrawals and safety alerts (company press releases) | FDA recalls RSS feed on fda.gov | Daily; newest 20 |
| `health-canada` (opt-in) | Health Canada recalls and safety alerts, incl. CFIA and Transport Canada | Open data JSON on `recalls-rappels.canada.ca` (one 15.9 MB file) | Daily |
| `uk-opss` (opt-in) | UK Office for Product Safety and Standards reports and recalls | GOV.UK Search API `www.gov.uk/api/search.json` | Daily |
| `uk-mhra` (opt-in) | UK MHRA drug and medical device alerts | GOV.UK Search API | Daily |
| `uk-fsa` (opt-in) | UK Food Standards Agency allergy alerts, recall notices and food alerts for action | `data.food.gov.uk/food-alerts` | Daily |
| `eu-safety-gate` (opt-in) | EU Safety Gate alerts (dangerous non-food products, EU/EEA) | Weekly report XML on `ec.europa.eu/safety-gate-alerts` | Weekly (Fridays) |

What one agency API request is for the opt-in feeds: one openFDA page of up to 100 device recalls, the RSS feed, the Health Canada file (one request, however many recalls it holds; the download itself is one bulk data file download on top, see Pricing), one GOV.UK or FSA page of up to 100 alerts, the Safety Gate list of weekly reports, and each weekly report in the window.

Attribution (keep it with any redistribution): contains information licensed under the Open Government Licence – Canada (Health Canada, CFIA, Transport Canada). Contains public sector information licensed under the Open Government Licence v3.0 (OPSS, MHRA, FSA). EU Safety Gate data © European Union, published by the European Commission and reusable under Commission Decision 2011/833/EU (CC BY 4.0).

### Pricing

Pay per event. You only pay for what the Actor produces; set a **maximum cost per run** in the run options to cap spend.

| Event | Price | Notes |
|---|---|---|
| Recall record (`recall-record`) | $0.002 | Per dataset item: a recall from any agency, a vehicle recall, or an error item for a feed that failed |
| Agency API request (`agency-page-fetched`) | $0.004 | Per request to an agency API: an FDA page of up to 100 recalls, an NHTSA page of up to 500, one CPSC 90-day window, one vehicle lookup, one GOV.UK or FSA page, the Safety Gate list or one weekly report, the FDA RSS feed, or the request for the Health Canada file; including empty, no-match, already-seen and failed requests |
| Bulk data file download (`bulk-file-fetched`) | $0.01 | Per download attempt of a whole-dataset file, today the Health Canada open-data file (about 16 MB; opt-in feed `health-canada` or jurisdiction `CA`), on top of the API request for it; also charged when the download times out, never when the file is unchanged (304 in changes mode) or the request is refused. The default US feeds never charge it |
| Actor start | $0.00005 | Apify platform event |

**Examples:** the quick-start input (25 recalls, 5 requests) ≈ **$0.07**; 1,000 FDA drug recalls = $2.00 + 10 pages $0.04 ≈ **$2.04**; a daily changes-mode check of all five feeds with a 14-day window and nothing new ≈ **$0.02**, plus $0.002 per new recall. 1,000 recalls ≈ $2. A daily Health Canada check with nothing new ≈ **$0.014** (one request plus one file download), or $0.004 when the file has not changed since the last run; Canada, the UK and the EU together on a quiet day ≈ **$0.05**.

Store pricing discounts: users on the Scale plan (Silver) pay 10% less per event, Business (Gold) 20% less on recall records and 15% less on API requests and bulk downloads.

### Limits and fair use

- At most 2 requests per second per agency host, exponential backoff on 429/5xx, three feeds queried in parallel.
- **Time budget per source.** Each feed gets `maxSecondsPerSource` seconds (default 300) and is abandoned earlier, with a charged `TIME_BUDGET` error item, when 60 seconds pass without a successful response. Every request, retries included, is limited to 30 seconds, and a `Retry-After` longer than 10 seconds is not waited for. A feed cut off after producing recalls keeps them.
- openFDA allows 240 requests per minute and 1,000 per day per IP without a key and pages up to 25,100 records per query; add a free `fdaApiKey` for heavy daily use.
- CPSC has no server-side keyword or firm search, so the whole date window is downloaded (one request per 90 days, about 0.45 MB) and filtered locally.
- **Health Canada** publishes one 15.9 MB file. It is streamed, never held whole: only items inside the date window are parsed, and because the file is newest first the download stops 45 days past the window's start (a 30-day window reads about 0.25 MB). Bulk downloads run one at a time, are capped at 32 MB and 60 seconds, and in changes mode are skipped with a conditional request when the file has not changed since the last complete run (the validators are kept in the state store; the 304 costs one API request and no download). Each download attempt is one bulk data file download event ($0.01) on top of the API request, charged when the file starts arriving or when the download times out. Peak memory measured with every item of a 10-year window kept: about 210 MB.
- **EU Safety Gate** is read week by week: one request for the list of weekly reports plus one per week in the window, so long windows cost more requests (about 52 per year).
- Default run memory is 512 MB.
- Coverage is what each agency publishes: FDA enforcement reports list recalls after FDA classifies them (often weeks after the firm's press release); NHTSA and CPSC publish within days.

### Related Actors

Other pay-per-event Actors from the same account, all limited-permission and agent-friendly:

- [Grants.gov API & SAM.gov Scraper — Federal Grants Feed](https://apify.com/owlabee/grants-gov-api) — federal grants, SAM.gov notices and USAspending awards with new/updated flags.
- [Website Change Monitor](https://apify.com/owlabee/website-change-monitor) — watch a manufacturer's or retailer's recall page for changes.
- [License Verification API — TDLR, DBPR, DORA, WA DOH, NPI](https://apify.com/owlabee/license-verification-api) — check licenses of the providers and firms behind a recall.

### Guide and code examples

- Guide: [Product Recalls API: FDA, NHTSA and CPSC in One Feed](https://owlabee.com/product-recalls-api/), covering the source, its fields and limits, and curl and MCP examples.
- No-code recipe: [FDA, NHTSA and CPSC Product Recalls to Email with n8n](https://owlabee.com/product-recalls-email-n8n/), step by step with Apify's official n8n app and a ready input.
- Runnable examples (curl, Python, Node.js and an MCP config): [owlabee/owlabee on GitHub](https://github.com/owlabee/owlabee/tree/main/examples/product-recall-feed).

### Use from AI agents (MCP)

Pin this Actor in the Apify MCP server with one link: `https://mcp.apify.com?tools=owlabee/product-recall-feed`, or let the agent find it with the `search-actors` tool. It is pay-per-event with limited permissions. Prompts that map directly onto the input:

1. "List this month's Class I FDA food and drug recalls with the firm, the reason and the distribution."
2. "Every morning, send new recalls from FDA, NHTSA and CPSC that mention lithium batteries to my webhook."
3. "Are there any open recalls on a 2021 Ford F-150, a 2020 Toyota Camry or a 2019 Honda Accord?"

To call it from an agent that already has the Apify MCP server connected, copy this tool call and change only the `input` object:

```json
{
  "tool": "call-actor",
  "arguments": {
    "actor": "owlabee/product-recall-feed",
    "input": {
      "agencies": ["fda-drug", "fda-device", "fda-food", "nhtsa", "cpsc"],
      "daysBack": 30,
      "maxResultsPerSource": 5
    }
  }
}
```

### FAQ

**Does this need proxies or an API key?** No. It calls the agencies' public APIs over plain HTTPS. An openFDA key is optional and only raises openFDA's rate limits.

**How is cost controlled?** Set `maxResultsPerSource`, narrow the window and filters, and set the run's maximum total charge; the Actor stops cleanly when any limit is reached. In changes mode you pay per new recall plus one charge per API request.

**Why is `remedy` empty for FDA recalls?** FDA enforcement reports do not publish a remedy field; the reason, classification, distribution and quantity are there. Turn on `includeRaw` to see every field the agency publishes.

**What does the first changes-mode run do?** It is the baseline: every recall in the window is written and charged, and remembered. Later runs write only recalls not seen before.

**Why does a recall I saw in the news not appear yet?** FDA lists a recall in its enforcement report after classifying it, which can take weeks after the firm's announcement. Use a wider `daysBack` (30 or more) for FDA.

**What happens when an agency API is down?** That feed yields one error item (charged like a recall) and the other feeds continue, so a schedule never fails on one agency's outage.

**Which countries are covered?** The US by default; Canada, the United Kingdom and the EU/EEA when you choose them in `jurisdictions` or pick their feeds in `agencies`. Each record says where it came from in `jurisdiction` and `agency` (FDA, NHTSA, CPSC, HC, CFIA, TC, OPSS, MHRA, FSA, EC). Australia, France and Transport Canada's vehicle lookups are not built yet.

**Do the new fields change my existing results?** No existing field changed. Every record gained `jurisdiction`, `language`, `recordType`, `productCategory`, `productSubcategory` and `hazardCategories`, and some records gained `pathogens`, `identifiers` or `imageUrls`. Saved inputs query the same five feeds as before.

**Can I try it for free?** Yes. The Apify free plan comes with monthly platform credit, and the quick-start input costs a few cents.

### Support

Questions, bugs or a source you'd like added? Open an issue on the Actor's **Issues** tab, or email jacob@owlabee.com.

### Changelog

- **2026-11-08** — Pricing: new `bulk-file-fetched` event ($0.01; Silver $0.009, Gold $0.0085) per download attempt of the Health Canada open-data file, charged on top of the API request for it, never on a 304 or a refusal. The default US feeds and every saved input that does not select Health Canada cost exactly what they did before; the `agency-page-fetched` description now lists the opt-in request shapes.
- **2026-11-07** — Opt-in feeds: Health Canada (with CFIA and Transport Canada), UK OPSS, MHRA and FSA, EU Safety Gate, the FDA device recall database and FDA recall press releases, plus a `jurisdictions` input. New fields on every record: `jurisdiction`, `language`, `recordType`, `productCategory`, `productSubcategory`, `hazardCategories`, and where published `pathogens`, `identifiers`, `imageUrls` and `rootCause`. The default five feeds and their existing fields are unchanged.
- **2026-11-01** — First release: FDA drug, device and food enforcement reports, NHTSA recall campaigns and vehicle lookups, CPSC recalls in one normalized record; date, keyword, firm, product type, state and class filters; changes mode with a seen-set per feed; webhook alerts with counts per agency and severity; time budget per source and charged error items.

### Legal

The five default feeds come from public US government APIs (openFDA, NHTSA via DOT open data and api.nhtsa.gov, and CPSC SaferProducts) that are free to use and need no login. openFDA's own disclaimer applies: do not rely on openFDA to make decisions regarding medical care, and assume results are unvalidated. The opt-in feeds are published under open licences whose attribution must stay with the data: Health Canada, CFIA and Transport Canada recalls contain information licensed under the Open Government Licence – Canada; OPSS, MHRA and Food Standards Agency records contain public sector information licensed under the Open Government Licence v3.0; EU Safety Gate alerts are © European Union, published by the European Commission and reusable under Commission Decision 2011/833/EU (CC BY 4.0), with the Commission's Safety Gate named as the source. Firm names are reproduced as published by the agency; the records describe products and firms, not people. This Actor is not affiliated with or endorsed by FDA, NHTSA, CPSC, Health Canada, CFIA, Transport Canada, OPSS, MHRA, the Food Standards Agency or the European Commission; agency names identify the source of each record. Use the data in compliance with each agency's terms and applicable law.

# Actor input Schema

## `agencies` (type: `array`):

Recall feeds to query. The default five are the US federal feeds: FDA drug, device and food enforcement reports (openFDA), NHTSA vehicle and equipment recall campaigns, and CPSC consumer product recalls; the prefilled value queries all five and is the cheap example used by Apify's daily automated test. The other feeds run only when you pick them here or choose their jurisdiction below: the openFDA device recall database (root cause and corrective action), FDA recall press releases (RSS), Health Canada (all Canadian recalls incl. CFIA food and Transport Canada), UK OPSS product safety, UK MHRA drug and device alerts, UK FSA food alerts, and the EU Safety Gate.

## `jurisdictions` (type: `array`):

Add every feed of a jurisdiction: US (the five default feeds plus the FDA device recall database and FDA press releases), CA (Health Canada), GB (OPSS, MHRA, FSA) or EU (Safety Gate). Combined with Agencies; leave Agencies empty to query only the chosen jurisdictions.

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

Date window ending today (or on Date to). Ignored when Date from is set. FDA publishes enforcement reports weekly, so keep at least 14 days for FDA.

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

Start of the window, YYYY-MM-DD (inclusive). Overrides Recalls from the last N days.

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

End of the window, YYYY-MM-DD (inclusive). Default today.

## `keyword` (type: `string`):

Keep recalls whose product, description or hazard mentions this text, e.g. "salmonella", "airbag", "lithium battery". Searched by the FDA, NHTSA and GOV.UK APIs, matched locally for the other feeds.

## `firm` (type: `string`):

Keep recalls whose recalling firm, manufacturer, importer or distributor contains this text, e.g. "Ford" or "Teva".

## `productType` (type: `string`):

NHTSA: recall type or component ("Tire", "Child Seat", "Air Bags"). CPSC: product category ("Coffee Makers", "Bicycles"). Other non-FDA feeds: the agency's own category (Health Canada "Toys and games", Safety Gate "Cosmetics"). Ignored for FDA (each FDA feed is already one product type).

## `state` (type: `string`):

Two-letter US state of the recalling firm, e.g. "CA". Applies to the FDA enforcement and device recall feeds; the other agencies do not publish it.

## `fdaClassifications` (type: `array`):

Keep only these FDA recall classes (FDA enforcement feeds). Class I is the most serious (reasonable probability of serious harm or death). The FDA device recall database and press releases publish no class and ignore this filter.

## `vehicles` (type: `array`):

Look up every recall for specific vehicles, one per line as "make, model, year" (e.g. "Honda, Accord, 2020"). Each lookup returns all recalls on that vehicle, whatever the date window and filters.

## `maxResultsPerSource` (type: `integer`):

Upper bound on recalls written per agency feed, newest first (in changes mode: new recalls). Use it together with the run's maximum cost to control spend.

## `maxSecondsPerSource` (type: `integer`):

Wall time one agency feed may take (Health Canada's 15.9 MB daily file typically downloads and parses in a few seconds). A source is also abandoned, with a charged error item, after 60 seconds without a successful response.

## `onlyNewSinceLastRun` (type: `boolean`):

Changes mode for monitoring: emit only recalls not seen in earlier runs (flagged isNew). The first run is a baseline that emits and charges every recall in the window. Schedule it daily with a webhook for alerts.

## `stateStoreName` (type: `string`):

Named key-value store that remembers which recalls were already seen (per agency feed, up to 50,000 each). Use one store per schedule when different filters run side by side.

## `notifyOnlyOnNew` (type: `boolean`):

Skip the webhook call when the run emitted no recalls (the usual quiet day in changes mode).

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

Attach every field the agency published as a nested raw object (FDA openfda codes, CPSC images and UPCs, NHTSA flags, Safety Gate fields).

## `fdaApiKey` (type: `string`):

Free key from open.fda.gov; only raises openFDA's rate limits (240 requests per minute and 1,000 per day per IP without one).

## `webhookUrl` (type: `string`):

POST target for a JSON alert at the end of the run: the run summary with counts per agency and severity plus the emitted recalls (max 1,000). Failures are logged, never fatal.

## Actor input object example

```json
{
  "agencies": [
    "fda-drug",
    "fda-device",
    "fda-food",
    "nhtsa",
    "cpsc"
  ],
  "daysBack": 30,
  "maxResultsPerSource": 5,
  "maxSecondsPerSource": 300,
  "onlyNewSinceLastRun": false,
  "stateStoreName": "product-recall-feed-state",
  "notifyOnlyOnNew": true,
  "includeRaw": false
}
```

# Actor output Schema

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

One item per recall in the default dataset (agency, source, recallId, date, title, product, productType, hazard, classification, severity, firm, distribution, remedy, url, optional raw and isNew); error items carry error and errorMessage. Fields are described in the README.

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

SUMMARY record in the run's default key-value store: window, filters, per-source requests and counts, recalls per agency and severity, charges, and webhookDelivered.

# 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 = {
    "agencies": [
        "fda-drug",
        "fda-device",
        "fda-food",
        "nhtsa",
        "cpsc"
    ],
    "daysBack": 30,
    "maxResultsPerSource": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("owlabee/product-recall-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 = {
    "agencies": [
        "fda-drug",
        "fda-device",
        "fda-food",
        "nhtsa",
        "cpsc",
    ],
    "daysBack": 30,
    "maxResultsPerSource": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("owlabee/product-recall-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 '{
  "agencies": [
    "fda-drug",
    "fda-device",
    "fda-food",
    "nhtsa",
    "cpsc"
  ],
  "daysBack": 30,
  "maxResultsPerSource": 5
}' |
apify call owlabee/product-recall-feed --silent --output-dataset

```

## MCP server setup

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