# NHTSA Vehicle Recalls — By Make, Model & Year (`northbell/nhtsa-vehicle-recalls`) Actor

US vehicle recalls from NHTSA's official API: campaign number, component, summary, consequence, remedy, report date, park-it flags, units affected. Search by make, model and year or by campaign number, and flag recalls that are new since your last run.

- **URL**: https://apify.com/northbell/nhtsa-vehicle-recalls.md
- **Developed by:** [Northbell](https://apify.com/northbell) (community)
- **Categories:** Automation, Business
- **Features:** 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 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

## NHTSA Vehicle Recalls — By Make, Model & Year

Look up US vehicle safety recalls on the **National Highway Traffic Safety Administration (NHTSA)** by **make, model and model year** or by **campaign number**, and get one clean row per recall campaign: **campaign number, component, summary, consequence, remedy, the date it was reported to NHTSA, park-it / park-outside flags, over-the-air flag, and the number of units affected**, with a link to the recall on NHTSA. Optionally add the number of owner complaints NHTSA has on file for the vehicle. **$2.00 per 1,000 recalls**, no login, no API key.

Run it again, or put it on a daily schedule, and every row also tells you **whether the recall is new for that vehicle since the last run** (`isNew`). Set *What to return* to *Only new* and you pay only for recalls you have not seen before. The history is kept per vehicle, so you can add vehicles to the list later without disturbing the others.

### What you get

| Field | What it is |
|---|---|
| `campaignNumber`, `url` | the NHTSA campaign number (`23V838000`; V = vehicle, E = equipment, T = tire, C = child seat) and the recall's page on nhtsa.gov |
| `lookup` | `vehicle` (found by make, model and year) or `campaign` (found by campaign number) |
| `manufacturer` | the manufacturer that filed the recall |
| `make`, `model`, `modelYear` | the vehicle the row is for, as NHTSA spells it (`FORD`, `F-150`, `2020`). In a campaign row they are filled only when the campaign covers one make, model or year; otherwise `null`. Equipment recalls have no model year |
| `vehicleCount`, `vehicles` | campaign rows only: how many make / model / year entries the campaign covers, and the first 300 of them as text (`TESLA MODEL Y 2022`) |
| `component`, `componentSystem` | NHTSA's component path (`AIR BAGS:FRONTAL:DRIVER SIDE:INFLATOR MODULE`) and its first part (`AIR BAGS`) |
| `summary`, `consequence`, `remedy` | the manufacturer's description of the defect, what could happen, and the fix, as filed with NHTSA |
| `reportReceivedDate` | the date the manufacturer reported it to NHTSA, as `YYYY-MM-DD` |
| `parkIt`, `parkOutside`, `overTheAirUpdate` | NHTSA's flags: do not drive it, park it outside, the fix is a software update sent over the air (`null` when NHTSA does not send the flag, as for equipment) |
| `potentialUnitsAffected` | NHTSA's number of potentially affected units. Filled on campaign rows; on vehicle rows only with *Add the number of units affected* |
| `nhtsaActionNumber` | NHTSA's action number (for example `EA15001`), when the recall belongs to an investigation |
| `complaintCount` | with *Add the number of NHTSA complaints*: the owner complaints on file for that make, model and year (the vehicle, not the single recall) |
| `isNew`, `previousRunAt` | `true` / `false` whether this recall was not in your previous run for the same vehicle, and when that run was (`null` the first time a vehicle is checked) |
| `observedAt` | when the row was collected |

### Run it again and again

- **On a schedule**: save your input as a task and add a **Schedule**: the same fleet list every morning. Send each run to Google Sheets, Slack or email with an integration on the task.
- **Watch for changes**: Set **What to return** to *Only new*; a recall is new for a vehicle the first time NHTSA lists it for that make, model and year.
- **In bulk**: Put every make, model and year of your fleet or catalogue in one run, with year ranges such as `"2018-2022"`.

### Use it for

- **Fleet and dealer monitoring**: a daily schedule with the vehicles you own or sell, *Only new*, sent to Slack or email
- **Used-car checks**: every recall for a model across its years, with the remedy and whether it is a park-it recall
- **Insurance and warranty research**: recalls by component, with units affected and report date
- **Market research**: how many campaigns a model line has had and how large they were

### Input

```json
{
  "vehicles": [
    { "make": "Ford", "model": "F-150", "modelYear": "2018-2020" },
    { "make": "Tesla", "model": "Model 3", "modelYear": 2022 }
  ],
  "campaignNumbers": ["23V838"],
  "includeUnitsAffected": true,
  "includeComplaintCounts": false,
  "output": "new",
  "maxResults": 200
}
```

- **vehicles**: a list of `{make, model, modelYear}`. `modelYear` is a year or a range (`"2018-2020"`, up to 30 years; one lookup per year). Spell make and model as NHTSA does — upper or lower case does not matter, punctuation does: `F-150` finds the F-150, `f 150` finds nothing. A text line `Ford, F-150, 2020` is accepted too. Up to 300 make / model / year combinations per run.
- **campaignNumbers**: NHTSA campaign numbers, with or without the trailing `000` (`23V838` = `23V838000`). One row per campaign, listing the affected vehicles.
- **includeUnitsAffected**: NHTSA's answer for a vehicle does not include the number of units, so this makes one extra request per recall campaign (each campaign once per run).
- **includeComplaintCounts**: one extra request per vehicle. The count is also listed per vehicle in the run summary (`OUTPUT`), including for vehicles with no recalls.
- **maxResults**: total rows (default 200, up to 5,000). Vehicles are read in the order you list them; within a vehicle the newest report date comes first.

A recall shows up once per vehicle it applies to: a campaign that covers three model years of a model gives three rows if you ask for all three years. In a run on 16 Oct 2026, 29 make / model / year lookups (F-150, Camry, Civic, Model 3 and Silverado 1500 over 5 to 6 model years each) returned 296 rows covering 141 different campaigns, in 19 seconds.

"Is it new" is remembered per vehicle (make, model and year) and per campaign number in a named key-value store in your Apify account. A vehicle you check for the first time has `isNew: null` on that run — there is nothing to compare with — and a vehicle that had no recalls at that time gets a baseline too, so its first recall later is `isNew: true`. With a *Maximum recalls* cap, only the recalls that were returned are remembered; the rest are new next time.

### Pricing

**$2.00 per 1,000 recalls**, plus $0.01 per run. Error rows (a lookup NHTSA could not answer, a campaign number that does not exist) are free. With *Only new*, recalls you have already seen are skipped and not charged. The extra lookups for units affected and complaint counts are not charged. The $0.01 run-start fee is the one charge every run pays, even one that returns nothing.

### Notes

- Source: the public NHTSA APIs on `api.nhtsa.gov` (recalls by vehicle, recalls by campaign number, complaints by vehicle). Anonymous access, no key. NHTSA's [terms of use](https://www.nhtsa.gov/about-nhtsa/terms-use) say the information on its site is public information that may be distributed or copied; they also say NHTSA makes no guarantee of its accuracy or completeness and that mentioning a product is not an endorsement. This Actor is not an NHTSA product and is not endorsed by NHTSA; check the recall on nhtsa.gov (`url`) before you rely on a row.
- I found no rate limit and no attribution requirement in NHTSA's API documentation or terms. The Actor waits 0.4 seconds between requests, and if NHTSA answers 429, 5xx or not at all it waits and retries twice. After three lookups in a row with no answer it stops, adds free error rows saying so, and the run is marked failed when nothing at all could be read.
- A vehicle with no recalls returns no rows (NHTSA answers such a lookup with HTTP 400 and a count of 0; the Actor treats that as "none", not as an error).
- `reportReceivedDate` is converted from NHTSA's day/month/year (`06/03/2019` is 6 March 2019 — checked against NHTSA's own page for campaign 19V182000).
- `potentialUnitsAffected` is the number for the whole campaign, not for the one vehicle. It is not in NHTSA's answer for a vehicle, only in the one for a campaign number.
- `parkIt` was `true` on 2 of the 296 rows in the run above, `parkOutside` on none, and `overTheAirUpdate` on 72. `parkIt` was `null` on 4 rows, where NHTSA does not send the flag.
- Recall texts are NHTSA's wording and can contain a manufacturer's customer-service phone number. NHTSA's standard hotline note is not returned; no contact persons are returned, e-mail addresses in the text are removed, and a stray comma at the start of a text (it happens, for example in the remedy of 26V373000) is dropped.
- Complaint counts are the number of complaint records NHTSA lists for that make, model and year (596 for a 2018 Honda Civic and 50 for a 2020 Ford F-150 on 16 Oct 2026). The Actor reads only the count, never the complaint texts.
- Equipment recalls (tires, child seats, parts) found by campaign number have no model year; NHTSA stores 9999 for them and the Actor returns `null`.

