The NHTSA vPIC API: how to use it and where it stops

Makes, models and VIN decoding from the free US government API, with working calls, the gotchas nobody mentions, and what to do when a vehicle isn't American.

Dotted illustration of a riveted identification plate on a car door jamb under a magnifying glass

The NHTSA API most developers mean is vPIC, the Product Information Catalog Vehicle Listing run by the US National Highway Traffic Safety Administration. It's free, it needs no key, and it does two things very well: it lists the makes and models sold in the US, and it decodes VINs.

We build a different product (VehiclesDB is a make and model catalog from 18 countries' official vehicle data), and we don't decode VINs at all. So this isn't a sales page for a vPIC replacement. It's the guide we wish we'd had: the calls you'll actually use, real responses, the limits you'll hit, and what to pair it with when your users drive cars that were never sold in America.

Every response on this page was fetched from vpic.nhtsa.dot.gov on October 3, 2026.

What the NHTSA API covers

vPIC is built from the information manufacturers file with NHTSA under 49 CFR Part 565, the rule that says how a VIN must be structured for vehicles sold in the United States. In NHTSA's own words, the dataset "is populated using the information submitted by the Motor Vehicle manufacturers through the 565 submittals".

That one sentence explains both the strength and the limit:

  • Strength: for a US-market vehicle, the decode comes from the manufacturer's own VIN filing. Make, model, model year, body class, engine, plant. Free, official, no key.
  • Limit: a vehicle that was never filed for the US market isn't in there. More on that below.

The base URL for everything is https://vpic.nhtsa.dot.gov/api/vehicles/, and every method takes format=json (or xml, csv).

Get all makes and models with vPIC

All makes:

curl "https://vpic.nhtsa.dot.gov/api/vehicles/GetAllMakes?format=json"
{
  "Count": 12380,
  "Results": [
    { "Make_ID": 12858, "Make_Name": "#1 ALPINE CUSTOMS" },
    { "Make_ID": 4877, "Make_Name": "1/OFF KUSTOMS, LLC" },
    ...
  ]
}

That's the first gotcha. 12,380 makes isn't 12,380 car brands: every company that ever filed a VIN structure is in there, including trailer builders and custom shops. If you put this straight into a dropdown, your users scroll past "#1 ALPINE CUSTOMS" to find Audi. Filter by vehicle type instead:

curl "https://vpic.nhtsa.dot.gov/api/vehicles/GetMakesForVehicleType/car?format=json"

Models of one make:

curl "https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMake/honda?format=json"
{
  "Count": 362,
  "Results": [
    { "Make_ID": 474, "Make_Name": "Honda", "Model_ID": 1861, "Model_Name": "Accord" },
    { "Make_ID": 474, "Make_Name": "Honda", "Model_ID": 1863, "Model_Name": "Civic" },
    ...
  ]
}

Models of a make for one year and type, which is what a year → make → model picker needs:

curl "https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMakeYear/make/honda/modelyear/2015/vehicletype/car?format=json"

That one returns 4 models: Accord, Civic, Fit and CR-Z. Note that the CR-V and Pilot aren't in it: NHTSA classes them as multipurpose passenger vehicles, not cars, so a "car" filter quietly drops most SUVs. Query vehicletype/mpv as well, or leave the type out.

Decode a VIN with the NHTSA VIN decoder API

The flat format is the one you want; it returns one object with named fields instead of a list of variable/value pairs:

curl "https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValues/1HGCM82633A004352?format=json"

Trimmed to the fields most apps use (the real response has 154 of them):

{
  "Make": "HONDA",
  "Manufacturer": "AMERICAN HONDA MOTOR CO., INC.",
  "Model": "Accord",
  "ModelYear": "2003",
  "Trim": "EX-V6",
  "BodyClass": "Coupe",
  "VehicleType": "PASSENGER CAR",
  "EngineCylinders": "6",
  "DisplacementL": "2.998832712",
  "PlantCountry": "UNITED STATES (USA)",
  "ErrorCode": "0",
  "ErrorText": "0 - VIN decoded clean. Check Digit (9th position) is correct"
}

A few things to know:

  • Check ErrorCode. 0 means a clean decode. Anything else means a partial or doubtful one, and ErrorText tells you why (a wrong check digit is the classic).
  • Most fields are empty strings, not nulls. Of those 154 fields, many will be "" for any given VIN. Treat empty as unknown.
  • Partial VINs work. Use * for unknown characters, as in 5UXWX7C5*BA, and pass modelyear= to help it.
  • Batch up to 50 VINs with a POST to DecodeVINValuesBatch, which is far kinder to the rate limit than 50 separate calls.
curl -X POST "https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/" \
  --data "format=json&data=5UXWX7C5*BA,2011;5YJSA3DS*EF"

The vPIC API documentation, the parts you'll actually use

The official documentation page lists a few dozen methods. In practice, almost every app uses five:

Method What it's for
DecodeVinValues/{vin} One VIN, flat JSON
DecodeVINValuesBatch/ (POST) Up to 50 VINs per request
GetMakesForVehicleType/{type} Make list for a dropdown, filtered
GetModelsForMakeYear/make/{make}/modelyear/{year} Models for a year → make → model picker
GetModelsForMake/{make} Every model of a make, all years

Two more things from the docs that matter in production:

  • There's a rate limit, but no published number. NHTSA says traffic "will be controlled by an automated traffic rate control mechanism". In practice: cache aggressively (makes and models barely change), batch your VIN decodes, and back off on errors.
  • You can run the decoder yourself. NHTSA publishes standalone databases (a SQL Server backup, around 178 MB, and a PostgreSQL version) that it updates regularly. They cover VIN decoding only; makes, models and the other lookups still need the API. If you decode a lot of VINs, a local copy takes the rate limit out of the picture.

If you need recalls rather than decoding, that's a different NHTSA API: https://api.nhtsa.gov/recalls/recallsByVehicle?make=honda&model=accord&modelYear=2003 returns the recall campaigns for a make, model and year.

Limits: US-market vehicles only

Here's the wall almost everyone outside the US hits. We took eight of Europe's best-selling cars and asked vPIC for their make's models. Then we checked the same models in our catalog, which is built from official vehicle data in 18 countries (national registers in 15 of them, one Swiss canton's register, plus the US and Canadian fuel economy listings):

