# US Recall Change Audit — FDA / CPSC / NHTSA (`embellished_dewberry/us-recall-change-audit`) Actor

Read official U.S. recall records from openFDA, CPSC and NHTSA, normalise them, and optionally report changes observed since your previous snapshot. Not safety, medical or legal advice. NHTSA only covers vehicles you specify.

- **URL**: https://apify.com/embellished_dewberry/us-recall-change-audit.md
- **Developed by:** [Drucilla Seeringtl](https://apify.com/embellished_dewberry) (community)
- **Categories:** Automation, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

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

## US Recall Change Audit — FDA / CPSC / NHTSA

Read **official U.S. recall records** from **openFDA** (food / drug / device enforcement), **CPSC** (saferproducts.gov), and **NHTSA** (vehicle recalls by make / model / year). Normalise them into a flat schema, and optionally report **changes this Actor observed** since your previous snapshot.

This Actor:

- uses **official HTTP GET APIs only** (no proxies, no social/e-commerce scraping, no image mirroring)
- does **not** give safety, medical, or legal advice
- does **not** promise real-time coverage or zero missed recalls
- does **not** merge recalls across agencies into one “same recall”
- for **NHTSA**, only queries the **vehicle combinations you provide** (not a full national vehicle-recall feed)

### What you get

Each dataset item includes:

| Field | Meaning |
|---|---|
| `source` | `fda_food` / `fda_drug` / `fda_device` / `cpsc` / `nhtsa` |
| `sourceId` | Stable id inside that source (`recall_number`, CPSC `RecallID`, or NHTSA campaign+vehicle key) |
| `officialUrl` | Link to the official notice when available |
| `publishedDate` / `sourceUpdatedDate` | Dates from the source (`sourceUpdatedDate` is `null` when the source does not provide one) |
| `retrievedAt` | When this Actor read the record |
| `recordHash` | Hash of the comparable factual fields |
| `changeType` | `snapshot`, `new`, `changed`, or `baseline` (first delta run) |
| `changedFields` / `oldValue` / `newValue` | Populated for `changed` |
| `sourceStatus` | Per-record source status (`ok` when built from a successful read) |
| plus | `title`, `summary`, `organization`, `productNames`, `classification`, `status`, `hazard`, `remedy`, vehicle fields, `attribution` |

Street addresses, e-mail fields, and CPSC `ConsumerContact` phone text are **not** output. Official remedy / summary text is kept as published by the agency.

### Modes

#### `snapshot`

Fetch and normalise records for the date window. Every output row has `changeType=snapshot` and is billable as a `recall-record` event (when pay-per-event is enabled).

#### `delta`

Compare the current fetch to **your** baseline and output only `new` / `changed` rows.

- **First run with no baseline:** establishes a baseline (`changeType=baseline`). Those rows are written for transparency but **are not charged** as `recall-record`.
- Baselines are **customer-isolated**. Provide one of:
  - `baselineRecords` — paste prior records / snapshot JSON in the input
  - `baselineDatasetId` — a dataset id from **your** previous run
  - `snapshotStoreName` — a **named key-value store you choose**; it is created under the Apify account that runs the Actor (not a shared author store)
- This Actor only reports changes **it observed** between two of its own snapshots. It does not reconstruct official historical edit times. Narrowing the date window can miss edits to older notices.

### Input

| Field | Default | Notes |
|---|---|---|
| `mode` | `snapshot` | `snapshot` or `delta` |
| `sources` | `["fda_food","cpsc"]` | Add `fda_drug`, `fda_device`, `nhtsa` as needed |
| `lastNDays` | `30` | UTC window ending today; ignored if `publishedFrom` is set |
| `publishedFrom` / `publishedTo` | — | Inclusive `YYYY-MM-DD` |
| `vehicles` | `[]` | Required for `nhtsa`; max 10 `{make,model,modelYear}` objects |
| `maxItems` | `200` | Cap across all sources (1–10,000) |
| `baselineRecords` | — | Delta baseline in-input |
| `baselineDatasetId` | — | Delta baseline from your dataset |
| `snapshotStoreName` | — | Optional named store for saving/loading snapshots |
| `snapshotKey` | `recall-snapshot` | Key inside that store |

Example (CPSC + FDA food, recent window — matches the Store example intent):

```json
{
  "mode": "snapshot",
  "sources": ["fda_food", "cpsc"],
  "lastNDays": 30,
  "maxItems": 20
}
```

Example with NHTSA (vehicles required):

```json
{
  "mode": "snapshot",
  "sources": ["cpsc", "nhtsa"],
  "lastNDays": 365,
  "maxItems": 50,
  "vehicles": [
    {"make": "Honda", "model": "Accord", "modelYear": "2018"}
  ]
}
```

### Output

Dataset items as above. The run also writes `RUN_SUMMARY` to the default key-value store with diagnostics per source (`ok`, `partial`, `rate_limited`, `timeout`, `error`, `skipped`), counts, baseline info, and a disclaimer.

**Failures are not reported as “no recalls”.** If every requested source errors, the run fails and `RUN_SUMMARY.diagnostics` explains timeout / rate limit / HTTP errors and `lastSuccessAt` when available.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Actor start (`apify-actor-start`, platform default, per GB of memory) | $0.00005 |
| `recall-record` — one billable normalised recall or observed change written to the dataset | $0.005 ($5.00 per 1,000) |

Not charged as `recall-record`: baseline establishment rows, unchanged delta rows (omitted), and error diagnostics. The Actor stops cleanly when the run’s max total charge is reached. Default dataset **per-item** platform charging is not used as a second fee on top of `recall-record`.

Default run resources: **256 MB** memory, **300 s** timeout.

### Attribution and terms

- **openFDA:** general content is CC0; third-party material may be excepted. See [openFDA terms](https://open.fda.gov/terms/) and [authentication / rate limits](https://open.fda.gov/apis/authentication/) (anonymous: 1,000 requests/day/IP, 240/minute).
- **CPSC:** recall notices may be copied with attribution and without implying endorsement. See [CPSC API info](https://www.cpsc.gov/Recalls/CPSC-Recalls-Application-Program-Interface-API-Information) and [privacy / reuse policy](https://www.cpsc.gov/About-CPSC/Policies-Statements-and-Directives/Privacy-Policy). This Actor attributes CPSC and does **not** use CPSC badges or logos.
- **NHTSA:** public vehicle-recall API by make/model/year. Coverage equals the vehicles you list only.

### Limits and honesty

- Anonymous openFDA quotas apply; on HTTP 429 the Actor backs off / stops that source and records `rate_limited` — it does not rotate IPs to bypass limits.
- CPSC / NHTSA exact quotas are not published here; requests are rate-limited conservatively.
- Data may be delayed relative to the agencies’ own sites.
- Fuzzy brand / UPC matching is **not** treated as a definitive hit across sources.

### Local development

```bash
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python -m unittest -v
## optional tiny live smoke (few requests only):
US_RECALL_LIVE=1 .venv/bin/python -m unittest -v tests.test_recalls.LiveSmokeTests
```

# Actor input Schema

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

snapshot = return normalised records for the window. delta = compare against your previous baseline and return only new/changed records. The first delta run with no baseline only establishes a baseline (not charged as recall-record).

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

Official sources to query. FDA food/drug/device are separate openFDA enforcement endpoints. NHTSA requires vehicles. Sources are never cross-merged into one recall.

## `lastNDays` (type: `integer`):

Publication/report-date window ending today (UTC), counting today as day 1. Ignored when Published from is set. For NHTSA, filters ReportReceivedDate client-side after the vehicle query.

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

Optional start of the date range (inclusive). Overrides last N days.

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

Optional end of the date range (inclusive). Defaults to today (UTC).

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

Required when sources includes nhtsa. Each entry must be a complete make / model / modelYear. This Actor does not query a full NHTSA recall feed.

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

Maximum number of records to output in this run (across all sources).

## `baselineRecords` (type: `array`):

Optional. Paste the previous snapshot records (or a snapshot object with a records array). Preferred isolation method: baseline stays in your input, never in the author account.

## `baselineDatasetId` (type: `string`):

Optional. ID of a dataset from your previous run (under your Apify account) to use as baseline.

## `snapshotStoreName` (type: `string`):

Optional. Customer-chosen Apify named key-value store (created under the account that runs this Actor) used to save/load the snapshot for delta mode. Do not reuse another customer's store name. Leave empty to rely on baselineRecords or baselineDatasetId only.

## `snapshotKey` (type: `string`):

Key inside the named snapshot store. Default recall-snapshot.

## Actor input object example

```json
{
  "mode": "snapshot",
  "sources": [
    "fda_food",
    "cpsc"
  ],
  "lastNDays": 30,
  "vehicles": [],
  "maxItems": 200,
  "snapshotKey": "recall-snapshot"
}
```

# Actor output Schema

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

One item per normalised recall or observed change. Includes source, sourceId, officialUrl, hashes, changeType and factual fields. No street addresses, emails or consumer phone fields.

## `runSummary` (type: `string`):

JSON record RUN_SUMMARY: mode, sources, date window, fetched/delivered/charged counts, per-source diagnostics (ok / rate_limited / timeout / error), baseline info, duration, max-cost flag and disclaimer.

# 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 = {
    "vehicles": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("embellished_dewberry/us-recall-change-audit").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 = { "vehicles": [] }

# Run the Actor and wait for it to finish
run = client.actor("embellished_dewberry/us-recall-change-audit").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 '{
  "vehicles": []
}' |
apify call embellished_dewberry/us-recall-change-audit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,embellished_dewberry/us-recall-change-audit"
        }
    }
}
```

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/BEnBoPfzMFs6WdlMG/builds/yKWagqnaEcqLa84Ws/openapi.json
