# Product Recalls API: CPSC, FDA and EU Safety Gate (RAPEX) (`nightwave-owner/product-recalls-monitor`) Actor

Product recalls and safety alerts from CPSC, openFDA (food, drugs, devices) and EU Safety Gate (RAPEX) in one format: product, brand, barcode, hazard, remedy, firm, date and link. Filter by keyword, brand, category, country and date, or watch for new recalls daily.

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

## Pricing

$5.00 / 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.

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 Recalls Monitor (CPSC, FDA, EU Safety Gate)

Product recalls are published in different places and formats: the U.S. Consumer Product Safety Commission (CPSC) for consumer products, the U.S. Food and Drug Administration (FDA) for food, drugs and medical devices, and the European Commission's Safety Gate (formerly RAPEX) for dangerous non-food products found in the EU and EEA.

This actor reads all three and returns one row per recall in the same format: product, brand, model, barcode, category, hazard, remedy, firm, date, country and a link to the official notice. Use it to check whether anything you sell or import has been recalled, to watch a brand or product type every day, or to build a recall feed for a shop, a marketplace or a purchasing team.

### Example from a real run

Run `eWS1somEovVwOrjou` on Apify on 4 October 2026, with this input:

```json
{
  "keywords": ["charger", "helmet", "sprouts"],
  "maxResults": 50
}
```

It returned 50 recalls in 12 seconds of run time: 43 Safety Gate alerts (mostly USB chargers), 4 FDA food recalls (sprouts) and 3 CPSC recalls (bicycle helmets). One row from each source, with `product`, `imageUrl`, `license` and `retrievedAt` left out here to keep it short:

```json
[
  {
    "recallId": "EU-SR/02650/26",
    "source": "eu",
    "country": "HU",
    "title": "USB charger, QUICK CHARGER",
    "brand": null,
    "model": "KeKe-PD002, XDT-526",
    "barcode": "8595511895266",
    "category": "Electrical appliances and equipment",
    "hazard": "Electric shock. The product lacks adequate insulation between the primary and secondary circuits. The secondary USB ports and the connected cable can become live under mains voltage. Consequently, the user may receive an electric shock. The product does not comply with the requirements of the Low Voltage Directive nor with the European standard EN 60335-1.",
    "riskLevel": "Serious risk",
    "remedy": "Type of economic operator to whom the measure(s) were ordered: Distributor. Category of measure(s): Recall of the product from end users. Date of entry into force: 23/06/2025. Type of economic operator to whom the measure(s) were ordered: Distributor. Category of measure(s): Withdrawal of the product from the market. Date of entry into force: 23/06/2025.",
    "publishedAt": "2026-10-02",
    "firm": null,
    "unitsAffected": null,
    "url": "https://ec.europa.eu/safety-gate-alerts/screen/webReport/alertDetail/10118792"
  },
  {
    "recallId": "FDA-H-1337-2026",
    "source": "fda-food",
    "country": "US",
    "title": "Everything Sprouts, LLC: Everything Sprouts and Calco branded Alfalfa Sprouts, Net Wt. 5.0 oz (142 g) UPC 7 68944-00005 9. The 5 oz. containers are distributed...",
    "brand": null,
    "model": null,
    "barcode": null,
    "category": "Food",
    "hazard": "Sprouts may be contaminated with STEC E. coli and/or Salmonella.",
    "riskLevel": "Class I",
    "remedy": "Voluntary: Firm initiated. Status: Ongoing.",
    "publishedAt": "2026-09-23",
    "firm": "Everything Sprouts, LLC",
    "unitsAffected": "1,830.25 pounds",
    "url": "https://api.fda.gov/food/enforcement.json?search=recall_number:%22H-1337-2026%22"
  },
  {
    "recallId": "CPSC-26789",
    "source": "cpsc",
    "country": "US",
    "title": "5Color Recalls Children’s Bicycle Helmet and Pads Sets Due to Risk of Serious Injury or Death from Head Injury; Violate Mandatory Standard for Bicycle Helmets",
    "brand": null,
    "model": null,
    "barcode": null,
    "category": "Helmets & Helmet Accessories",
    "hazard": "The helmets in the recalled sets violate the mandatory safety standard for bicycle helmets because the helmets do not comply with the impact attenuation, retention system, positional stability, labeling and certification requirements. The helmets can fail to protect the user in the event of a crash, posing a serious risk of injury or death due to head injury.",
    "riskLevel": null,
    "remedy": "Consumers should stop using the helmets immediately and contact 5Color for a full refund of the set. Consumers will be asked to destroy the recalled helmet by cutting the straps and email a photo of the destroyed helmet to (contact in the official notice). Consumers should then dispose of the recalled helmet. Consumers may keep the protection pads.",
    "publishedAt": "2026-09-24",
    "firm": "Hengqin Guangwei Consulting Co., Ltd., dba. 5Color, of China",
    "unitsAffected": "About 324",
    "url": "https://cpsc.gov/Recalls/2026/5Color-Recalls-Childrens-Bicycle-Helmet-and-Pads-Sets-Due-to-Risk-of-Serious-Injury-or-Death-from-Head-Injury-Violate-Mandatory-Standard-for-Bicycle-Helmets"
  }
]
```

