# US CPSC Recall and Safety Warning Monitor (`zeekr011/us-cpsc-monitor`) Actor

Query and monitor official CPSC recalls and product safety warnings.

- **URL**: https://apify.com/zeekr011/us-cpsc-monitor.md
- **Developed by:** [hugo liu](https://apify.com/zeekr011) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.004 / result item

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?

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 CPSC Recall & Safety Warning Monitor

Query and monitor official U.S. Consumer Product Safety Commission (CPSC)
recalls and Product Safety Warnings. Get product names, models, hazards,
injuries, remedies, manufacturers, retailers, images, and official source links
as clean, automation-ready Dataset rows.

Use it for consumer-product compliance, supplier and product research, catalog
screening, customer operations, and scheduled safety monitoring. The Actor
keeps a stable source identity and idempotency key on every row, so a downstream
workflow can trace an alert back to the CPSC notice and safely deduplicate
retries.

### Before you run

- For a one-off API call, use `lookup` or `export`. `monitor` is stateful and
  emits only new or changed rows after the monitor snapshot is established.
- Actor usage costs **$0.004 per result**, equivalent to **$4 per 1,000
  results**, under Pay Per Event pricing. Runs with zero results have no
  `result-item` usage charge. See the **Pricing** tab for the current terms.
- Data is returned from CPSC at fetch time. This Actor does not provide a
  real-time freshness guarantee or an SLA for upstream publication delays.
- Start with one record type, a precise filter, and a small `maxResults`.
  Warning queries download and parse the complete official warning CSV even
  when `maxResults` is `1`; a recall query may also download the complete
  official recall CSV if the REST service falls back. `maxResults: 0` can
  therefore consume substantially more time and resources and is not an
  unlimited historical archive.
- Successful runs have a per-run Dataset. This Actor does not automatically
  rename it; retain the returned Dataset ID or name/copy important results
  before relying on your account's default retention policy.

### What this Actor does

- Queries CPSC recalls and/or Product Safety Warnings.
- Filters by record type, exact recall or warning number, keyword, product name,
  manufacturer/importer, and inclusive publication windows.
- Normalizes HTML entities, source dates, nested product/hazard/remedy fields,
  URLs, and missing values into a predictable flat output contract.
- Supports `lookup`, `export`, and incremental `monitor` modes.
- Emits `CURRENT` rows for one-off queries, or `ADDED`/`UPDATED` rows for a
  recurring monitor.
- Publishes JSON rows to the run Dataset; Apify provides JSON, CSV, and Excel
  views of that same Dataset.

### Why teams use it

CPSC data is valuable when it is easy to filter, compare, and route. This Actor
turns two public machine-readable sources into a compact safety feed with
source-level traceability, normalized dates, structured arrays, and change
metadata—ready for a spreadsheet, alerting rule, data warehouse, or AI agent.

Common use cases:

- Find product recalls by hazard, manufacturer, product, or date.
- Track Product Safety Warnings alongside formal recalls.
- Export a historical year or quarter for research and compliance review.
- Schedule a defined query and receive only new or changed notices.

### Data sources

| Record type | Source | Role |
| --- | --- | --- |
| `recall` | [SaferProducts.gov Recall REST service](https://www.saferproducts.gov/RestWebServices/Recall) | Primary recall source with structured fields and `LastPublishDate` |
| `recall` | [CPSC recall listing CSV](https://www.cpsc.gov/s3fs-public/recall-data/recalls_recall_listing.csv) | Official recovery source if the REST service is unavailable or unusable |
| `warning` | [CPSC Product Safety Warning CSV](https://www.cpsc.gov/s3fs-public/recall-data/product_safety_warning_listing.csv) | Product Safety Warning records |

The Actor uses the REST service for recall queries when possible and falls back
to the official recall CSV after transient or unusable REST responses. The CSV
does not include REST `LastPublishDate`, so fallback date filtering and change
discovery use the available recall date and may not expose a later edit to an
older notice as precisely as the REST path. Warning queries scan the official
warning CSV and can be more expensive than an exact recall lookup.

The source is current at fetch time, but this Actor is not a complete historical
archive. Use explicit date bounds for reproducible exports and retain important
results in your own Dataset or downstream storage.

There is no row-level output field that identifies whether a recall row came
from the REST service or the CSV fallback. The fallback is official, but its
available dates and fields do not preserve all REST semantics.

#### Request and resource notes

- `recall` queries use the REST service when possible. If that request is
  unusable, the Actor downloads and parses the complete official recall CSV
  before applying local filters.
- `warning` queries download and parse the complete official warning CSV before
  applying local filters. An exact warning number still does not make the
  upstream download proportional to the number of returned rows.
- `maxResults` limits normalized records after source retrieval and filtering;
  it does not cap bytes downloaded or upstream work. In `monitor` mode, the
  final event count can be lower or zero after the state diff.
- `maxResults: 0` removes the Actor-side result cap on the selected source, but
  it does not turn CPSC into an unlimited archive and can create a large
  Dataset.

### Run modes

| Mode | What it returns | State behavior |
| --- | --- | --- |
| `lookup` | Matching current records as `CURRENT` | Does not read or change monitor state |
| `export` | A bounded current-source date-window result as `CURRENT` | Does not read or change monitor state |
| `monitor` | New and changed records as `ADDED` or `UPDATED` | Reads and updates the named Key-Value Store |

Use `lookup` for a focused check, `export` for a defined report, and `monitor`
for recurring change detection. The default mode is `monitor`; if
`publishedSince` is omitted, it uses `lookbackDays` before today.

### Quick start

#### Apify Console

1. Open the Actor and select **Input**.
2. Choose one or both `recordTypes` values: `recall` and `warning`.
3. Choose `lookup`, `export`, or `monitor`, then add a precise filter.
4. Keep `maxResults` small for the first run and click **Start**.
5. Inspect the Dataset or download JSON, CSV, or Excel from the Output tab.

#### Apify CLI

After installing and authenticating the Apify CLI:

```bash
apify call <ACTOR_ID> \
  --input '{"mode":"lookup","recordTypes":["recall"],"recallNumber":"26-716","maxResults":1}' \
  --output-dataset
```

`<ACTOR_ID>` can be the Actor ID or an authorized Actor name. A valid query
with no matches completes successfully with zero Dataset items.

#### REST API

Start a run with a JSON request body:

```bash
curl -X POST \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "mode": "export",
    "recordTypes": ["recall"],
    "keyword": "lithium battery",
    "publishedSince": "2025-01-01",
    "publishedUntil": "2025-12-31",
    "maxResults": 100
  }' \
  "https://api.apify.com/v2/acts/<ACTOR_ID>/runs?waitForFinish=60"
```

Read the returned `defaultDatasetId` through the Dataset API:

```text
GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&clean=true
GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=csv&clean=true&attachment=true
GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=xlsx&clean=true&attachment=true
```

Use `limit` and `offset` for pagination. Keep `$APIFY_TOKEN` in an environment
variable; never put it in Actor input, source code, logs, or Dataset records.

#### API run lifecycle

The POST request starts an Apify run; it does not return Dataset rows directly.
If `waitForFinish=60` returns a non-terminal status such as `RUNNING`, keep the
returned `runId` and poll `GET /v2/actor-runs/<RUN_ID>` or use a Webhook. Read
`defaultDatasetId` after the run reaches `SUCCEEDED`. A successful empty Dataset
is valid; in `monitor` mode it can mean that no notice changed, not that CPSC
had no matching notice.

### Input API reference

The input is a JSON object. The [Input schema](./.actor/input_schema.json) is
the machine-readable contract used by the Console and API.

| Field | Type / default | Description |
| --- | --- | --- |
| `mode` | enum, `"monitor"` | `monitor`, `lookup`, or `export`. |
| `recordTypes` | array; default `recall`, `warning` | One or both CPSC record classes. Allowed values are `recall` and `warning`; the array must not be empty. |
| `keyword` | string | Case-insensitive AND terms matched across title, description, products, models, hazards, manufacturers, retailers, and importers. |
| `productName` | string | Case-insensitive partial match against normalized product names. |
| `manufacturer` | string | Case-insensitive partial match across manufacturers and importers. |
| `recallNumber` | string | Exact normalized recall or warning number; punctuation and letter case are ignored for matching. |
| `publishedSince` | `YYYY-MM-DD` | Inclusive lower bound. The REST recall path sends it as `LastPublishDateStart`; CSV paths use their available record date. |
| `publishedUntil` | `YYYY-MM-DD` | Inclusive upper bound. |
| `lookbackDays` | integer, `7` | Monitor-only lookback when `publishedSince` is omitted. Range: `0`–`3650`. |
| `maxResults` | integer, `500` | Maximum emitted rows. Range: `0`–`25000`; `0` removes the Actor-side cap after retrieval/filtering, but does not avoid full CSV downloads or create an unlimited archive. |
| `emitInitialSnapshot` | boolean, `true` | In `monitor`, emit first-seen records as `ADDED`; set `false` to seed state without emitting the initial snapshot. |
| `monitorId` | string | Optional stable namespace for separate monitors sharing a state store. |
| `stateStoreName` | string, `"us-cpsc-monitor-state"` | Named Apify Key-Value Store used for monitor fingerprints. |

#### Query behavior and validation

- Dates must be real calendar dates in `YYYY-MM-DD`; invalid dates such as
  `2026-02-30` are rejected.
- `publishedSince` cannot be after `publishedUntil`.
- Filtering is applied after source normalization and before `maxResults`.
- `recordTypes` controls which official sources are queried. A query containing
  both types can fetch a REST response plus one or more CSV files.
- `maxResults` is applied after normalization and filtering. It does not limit
  the warning CSV or recall-CSV download, and it does not prevent a REST
  fallback from scanning the complete recall CSV.
- A valid query with no matching records returns `SUCCEEDED` with an empty
  Dataset. Invalid input, malformed source payloads, or an unrecoverable
  upstream error fails the run with an explanatory message.

### Example inputs

#### Monitor recalls and warnings by safety topic

```json
{
  "mode": "monitor",
  "recordTypes": ["recall", "warning"],
  "keyword": "lithium battery",
  "lookbackDays": 14,
  "maxResults": 500,
  "monitorId": "battery-safety"
}
```

#### Look up one current recall

```json
{
  "mode": "lookup",
  "recordTypes": ["recall"],
  "recallNumber": "26-716",
  "maxResults": 1
}
```

#### Export a historical CPSC window

```json
{
  "mode": "export",
  "recordTypes": ["recall"],
  "publishedSince": "2015-01-01",
  "publishedUntil": "2015-12-31",
  "maxResults": 1000
}
```

#### Valid empty result and invalid input

An exact lookup for an unknown number is valid and returns an empty Dataset:

```json
{
  "mode": "lookup",
  "recordTypes": ["recall"],
  "recallNumber": "not-a-real-recall",
  "maxResults": 1
}
```

An input such as `{"publishedSince":"2026-02-30"}` is rejected before the
source is queried.

For abnormal upstream behavior, the Actor retries bounded transient failures
such as `429`, `5xx`, network errors, and timeouts. Malformed source payloads
and other unrecoverable responses fail the run with an explanatory error; they
are not silently converted to an empty Dataset.

### Output API reference

Each Dataset item is one normalized recall or warning. Array fields remain
arrays; missing source values are represented by empty strings, empty arrays,
or `null` dates according to the field contract.

#### Representative historical recall output

This trimmed example is based on a real CPSC recall record. Hash values and
timestamps are placeholders for the values generated by the run; the complete
row contains every field listed below.

```json
{
  "source": "cpsc",
  "sourceRecordId": "recall:15123",
  "recordType": "recall",
  "recallNumber": "15123",
  "title": "Whistler Recalls Jump&Go Portable Jumpstart and Power Supply Units Due to Fire Hazard",
  "recallDate": "2015-04-28",
  "sourceUpdatedAt": "2015-04-28",
  "productNames": ["Portable jumpstart power supplies"],
  "unitsAffected": "About 10,400",
  "hazards": ["The jumpstart's lithium battery can overheat and cause the units to melt, catch fire and ignite nearby items, posing a fire hazard to consumers."],
  "remedies": ["Consumers should immediately stop using the recalled unit and contact Whistler for a free replacement."],
  "sourceUrl": "https://www.cpsc.gov/Recalls/2015/Whistler-Recalls-Jump-and-Go-Portable-Jumpstart-and-Power-Supply-Units",
  "changeType": "CURRENT",
  "changedFields": [],
  "detectedAt": "2026-09-02T00:00:00.000Z",
  "contentHash": "<sha256>",
  "idempotencyKey": "<sha256>"
}
```

For `monitor`, the same content fields are accompanied by `ADDED` or
`UPDATED`; `changedFields` lists normalized fields that changed since the prior
snapshot. `CURRENT` is used by `lookup` and `export`.

#### Identity and monitor metadata

| Field | Type | Description |
| --- | --- | --- |
| `source` | string | Always `cpsc`. |
| `sourceRecordId` | string | Stable identity, normally `recall:<normalized-number>` or `warning:<normalized-number>`. |
| `recordType` | string | `recall` or `warning`. |
| `changeType` | string | `ADDED`, `UPDATED`, or `CURRENT`. |
| `changedFields` | string\[] | Normalized fields changed since the previous monitor snapshot; empty for `ADDED` and `CURRENT`. |
| `detectedAt` | ISO timestamp | Time this Actor produced the row. |
| `contentHash` | string | SHA-256 hash of the normalized source snapshot, excluding derived monitor metadata. |
| `idempotencyKey` | string | Stable key for monitor scope, source record, and content version. Use it to deduplicate retries. |

#### Recall and warning fields

| Field | Type | Description |
| --- | --- | --- |
| `recallNumber` | string | Official CPSC recall or warning number. |
| `title` | string | Notice title or heading. |
| `description` | string | Product and notice description. |
| `recallDate` | string or null | Normalized notice date, `YYYY-MM-DD` when available. |
| `sourceUpdatedAt` | string or null | REST `LastPublishDate` when available; otherwise the available source date. |
| `productNames` | string\[] | Product names. |
| `productModels` | string\[] | Product models when supplied by the REST record. |
| `unitsAffected` | string | Reported units or quantities. |
| `hazards` | string\[] | Hazard descriptions. |
| `injuries` | string\[] | Injury or incident descriptions. |
| `remedies` | string\[] | Consumer actions and remedy descriptions. |
| `remedyOptions` | string\[] | Structured remedy options when supplied. |
| `manufacturers` | string\[] | Manufacturers. |
| `retailers` | string\[] | Retailers or sales channels. |
| `importers` | string\[] | Importers. |
| `distributors` | string\[] | Distributors. |
| `manufacturerCountries` | string\[] | Reported manufacturing countries. |
| `upcs` | string\[] | Product UPC values when supplied. |
| `images` | string\[] | Official image URLs when supplied. |
| `consumerContact` | string | Consumer contact information when supplied. |
| `soldAt` | string | Reported sales location or channel text. |
| `sourceUrl` | string | Official CPSC notice or recall lookup URL. |

#### Dataset and output links

The Actor pushes structured JSON rows only. Apify generates these views from
the same Dataset:

- JSON: `.../items?format=json&clean=true`
- CSV: `.../items?format=csv&clean=true&attachment=true`
- Excel: `.../items?format=xlsx&clean=true&attachment=true`

Successful non-empty runs have their own Dataset ID. If long-term retention is
required, name or copy the Dataset after the run; this Actor does not merge
separate runs into a shared output Dataset. Dataset lifetime follows the
retention rules of your Apify account and plan; do not treat an unnamed
per-run Dataset as a permanent archive.

### Monitoring and delivery semantics

In `monitor` mode:

1. Records are normalized and deduplicated by `sourceRecordId`.
2. The Actor compares normalized content with the snapshot in the named
   Key-Value Store.
3. New records become `ADDED`; changed records become `UPDATED` with
   `changedFields`.
4. Dataset delivery completes before the checkpoint is written.

This is safe at-least-once delivery. If Dataset delivery succeeds but the
checkpoint write fails, a later run may emit the same event again; downstream
consumers should deduplicate with `idempotencyKey`. Records absent from a later
rolling source window are not emitted as `REMOVED`, because absence may reflect
pagination, filtering, fallback-source coverage, or source-window behavior.

For scheduled monitoring, keep the same `monitorId`, `stateStoreName`, and
query scope. The rolling date calculated from `lookbackDays` is not itself used
to create a new state namespace every day.

The HTTP layer starts requests serially within one process, waits one second by
default between request starts, retries transient network errors, `429`, and
`5xx` responses with bounded exponential backoff and jitter, and honors
`Retry-After` when supplied. These locks and limits are process-local; they do
not provide account-wide rate limiting or cross-container exactly-once
delivery.

### Reliability and limitations

- Public CPSC endpoints can change fields, throttle requests, or enter
  maintenance windows. The Actor retries transient failures within a finite
  budget and uses the official recall CSV as a recovery source.
- The recall CSV cannot preserve REST `LastPublishDate` semantics.
- Warning CSV scans can be substantially larger than an exact recall lookup;
  use a precise filter, but remember that `maxResults` limits output after the
  scan rather than the download itself.
- CSV fallback rows do not carry a definitive row-level marker saying that the
  REST service was unavailable; preserve the normalized source fields and
  `sourceUrl` for audit context.
- The Actor reports what CPSC publishes. It does not independently verify
  product claims, injury reports, remedy effectiveness, or legal status.
- The Actor is not a product-specific legal or safety determination. Preserve
  `sourceUrl` and review the authoritative notice before acting.

### Local development

Requirements: Node.js 20 or newer.

```bash
npm install
npm test
npm run build
apify validate-schema
npm run test:real
```

`npm test` uses deterministic fixtures and injected failures, including
malformed payloads, `429`/`Retry-After`, `5xx`, rate limiting, state ordering,
and delivery failure. The bounded real smoke script checks a current record, a
historical record/date window, a valid empty result, and output identity/date
fields; it is not an unbounded export.

### Attribution

This Actor consumes public CPSC data. Review the current CPSC and
SaferProducts.gov terms, availability, and usage guidance before operating at
scale. Preserve `sourceUrl` and `recallNumber` when passing records downstream.

# Actor input Schema

## `mode` (type: `string`):

Choose whether to emit changes, current matches, or a historical export.

## `recordTypes` (type: `array`):

CPSC record classes to query.

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

Terms matched across title, product, hazard, manufacturer, and retailer fields.

## `productName` (type: `string`):

Case-insensitive partial product-name match.

## `manufacturer` (type: `string`):

Case-insensitive partial manufacturer/importer match.

## `recallNumber` (type: `string`):

Exact CPSC recall or warning number.

## `publishedSince` (type: `string`):

YYYY-MM-DD. Monitor mode defaults to lookbackDays before today.

## `publishedUntil` (type: `string`):

Inclusive publication date in YYYY-MM-DD format.

## `lookbackDays` (type: `integer`):

Days before today used when monitor mode has no explicit start date.

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

Maximum records emitted; zero returns all matching records.

## `emitInitialSnapshot` (type: `boolean`):

Emit all first-seen records during the first monitor run.

## `monitorId` (type: `string`):

Stable identifier that separates independent monitor state namespaces.

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

Named Apify Key-Value Store used for monitor fingerprints.

## Actor input object example

```json
{
  "mode": "monitor",
  "recordTypes": [
    "recall",
    "warning"
  ],
  "lookbackDays": 7,
  "maxResults": 500,
  "emitInitialSnapshot": true,
  "stateStoreName": "us-cpsc-monitor-state"
}
```

# Actor output Schema

## `json` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `excel` (type: `string`):

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("zeekr011/us-cpsc-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("zeekr011/us-cpsc-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 '{}' |
apify call zeekr011/us-cpsc-monitor --silent --output-dataset

```

## MCP server setup

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