### For AI agents

`{"vehicles": [{"make": "Ford", "model": "F-150", "modelYear": 2020}], "maxResults": 5}` returns up to 5 rows, newest report date first, each with `campaignNumber`, `component`, `summary`, `consequence`, `remedy` and `reportReceivedDate`. Rows have `type`: `recall` or `error`. Fields that do not apply to a recall are `null`, never missing. Schedule the same input daily with `"output": "new"` to get only the recalls that appeared since the previous run.

# Actor input Schema

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

The vehicles to check, as a list of {"make": "Ford", "model": "F-150", "modelYear": 2020}. modelYear can also be a range such as "2018-2020" (one lookup per year). Spell make and model as NHTSA does (not case sensitive): Ford, F-150; Tesla, Model 3; Mercedes-Benz, GLE 350. A line of text "Ford, F-150, 2020" works too. A vehicle with no recalls returns no rows.

## `campaignNumbers` (type: `array`):

Look up recalls directly by NHTSA campaign number, one per line, for example 23V838 or 23V838000 (V = vehicle, E = equipment, T = tire, C = child seat). You get one row per campaign with the list of affected vehicles and the number of units.

## `includeUnitsAffected` (type: `boolean`):

NHTSA does not send the number of potentially affected units with a vehicle search, so this makes one extra request per recall campaign. Rows from campaign numbers always have it. Slower, same price.