With empty input (run `9jcq4KTDePQqJXqPY`, 11 seconds) the actor read 711 recalls from the last 30 days (37 CPSC, 72 FDA food, 50 FDA drug, 172 FDA device and 380 Safety Gate) and returned the 50 newest, 10 from each source.

### Use cases

- **Shops and marketplaces:** run your brands or product types as keywords every morning and get the new recalls only.
- **Importers and buyers:** check a supplier's products or a product category before you place an order, for example all Safety Gate alerts for toys notified by Sweden in the last quarter.
- **Compliance and quality teams:** keep a searchable archive of recalls in your own database, with the official link on every row.
- **Product data:** match `barcode` and `model` against your catalogue. Safety Gate alerts often carry the EAN.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `sources` | array | all | `cpsc`, `fda` (all three FDA lists) or `fda-food`, `fda-drug`, `fda-device` one by one, and `eu` (Safety Gate). |
| `keywords` | array | | Words to look for in the product name, brand, model, barcode and firm, for example `["helmet", "IKEA"]`. A recall matches if any keyword is found. Case does not matter. |
| `categories` | array | | Keep recalls whose category contains one of these words, for example `["Toys"]`. Each source has its own categories, see "Limits". |
| `countries` | array | | ISO 3166 alpha-2 code of the country where the recall was issued, for example `["SE", "DE"]`. CPSC and FDA recalls are `US`. |
| `publishedFrom` | string | 30 days before `publishedTo` | First publication date, `YYYY-MM-DD`. |
| `publishedTo` | string | today | Last publication date, `YYYY-MM-DD`. |
| `maxResults` | integer | `50` | Maximum number of recalls, 1 to 10 000. |
| `onlyNew` | boolean | `false` | Return only recalls that earlier runs with the same input have not delivered. See "Monitoring and scheduling". |

The newest recalls come first. When `maxResults` cuts the list, the sources take turns, so 50 results from five sources give about 10 from each instead of 50 from whichever source published last.

Example input: Safety Gate alerts for toys notified by Sweden, Finland or Denmark since July.

```json
{
  "sources": ["eu"],
  "categories": ["Toys"],
  "countries": ["SE", "FI", "DK"],
  "publishedFrom": "2026-07-01",
  "maxResults": 200
}
```

### Output

