# CPSC Product Recalls Scraper: UPCs, Importers & Retailers (`groundplanes/cpsc-product-recall-upc-importer-scraper`) Actor

Pull US Consumer Product Safety Commission (CPSC) recalls from the official SaferProducts.gov API: products, UPCs, hazards, remedies, manufacturers, importers, where sold, and recall notice links, one flat row per recall.

- **URL**: https://apify.com/groundplanes/cpsc-product-recall-upc-importer-scraper.md
- **Developed by:** [Groundplane Labs](https://apify.com/groundplanes) (community)
- **Categories:** Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 recalls

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## CPSC Product Recalls Scraper: UPCs, Importers & Retailers

Pull US Consumer Product Safety Commission (CPSC) recalls from the official SaferProducts.gov recall web service and get one flat row per recall: product names, UPCs (where CPSC publishes them), hazards, remedies, manufacturers, importers, where the product was sold, country of manufacture, images and the cpsc.gov notice link. Filter by recall date, product name, description, importer or manufacturer, and retailer.

> This actor is an independent tool. It is not affiliated with or endorsed by the U.S. Consumer Product Safety Commission (CPSC).

### What it does

- Calls the CPSC recall web service `https://www.saferproducts.gov/RestWebServices/Recall?format=json` with your date window and keywords, using the parameter names in CPSC's programmer's guide (`RecallDateStart`, `RecallDateEnd`, `ProductName`, `RecallDescription`, `Importer`, `Manufacturer`, `Retailer`).
- Never makes an unfiltered call. The default window is the last 90 days. Long ranges are fetched one year at a time (the full history since 1973 is about 10,000 recalls and takes about a minute).
- Checks every returned recall against the filters it sent. CPSC silently ignores parameters it does not recognize, so if CPSC ever stops applying a filter, the run fails loudly instead of handing you unfiltered rows.
- Removes email addresses from all text and phone numbers from the remedy text, so seller contact details are not copied into your dataset.
- Sorts newest first and stops at `maxItems`.

### Who it's for

- Marketplace sellers, aggregators and 3PL warehouses checking their catalog against new recalls.
- Product liability law firms and commercial liability insurers tracking recalls by importer, manufacturer or product type.
- Compliance teams monitoring recalls of products sold on Amazon, Walmart and other channels.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `recallDateStart` | string (YYYY-MM-DD) | blank = run date minus 90 days | Earliest recall date. For full history use `1970-01-01`. |
| `recallDateEnd` | string (YYYY-MM-DD) | blank = run date | Latest recall date, inclusive. |
| `productNameKeyword` | string | `""` | Product name contains this text (case-insensitive). |
| `recallDescriptionKeyword` | string | `""` | Recall description contains this text. |
| `importerOrManufacturer` | string | `""` | An importer name **or** a manufacturer name contains this text. |
| `retailerFilter` | array of strings | `[]` | CPSC's "sold at" text contains **any** of these. |
| `maxItems` | integer | `1000` | Maximum recalls, newest first. |

Keywords are plain text (no `*` or `%`); CPSC matches them anywhere in the field. Several filters together must all match, except that `importerOrManufacturer` matches either field and `retailerFilter` matches any listed value.

#### Example input (canary)

```json
{
  "recallDateStart": "",
  "recallDateEnd": "",
  "productNameKeyword": "",
  "recallDescriptionKeyword": "",
  "importerOrManufacturer": "",
  "retailerFilter": [],
  "maxItems": 1000
}
```

#### Example run options (default $50 max charge)

```json
{ "maxTotalChargeUsd": 50 }
```

The actor checks your max charge before every dataset push and stops cleanly when the limit is reached.

### Output

One row per recall, newest first. Array fields keep CPSC's order with exact duplicates removed (CPSC sometimes lists the same country or name twice).

| Field | Type | Description |
|---|---|---|
| `recallId` | integer | CPSC internal recall ID. |
| `recallNumber` | string | CPSC recall number. |
| `recallDate` | string | Recall date, YYYY-MM-DD. |
| `title` | string | Recall title. |
| `description` | string or null | Recall description as published (email addresses replaced with `[email removed]`). |
| `productNames` | array | Recalled product names. |
| `upcs` | array | UPCs CPSC lists for the recall. **Usually empty**: see "UPC coverage" below. |
| `hazards` | array | Hazard statements. |
| `remedies` | array | Remedy statements (emails and phone numbers replaced with placeholders). |
| `manufacturers` | array | Manufacturer names as CPSC publishes them, usually "Company, of City, State/Country". |
| `importers` | array | Importer names as published. |
| `retailersSoldAt` | array | CPSC's "sold at" text: stores, websites, dates and prices, with the leading "Sold At:" label removed. It is free text, not a parsed list of retailers; later entries can name the online seller (for example "… d/b/a BrandName, of China"). |
| `manufacturerCountries` | array | Countries of manufacture. |
| `asinMentionsUnverified` | array | Text that matches the Amazon ASIN pattern (`B0` + 8 characters) found in the recall title, description or product fields. These are **unverified text mentions**, not confirmed product identifiers. |
| `imageUrls` | array | Recall photos on cpsc.gov. |
| `cpscNoticeUrl` | string or null | The recall notice on cpsc.gov (the firm's contact details for consumers are there). |

#### UPC coverage (honest numbers)

CPSC publishes UPCs for only a small share of recalls. On 2026-11-01: 2 of the 146 recalls in the last 90 days, 17 of 575 in the last 12 months, and 458 of all 10,070 recalls since 1973 had any UPC. This actor is **not** a complete UPC database; match on product names, models (in the description), importers and ASIN mentions as well.

#### Sample row (synthetic)

```json
{
  "recallId": 99999,
  "recallNumber": "27999",
  "recallDate": "2026-10-29",
  "title": "Example Co. Recalls Space Heaters Due to Fire Hazard",
  "description": "This recall involves Example model EX-100 space heaters...",
  "productNames": ["Example EX-100 space heaters"],
  "upcs": ["012345678905"],
  "hazards": ["The heater can overheat, posing a fire hazard."],
  "remedies": ["Consumers should stop using the heaters and contact Example Co. at [phone removed] for a full refund."],
  "manufacturers": ["Example Manufacturing Ltd., of China"],
  "importers": ["Example Co., of Dallas, Texas"],
  "retailersSoldAt": ["Amazon.com and Walmart.com from January 2025 through August 2026 for about $40."],
  "manufacturerCountries": ["China"],
  "asinMentionsUnverified": ["B0EXAMPLE1"],
  "imageUrls": ["https://www.cpsc.gov/s3fs-public/example.jpg"],
  "cpscNoticeUrl": "https://www.cpsc.gov/Recalls/2027/Example-Co-Recalls-Space-Heaters"
}
```

### How pricing works

Pay per result. You only pay for rows returned.

### Data source and terms

- **CPSC Recalls Retrieval Web Service** on SaferProducts.gov, https://www.saferproducts.gov/RestWebServices/Recall. Recall notices are public information published by a U.S. federal agency. CPSC documents the service's parameters and fields in its "Recalls Retrieval Web Services Programmers Guide" (v1.3), linked from the CPSC recalls API page on cpsc.gov.
- Two published notice links carry typos (`https:/www…`, `hhttps://…`); the actor repairs the scheme so every `cpscNoticeUrl` is a working https link.
- **Update cadence:** CPSC posts recalls several times a week, usually on Thursdays, and sometimes edits older notices.

### Limits and FAQ

- **The retailer filter depends on CPSC's free-text "sold at" field**, which is sometimes empty or names only a channel. A retailer filter can therefore miss recalls of products sold there. Leave it empty for complete coverage.
- **ASIN mentions are text matches only.** Treat them as leads to check, not as confirmed listings.
- **Contact details are removed.** CPSC's consumer-contact field is not output, and emails/phones are stripped from the remedy text, because many small sellers list a personal email. Use `cpscNoticeUrl` to reach the firm.
- **Zero matches** end as `Complete: 0 recalls matched your filters` with counts. Widen the date window or shorten the keywords.
- CPSC sometimes answers with a temporary "Error retrieving Recalls" record. The actor retries it like a server error and fails clearly if it persists. It never passes that record on as data.

### Troubleshooting

- **Failed: "returned an error record"**: the CPSC service was down for longer than the retry window. Retry later.
- **Failed: "CPSC appears to be ignoring the … filter"**: CPSC changed a parameter; nothing unfiltered was delivered.
- **Failed or PARTIAL: "failed validation"**: CPSC changed its record format; the named fields could not be read.
- **PARTIAL: "CPSC API failed mid-run for <dates>"**: the newest part of your range was delivered; retry for the rest.
- **PARTIAL: max charge limit reached**: raise the max charge for the run.

If this keeps happening, please open an issue on this actor's Issues tab and we'll fix it fast.

Found a problem or need a field? Open an issue on the Issues tab; we respond quickly.

### Changelog

- **0.1.1 (2026-11-01):** Review fix: nested collection subfields (for example `Products.Name`, `ProductUPCs.UPC`, `Images.URL`) are now validated on every item, so a renamed subfield fails loudly instead of shipping empty arrays. Full-history timing wording corrected to about a minute.
- **0.1 (2026-11-01):** First build.

# Actor input Schema

## `importerOrManufacturer` (type: `string`):

Keep recalls with an importer name or a manufacturer name containing this text. Plain text, no wildcards.

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

Maximum recalls to return, newest first.

## `productNameKeyword` (type: `string`):

Keep recalls with a product name containing this text (case-insensitive, matched by CPSC). Plain text, no wildcards.

## `recallDateEnd` (type: `string`):

Latest recall date (YYYY-MM-DD), inclusive. Leave blank for the run date.

## `recallDateStart` (type: `string`):

Earliest recall date (YYYY-MM-DD). Leave blank for the run date minus 90 days, computed at run time. For full history use an early date such as 1970-01-01; the actor then fetches one year at a time.

## `recallDescriptionKeyword` (type: `string`):

Keep recalls whose description contains this text (case-insensitive, matched by CPSC). Plain text, no wildcards.

## `retailerFilter` (type: `array`):

Keep recalls whose published 'sold at' text contains any of these (for example Amazon.com, Walmart). Leave empty for all recalls. CPSC's 'sold at' text is free text and sometimes empty, so a retailer filter can miss recalls of products sold there.

## Actor input object example

```json
{
  "importerOrManufacturer": "",
  "maxItems": 1000,
  "productNameKeyword": "",
  "recallDateEnd": "",
  "recallDateStart": "",
  "recallDescriptionKeyword": "",
  "retailerFilter": []
}
```

# Actor output Schema

## `results` (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 = {
    "importerOrManufacturer": "",
    "maxItems": 1000,
    "productNameKeyword": "",
    "recallDateEnd": "",
    "recallDateStart": "",
    "recallDescriptionKeyword": "",
    "retailerFilter": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("groundplanes/cpsc-product-recall-upc-importer-scraper").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 = {
    "importerOrManufacturer": "",
    "maxItems": 1000,
    "productNameKeyword": "",
    "recallDateEnd": "",
    "recallDateStart": "",
    "recallDescriptionKeyword": "",
    "retailerFilter": [],
}

# Run the Actor and wait for it to finish
run = client.actor("groundplanes/cpsc-product-recall-upc-importer-scraper").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 '{
  "importerOrManufacturer": "",
  "maxItems": 1000,
  "productNameKeyword": "",
  "recallDateEnd": "",
  "recallDateStart": "",
  "recallDescriptionKeyword": "",
  "retailerFilter": []
}' |
apify call groundplanes/cpsc-product-recall-upc-importer-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,groundplanes/cpsc-product-recall-upc-importer-scraper"
        }
    }
}
```

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/gOQczIAM9TQo5P0jT/builds/8mc0vTxa2RlpQgoC9/openapi.json