## `includeComplaintCounts` (type: `boolean`):

Adds complaintCount to every row: how many owner complaints NHTSA has on file for that make, model and year (not for the single recall). One extra request per vehicle. Also listed for vehicles with no recalls in the run summary.

## `output` (type: `string`):

With "Only new", recalls you already got for that vehicle in an earlier run are skipped and not charged. The first run for each vehicle returns everything, because there is nothing to compare with yet.

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

How many recall rows to return in total, vehicles in the order you listed them, newest report date first within a vehicle. You pay per row returned.

## Actor input object example

```json
{
  "vehicles": [
    {
      "make": "Ford",
      "model": "F-150",
      "modelYear": 2020
    }
  ],
  "includeUnitsAffected": false,
  "includeComplaintCounts": false,
  "output": "all",
  "maxResults": 200
}
```

# Actor output Schema

## `recalls` (type: `string`):

Campaign, vehicle, component, summary, consequence and remedy for each recall.

# 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": [
        {
            "make": "Ford",
            "model": "F-150",
            "modelYear": 2020
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("northbell/nhtsa-vehicle-recalls").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": [{
            "make": "Ford",
            "model": "F-150",
            "modelYear": 2020,
        }] }

# Run the Actor and wait for it to finish
run = client.actor("northbell/nhtsa-vehicle-recalls").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": [
    {
      "make": "Ford",
      "model": "F-150",
      "modelYear": 2020
    }
  ]
}' |
apify call northbell/nhtsa-vehicle-recalls --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,northbell/nhtsa-vehicle-recalls"
        }
    }
}
```

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/QDAynKkfqbo88eDMz/builds/cBoi5wS0xnUoBAXqe/openapi.json