| Field | Description |
|---|---|
| `recallId` | Source and the source's own number, for example `CPSC-26789`, `FDA-H-1337-2026` or `EU-SR/02650/26`. Stable between runs. |
| `source` | `cpsc`, `fda-food`, `fda-drug`, `fda-device` or `eu` |
| `country` | Where the recall was issued: `US`, or the Safety Gate notifying country such as `SE` or `DE` |
| `title` | Headline of the recall. For FDA, the firm and the start of the product description |
| `product` | Product name or description |
| `brand` | Brand as reported (Safety Gate, and FDA drugs and devices when openFDA has it). `null` for CPSC, where the brand is in the title |
| `model` | Model or type number |
| `barcode` | EAN, UPC or GTIN when reported |
| `category` | Category in the source's own terms |
| `hazard` | What is wrong with the product and the risk. Safety Gate rows start with the risk type, for example `Electric shock.` |
| `riskLevel` | FDA class (`Class I` is the most serious) or Safety Gate level (`Serious risk`). `null` for CPSC |
| `remedy` | What consumers or companies are told to do, or the measures taken |
| `publishedAt` | Publication date, `YYYY-MM-DD`. For Safety Gate, the date of the weekly report |
| `firm` | Company that recalls, makes, imports or distributes the product, when reported |
| `unitsAffected` | Number of units or quantity, as written by the source |
| `url` | Official notice. For FDA, the openFDA record, since FDA has no stable page per enforcement report |
| `imageUrl` | Link to the first product photo at the source. Images are not copied |
| `license` | Source and license of the row |
| `retrievedAt` | When the actor read the data |

### Monitoring and scheduling

Set `onlyNew` to `true` to watch a set of keywords, categories or countries over time. The actor then remembers which recalls it has delivered for the same input, in a named key-value store in your Apify account (`nightwave-state-product-recalls-monitor`, one record per input). Each run returns and charges only recalls that are new since earlier runs. The first run returns everything in the selection. A run without news finishes successfully with 0 rows.

`onlyNew` and `maxResults` are not part of the remembered input, so you can change them without starting over. Leave `publishedFrom` and `publishedTo` empty for a schedule: the window then moves with the date, and recalls you already have are skipped. When there are more new recalls than `maxResults`, the rest come in the next run.

Tested on 4 October 2026 with `{"keywords": ["helmet", "charger"], "sources": ["cpsc", "eu"], "onlyNew": true}`: the first run (`OD3itNuonyvP8OM2e`) returned 50 of the 70 matching recalls, the second (`9fUsglFZXfRcjDK95`) the remaining 20, and the third (`EDbRHlsH2X5Y3VdCP`) 0.

Example: a daily check at 07:00 Swedish time for recalls of the brands you sell. In Apify Console, open **Schedules**, create a schedule with the cron expression `0 7 * * *` and add this actor with the input below.

```json
{
  "keywords": ["Philips", "Anker", "Lego"],
  "onlyNew": true
}
```

The same schedule through the Apify API:

```sh
curl -X POST "https://api.apify.com/v2/schedules?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"name": "daily-recall-check", "cronExpression": "0 7 * * *", "timezone": "Europe/Stockholm", "isEnabled": true, "isExclusive": true,
       "actions": [{"type": "RUN_ACTOR", "actorId": "nightwave-owner~product-recalls-monitor",
                    "runInput": {"contentType": "application/json; charset=utf-8", "body": "<the input above as a JSON string>"}}]}'
```

Add a webhook or an integration (Slack, e-mail, Google Sheets) on the schedule's runs if you want the new rows sent somewhere.

### Limits