Model vPIC (GetModelsForMake) Official registers where VehiclesDB sees it
Dacia Sandero Dacia: 0 models 11 (CH, DE, ES, FI, GB, IE, IL, LU, NL, NO, UA)
Škoda Octavia Škoda: 0 models 13
Volkswagen Polo Not among Volkswagen's 40 models 15
Opel Corsa Not among Opel's 8 models 13
Renault Clio Not among Renault's 5 models 13
Peugeot 208 Not among Peugeot's 8 models 14
SEAT Ibiza Not among SEAT's 2 models 13
Fiat Panda Not among Fiat's 11 models 13

None of that is a bug. vPIC is a catalog of what manufacturers filed for the US market, and the Polo, the Clio and the Sandero were never sold there. But if your users are in Europe, Latin America or Asia, a vPIC-backed dropdown simply won't have their car. It's the most common complaint about vPIC in developer forums, usually some version of "it only has models sold in the US".

The same goes for VIN decoding. A European VIN has the same 17-character shape, but vPIC only knows the VIN patterns that were filed with NHTSA. Feed it a Dacia-pattern VIN and you get error 7: "Manufacturer is not registered with NHTSA for sale or importation in the U.S. for use on U.S roads". A European Volkswagen VIN gets you the manufacturer and not much else.

Non-US vehicles: pairing vPIC with VehiclesDB

If you need both, here's the split that works:

1. Decode US VINs with vPIC. It's free and official, and nothing we have beats it for that.

2. Use VehiclesDB for the make and model layer. The catalog has 15,068 models across 934 makes, including the eight above, built from the national registration registers of Argentina, Australia, Finland, Germany, Ireland, Israel, Luxembourg, Malaysia, the Netherlands, New Zealand, Norway, Spain, Thailand, Ukraine and the United Kingdom, the vehicle register of the Swiss canton of Thurgau, plus the official fuel economy listings of the United States (fueleconomy.gov) and Canada (Natural Resources Canada). You can download it as CSV, JSON or SQLite, or put it behind a make and model dropdown for free.

3. Resolve vPIC's strings to stable ids. vPIC gives you "Make": "HONDA" and "Model": "Accord" as plain text, in whatever case and spelling the manufacturer filed. If you're merging vPIC decodes with data from anywhere else, the resolve endpoint turns those strings into one canonical id:

curl -X POST https://vehiclesdb.com/api/v1/resolve \
  -H "Authorization: Bearer vdb_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input": "HONDA Accord", "country_hint": "us"}'
{
  "match": {
    "id": "car/honda/accord",
    "name": "Accord",
    "make": { "id": "honda", "name": "Honda" },
    "kind": "car",
    "confidence": 0.97,
    "method": "fold"
  },
  "alternates": [],
  "resolved_via": ["fold:accord→accord"],
  "dataset_version": "2026.10.3"
}

Now a US decode and, say, a Dutch registration row for the same model share one id, which is the whole point when you're building anything that spans countries.

Every account gets 100 free credits, once, with no card; a resolve costs 1. Create a key when you need one, or start with the getting started guide.

Is the NHTSA VIN decoder API free?

Yes. vPIC needs no key, no account and no payment. The only cost is the rate limit, which you avoid with caching, batching or the standalone database. If you're comparing it with paid options, we keep an honest comparison of vehicle data providers, including which ones decode VINs outside the US.