# VIN Decoder with Recalls Included: Bulk VIN Lookup (NHTSA) (`solidfetch/vin-decoder`) Actor

VIN decoder and VIN validator for bulk VIN lookup on the official NHTSA vPIC API: vehicle identification number to vehicle specs: make, model, year, trim, engine, fuel, drive, body, safety features. VIN check digit validation, open vehicle recalls, car data. No API key. Bad and repeated VINs free.

- **URL**: https://apify.com/solidfetch/vin-decoder.md
- **Developed by:** [Solid Fetch](https://apify.com/solidfetch) (community)
- **Categories:** Agents, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 vin decodeds

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

## VIN Decoder with Recalls Included: Bulk VIN Lookup (NHTSA)

VIN decoder for bulk VIN lookup: paste a list of VINs and get make, model, year, trim, engine and plant for each one from the official US NHTSA vPIC database, plus the NHTSA vehicle recalls listed for that make, model and model year (not proof that this car is unrepaired, see the recalls section). The check digit of each VIN is validated. A decoded VIN costs $0.002 on the free plan, and invalid or repeated VINs are saved free.

```json
{
    "vins": ["1HGCM82633A004352", "5YJSA1E27HF000337"]
}
```

One output row per VIN (shortened, in the format of the real answer):

```json
{
    "vin": "1HGCM82633A004352",
    "valid": true,
    "checkDigitValid": true,
    "status": "decoded",
    "make": "HONDA",
    "model": "Accord",
    "modelYear": 2003,
    "trim": "EX-V6",
    "bodyClass": "Coupe",
    "plantCountry": "UNITED STATES (USA)",
    "modelYearRecallCount": 4
}
```

### VIN decoder with recalls included: bulk VIN lookup (NHTSA)

Decode a list of VINs in bulk for **$0.002 per decoded VIN** on the free plan ($0.0012 on Gold and above). For every VIN the actor returns the vehicle specs from the official US NHTSA vPIC database (make, model, model year, trim, body class, vehicle type, drive type, engine, transmission, plant, manufacturer, weight class, doors, seats, safety features), checks the VIN's check digit, and adds the recalls NHTSA lists for that make, model and year. Optionally add NHTSA crash-test star ratings and EPA fuel economy (mpg). Paste a column of VINs, get a table. No API key, no account with anyone, no scraping: every data source is a public US government API.

You pay only for a real answer. Invalid VINs, repeats of an earlier VIN, VINs with a wrong check digit and VINs that vPIC cannot decode are saved free, each with a note that says why.

### What you get for each VIN

| Field | Content |
|---|---|
| `vin`, `valid` | The VIN as read (upper case, spaces and dashes removed) and whether it has 17 valid characters and, for North American VINs, a correct check digit |
| `checkDigitChecked`, `checkDigitValid` | Whether the check digit rule applies (VINs starting with 1 to 5: USA, Canada, Mexico) and whether position 9 is right |
| `make`, `model`, `modelYear`, `trim` | From vPIC. `trim` can be a list of trims ("Two Eco/Three/Four") when the VIN does not pin down one |
| `bodyClass`, `vehicleType`, `driveType`, `doors`, `seats`, `gvwr` | Body, vehicle type, drive (FWD, 4WD, ...), door and seat counts, gross vehicle weight rating class |
| `engine` | `cylinders`, `displacementL`, `displacementCc`, `fuelType`, `fuelTypeSecondary`, `horsepower`, `configuration`, `model`, `turbo`, `electrification` |
| `transmission` | `style` and `speeds` |
| `plantCountry`, `plantCity`, `plantState`, `manufacturer` | Where and by whom it was built |
| `safetyFeatures` | Systems vPIC lists as standard (ABS, stability control, traction control, tire pressure monitoring, collision warning, lane keeping, blind spot, backup camera and so on) and the airbag locations |
| `errorCode`, `errorText` | vPIC's own result, for example `0` "VIN decoded clean", or `1,5,14,400` for a VIN with a wrong check digit and invalid characters |
| `recalls`, `modelYearRecallCount` | With `includeRecalls` on (the default): the recall campaigns NHTSA lists for the vehicle's make, model and model year (campaign number, component, summary, report date), newest first, and how many there are. Not this VIN's own recalls |
| `wheelSize` | `frontInches`, `rearInches` when vPIC lists the wheel diameter. vPIC has no tire size (width/aspect ratio), only the wheel, so that is what is returned |
| `safetyRatings`, `safetyRatingsStatus` | With `includeSafetyRatings` on: NHTSA's crash-test (NCAP) stars, `overall`, `frontCrash`, `sideCrash`, `sidePole`, `rollover` and the driver and passenger sides, 1 to 5 or `null` when NHTSA did not rate it, plus rollover chance, stability control / forward collision / lane departure status, NHTSA complaint, recall and investigation counts, `variants` and `matchConfidence` |
| `fuelEconomy`, `fuelEconomyStatus` | With `includeFuelEconomy` on: EPA `mpgCity`, `mpgHighway`, `mpgCombined`, `fuelType`, `annualFuelCostUsd`, `co2GramsPerMile`, `vehicleClass`, `atvType` (an EV's mpg is MPGe), the fueleconomy.gov variant it was matched to, `variants` and `matchConfidence` |
| `status`, `partial`, `note` | `decoded`, `partial`, `not-decoded`, `invalid-format` or `duplicate`; `partial` is `true` when only make plus model year (or make plus model) was resolved; `note` explains anything unusual and why a free row is free |

### Read this about the recalls

NHTSA's recalls API answers by **make, model and model year**. The `recalls` list is every recall campaign NHTSA lists for that vehicle model, for the model year, not proof that this particular car is still unrepaired. A car whose air bag was replaced under a campaign still shows that campaign here. To find out whether an individual VIN has an open, unrepaired recall, the owner or buyer checks NHTSA's VIN lookup (nhtsa.gov/recalls) or asks the manufacturer. `modelYearRecallCount` is the number of model-year campaigns in the list (it is not a count of this VIN's open recalls), and `recallsStatus` says `ok`, `skipped` (option off), `error` (lookup failed) or `not-applicable` (no model or year to look up).

### Read this about safety ratings and fuel economy

Both are looked up by **model year, make and model**, not by VIN, and both are off by default. NHTSA rates a model year in several body variants (2 doors, 4 doors, with side airbags); the variant that fits the VIN's door count is used. fueleconomy.gov lists a model year per engine and transmission; the variants that fit the VIN's cylinders, displacement and transmission are used. `matchConfidence` says how sure the match is: `high` one candidate fits every check, `medium` the first of several that fit, `low` a guess among several. When no variant fits (a different engine), the field is empty and the status is `not-found` rather than another engine's number. A failed lookup does not change the charge: the row is still charged and `safetyRatingsStatus` or `fuelEconomyStatus` is `error`. Each distinct vehicle is looked up once per run, and at most 500 distinct vehicles per run and kind (the rest are `skipped`).

### Non-US VINs

The check digit is a North American rule. A European or Asian VIN has no check digit to validate, so `valid` only means the VIN has 17 allowed characters, and `checkDigitChecked` is `false`. vPIC decodes VINs from other regions only partially (usually make, model year and plant country, sometimes the model). When it resolves the make and the model year (or the model) the row is returned and charged, with `partial: true` if one of the three is missing, and says so in `note`. A North American VIN with a wrong check digit is almost always a typo, so it is not decoded: the row is free, `valid` is `false`, and `note` says the check digit is wrong.

### How it is billed

One `vin-decoded` event per VIN for which vPIC returned a real answer: its result code is `0` (decoded clean) or `14` (decoded, with a warning that the manufacturer gave no data for some characters), it resolved the make and the model year or the model, it has a correct check digit if it is North American, it is not a repeat in the same run, and, when `includeRecalls` is on and the model year, make and model are known, its recalls were fetched.

Free, with the reason in `note` and vPIC's own `errorCode` and `errorText`: VINs that are not 17 valid characters; repeats of an earlier VIN; a wrong check digit on a North American VIN (vPIC code 1); any other vPIC code: 2 to 4 (vPIC changed a character, so the answer is for another VIN), 5 (errors in a few positions), 6 and 11 (incomplete VIN), 7 (manufacturer not registered), 8 (no detailed data), 400 and 401 (invalid characters), or a code vPIC adds later; a VIN where vPIC returned no make; and decoded VINs where the recalls lookup failed (run those again). A free row carries no vehicle fields, only the reason. If vPIC does not answer, the run stops with the rows saved so far counted and charged only once; if the recalls API does not answer for any lookup, the run fails and nothing is charged for those rows. The run stops at your cost limit and says how many inputs were left. A run that fails and is restarted continues after the rows already saved and does not charge them twice.

### Input

- **VINs** (`vins`): a list, one VIN per entry (an entry may hold several, separated by commas, semicolons or line breaks).
- **Or paste a list** (`text`): a column from a spreadsheet, one VIN per line.
- **Include recalls** (`includeRecalls`, default on).
- **Include NHTSA safety ratings** (`includeSafetyRatings`, default off).
- **Include EPA fuel economy** (`includeFuelEconomy`, default off).

Up to 50,000 inputs per run. An input field the actor does not know stops the run before anything is charged, so a misspelt setting never gives you a result that ignored it.

```json
{
  "vins": ["1HGCM82633A004352", "5YJSA1E27HF000337"],
  "includeRecalls": true,
  "includeSafetyRatings": true,
  "includeFuelEconomy": true
}
```

### Where the data comes from, and how politely

- Specs: the NHTSA vPIC `DecodeVinValuesBatch` endpoint, 50 VINs per request, two requests at a time at most. vPIC states that it applies an automated traffic rate control, so the actor keeps its request rate low, waits and retries on HTTP 429 and 5xx answers (honouring `Retry-After`), and identifies itself with a User-Agent.
- Recalls: the NHTSA recalls API (`recallsByVehicle`), one request per distinct make, model and model year in your list, three at a time at most, and never repeated within a run.
- Safety ratings: the NHTSA SafetyRatings API (official, free), two requests per distinct make, model and year. Fuel economy: the fueleconomy.gov web services (US DOE and EPA, public domain), two requests per distinct make, model, year and engine. Two lookups at a time per kind, each distinct vehicle once per run.
- All are free public US government data. A VIN is vehicle data. The actor never looks up or returns an owner, a name, an address or a plate.

### Limits

- vPIC covers vehicles sold in the US market best. It can lack data on older, rare, or non-US vehicles. A missing value in vPIC means NHTSA has no data on it, not that the vehicle lacks the feature.
- Only the vehicle data listed above is returned: no listings, prices, mileage, accident history or owner data.

Did this help? A review on this page, or an issue saying what is missing, helps other people find it.

# Changelog

This Actor's version history is a separate document: https://apify.com/solidfetch/vin-decoder/changelog.md

# Actor input Schema

## `vins` (type: `array`):

One 17-character VIN per entry. An entry may also hold several VINs separated by commas, semicolons or line breaks. Repeats are dropped (the repeat gets a free row). Use this or the text box below, or both. Up to 50,000 entries per run.

## `text` (type: `string`):

Paste a column from a spreadsheet or a CSV export: one VIN per line, or separated by commas, semicolons or tabs. Spaces and dashes inside a VIN are removed.

## `includeRecalls` (type: `boolean`):

Add the recalls NHTSA lists for the vehicle's make, model and model year (campaign number, component, summary, report date) and how many there are. These are recalls for the model year, not proof that this particular car is still unrepaired. Costs no extra: the price is per VIN that vPIC really decodes.

## `includeSafetyRatings` (type: `boolean`):

Add NHTSA's crash-test star ratings (overall, front, side, side pole, rollover) for the vehicle's make, model and model year, from the official NHTSA SafetyRatings API. Several body variants are rated per model year; the one that fits the VIN's door count is taken and `matchConfidence` says how sure the match is. Costs no extra. Off by default.

## `includeFuelEconomy` (type: `boolean`):

Add EPA mpg (city, highway, combined), yearly fuel cost and CO2 per mile from fueleconomy.gov (public domain), matched on model year, make, model, engine size and transmission. Best-effort: `matchConfidence` is high, medium or low, and no row is returned for an engine that does not fit. Costs no extra. Off by default.

## Actor input object example

```json
{
  "vins": [
    "1HGCM82633A004352",
    "5YJSA1E27HF000337"
  ],
  "text": "1HGCM82633A004352\n5YJSA1E27HF000337",
  "includeRecalls": true,
  "includeSafetyRatings": false,
  "includeFuelEconomy": false
}
```

# Actor output Schema

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

Dataset table with one row per input entry: VIN validity, vehicle specs from NHTSA vPIC, model-year recalls, optional safety ratings and fuel economy, and a status and note per row.

## `summary` (type: `string`):

SUMMARY record in the key-value store: counts of rows by status and by vPIC result code for the run.

# 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 = {
    "vins": [
        "1HGCM82633A004352",
        "5YJSA1E27HF000337",
        "JTDKARFU2H3000001"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidfetch/vin-decoder").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 = { "vins": [
        "1HGCM82633A004352",
        "5YJSA1E27HF000337",
        "JTDKARFU2H3000001",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("solidfetch/vin-decoder").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 '{
  "vins": [
    "1HGCM82633A004352",
    "5YJSA1E27HF000337",
    "JTDKARFU2H3000001"
  ]
}' |
apify call solidfetch/vin-decoder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,solidfetch/vin-decoder"
        }
    }
}
```

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/U6xYRVGV2emNAtQSx/builds/c4kgkt7zyw3fdsvWT/openapi.json