- **Publication rhythm.** CPSC publishes recalls on most Thursdays. openFDA updates its enforcement reports weekly, and `publishedAt` is the FDA report date, which can be days or weeks after the firm started the recall. Safety Gate alerts are read from the weekly reports published every Friday, so a new alert shows up here on the Friday after the Commission validates it.
- **Keywords are matched on product, brand, model, barcode and firm**, not on the hazard text. For FDA the search runs on openFDA's servers on the product description, firm and brand name, which matches whole words; for CPSC and Safety Gate it matches any part of a word, so `charg` finds `charger`.
- **Categories differ per source.** Safety Gate uses categories such as `Toys`, `Cosmetics` or `Electrical appliances and equipment`. CPSC uses product types such as `Helmets & Helmet Accessories`. FDA uses `Food`, `Drugs` and `Devices`.
- **openFDA returns at most 25 000 records per list and search.** The log warns if a long date range hits this. Narrow the dates or add keywords.
- **Large date ranges take longer.** A year of Safety Gate is 52 weekly reports of 100-300 alerts each, which took 71 seconds to read in a test on 4 October 2026 (3,884 alerts).
- **No personal data.** Contact names, phone numbers and e-mail addresses in recall notices are not returned; free text has phone numbers and e-mail addresses replaced with "(contact in the official notice)". Company addresses from FDA are left out.
- **Product data, not advice.** The rows repeat what the authorities publish. They are not medical, legal or safety advice, and the official notice is what counts. openFDA states: "Do not rely on openFDA to make decisions regarding medical care."
- Requests are retried three times on network errors, rate limits and server errors. If one source is down, the others are still delivered and the log says which one failed. The run fails only if no source could be read.

### Source and license

- **CPSC:** [Recalls API](https://www.cpsc.gov/Recalls/CPSC-Recalls-Application-Program-Interface-API-Information) (`https://www.saferproducts.gov/RestWebServices/Recall`), no key. CPSC writes: "The information is publicly available to consumers and businesses as well as software and application developers."
- **openFDA:** [enforcement reports](https://open.fda.gov/apis/food/enforcement/) for food, drugs and devices (`https://api.fda.gov`), no key, up to 240 requests per minute. The [terms](https://open.fda.gov/terms/) say the data is "public domain and made available with a Creative Commons CC0 1.0 Universal dedication". The actor sends at most two requests per second.
- **EU Safety Gate:** [weekly reports in XML](https://ec.europa.eu/safety-gate-alerts/api/download/weeklyReport/list/xml/en), no key. The European data portal lists the XML weekly reports under [Creative Commons CC0 1.0](https://data.europa.eu/data/datasets/rapex-rapid-alert-system-non-food), and the rest of the Safety Gate site falls under the [Commission's reuse decision 2011/833/EU](https://eur-lex.europa.eu/eli/dec/2011/833/oj), which allows reuse with the source acknowledged. Product photos stay at the Commission and are only linked.

Every row carries `license` so you can credit the source. This actor is not affiliated with or endorsed by CPSC, FDA or the European Commission.

### Pricing

Pay per event: 0.005 USD per recall returned (event `recall`), which is 5.00 USD per 1 000 recalls. Platform usage is included, so you pay only per result. A run without matches costs nothing per recall. `maxResults` caps how many recalls, and therefore how much, a run can charge.

Rows are delivered only after they have been charged. If you set a maximum cost per run (maxTotalChargeUsd), the run stops there and its status message says how many rows were delivered.

### Contact

Built and maintained by Nightwave AB. Questions, bugs and feature requests: kontakt@nightwave.se

### På svenska

Actorn samlar produktåterkallelser från tre myndigheter i ett och samma format: amerikanska CPSC (konsumentprodukter), FDA via openFDA (livsmedel, läkemedel och medicintekniska produkter) och EU-kommissionens Safety Gate (tidigare RAPEX, farliga produkter som inte är livsmedel i EU och EES).

- Fält per återkallelse: id, källa, land, rubrik, produkt, märke, modell, streckkod, kategori, fara, risknivå, åtgärd, publiceringsdatum, företag, antal enheter, länk till det officiella meddelandet, bildlänk och licens.
- Filter: källor, sökord (produkt, märke, modell, streckkod eller företag), kategorier, land (ISO-kod, till exempel SE) och datum. Standard är de senaste 30 dagarna och 50 återkallelser, nyast först och fördelade mellan källorna.
- Bevakning: med `onlyNew` kommer actorn ihåg vilka återkallelser den redan har levererat för samma input (key-value store `nightwave-state-product-recalls-monitor`). Varje körning levererar och debiterar bara nya återkallelser. Lägg actorn på ett dagligt schema i Apify under Schedules, se "Monitoring and scheduling".
- Inga personuppgifter: kontaktpersoner, telefonnummer och e-postadresser i meddelandena tas bort.
- Datan är produktinformation från myndigheterna, inte medicinsk, juridisk eller säkerhetsmässig rådgivning. Det officiella meddelandet gäller.
- Källor och licens: CPSC (offentlig data), openFDA (CC0 1.0), Safety Gate (veckorapporter under CC0 1.0 enligt EU:s dataportal, i övrigt kommissionens beslut 2011/833/EU om vidareutnyttjande med angiven källa).
- Pris: 0,005 USD per återkallelse (5,00 USD per 1 000). Plattformsanvändningen ingår.
- Kontakt: kontakt@nightwave.se

# Actor input Schema

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

Which registers to read, for example \["cpsc", "eu"]. cpsc is the U.S. Consumer Product Safety Commission, fda is openFDA food, drug and device enforcement reports (or pick fda-food, fda-drug, fda-device), eu is the EU Safety Gate (RAPEX). Empty means all.

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

Words to look for in the product name, brand, model, barcode and firm, for example \["helmet", "IKEA"]. A recall matches if any keyword is found. Empty means all recalls in the date range.

## `categories` (type: `array`):

Keep only recalls whose category contains one of these words, for example \["Toys", "Food"]. Categories differ per source: Safety Gate uses Toys or Cosmetics, CPSC uses product types such as Helmets & Helmet Accessories, openFDA uses Food, Drugs or Devices. Empty means all.

## `countries` (type: `array`):

Country where the recall or alert was issued, as ISO 3166 alpha-2 codes, for example \["SE", "DE"]. CPSC and openFDA recalls are US. Safety Gate alerts carry the notifying country. Empty means all.

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

First publication date to include, YYYY-MM-DD, for example "2026-09-01". Defaults to 30 days before publishedTo.

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

Last publication date to include, YYYY-MM-DD, for example "2026-09-30". Defaults to today.

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

Maximum number of recalls to return, newest first, for example 50. Each recall is one billable result. 1 to 10 000, defaults to 50.

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

For scheduled runs. When true, recalls that an earlier run with the same input already delivered are skipped and not charged, so a daily run returns only recalls published since the last run. The first run returns everything in the selection. Defaults to false.

## Actor input object example

```json
{
  "sources": [
    "cpsc",
    "eu"
  ],
  "keywords": [
    "helmet",
    "charger"
  ],
  "categories": [
    "Toys"
  ],
  "countries": [
    "SE",
    "US"
  ],
  "publishedFrom": "2026-09-01",
  "publishedTo": "2026-09-30",
  "maxResults": 50,
  "onlyNew": true
}
```

# Actor output Schema

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

All recalls returned by the run, as JSON. Open in Apify Console or download via the dataset API.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "sources": [
        "cpsc",
        "fda",
        "eu"
    ],
    "keywords": [],
    "categories": [],
    "countries": [],
    "maxResults": 50,
    "onlyNew": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("nightwave-owner/product-recalls-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "sources": [
        "cpsc",
        "fda",
        "eu",
    ],
    "keywords": [],
    "categories": [],
    "countries": [],
    "maxResults": 50,
    "onlyNew": False,
}

# Run the Actor and wait for it to finish
run = client.actor("nightwave-owner/product-recalls-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sources": [
    "cpsc",
    "fda",
    "eu"
  ],
  "keywords": [],
  "categories": [],
  "countries": [],
  "maxResults": 50,
  "onlyNew": false
}' |
apify call nightwave-owner/product-recalls-monitor --silent --output-dataset

```

## MCP server setup

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