Decoding VINs with the free NHTSA vPIC API: a practical guide
How to use the free NHTSA VIN decoder API (vPIC): endpoints, batch decoding, ErrorCode meanings, reliable and empty fields, caching, JS and Python code.

The NHTSA VIN decoder API is vPIC: a free, keyless REST service at https://vpic.nhtsa.dot.gov/api/vehicles/. Call DecodeVinValues/{vin}?format=json and you get one flat JSON object with about 140 variables (make, model, model year, body class, engine, plant, safety equipment) plus an error code that tells you how far to trust the answer. For up to 50 VINs at once there is a batch call.
It is the same database behind NHTSA's own decoder page, filled from what manufacturers submit for vehicles sold in the US. That explains both its strengths and its gaps. Below are the calls worth knowing, how to read the error codes, which fields you can rely on, how to batch and cache, and working code in JavaScript and Python.
Key takeaways
- Use
DecodeVinValues(flat object) orDecodeVINValuesBatch(up to 50 VINs per POST); both needformat=json. ErrorCodeis a comma-separated string: "0" is a clean decode, "1" a wrong check digit, and data can come back even with errors.- Every value is a string, and an empty string means NHTSA has no data, not that the feature is absent.
- Make, model, model year and body class are dependable for US-market vehicles; drive type, trim and many equipment fields are often empty.
- Cache decoded VINs on your side: the data for a VIN rarely changes, and the service limits automated traffic.
Your first request to the NHTSA VIN decoder API
No key, no sign-up. A plain GET works from a terminal:
The answer has four top-level keys: Count, Message, SearchCriteria and Results, an array with one object per VIN. Trimmed to the fields most people use, the result for this 2003 Honda Accord looks like this:
Three things to notice. Every value is a string, including ModelYear and EngineCylinders, so parse numbers yourself. Missing data is an empty string, and the Message spells out what that means: NHTSA has no data for that variable, which is different from "the car does not have it". And some fields carry Not Applicable (for example BedType on a coupe), which is again different from empty. The DriveType of this Accord is empty although the car obviously drives its front wheels.
The vPIC endpoints worth knowing
The API page lists more than twenty calls. All accept format=json, xml or csv; JSON is what you want. The ones that matter for VIN work:
| Call | URL pattern | Use it for |
|---|---|---|
| DecodeVinValues | /DecodeVinValues/{vin}?format=json&modelyear={y} | One VIN, one flat object. The default choice. |
| DecodeVin | /DecodeVin/{vin}?format=json | Same data as rows of Variable, Value, VariableId. Handy if you store by variable id. |
| DecodeVinValuesExtended | /DecodeVinValuesExtended/{vin}?format=json | Flat object with extra NHTSA program variables. |
| DecodeVINValuesBatch | POST /DecodeVINValuesBatch/ | Up to 50 VINs per request, same output as DecodeVinValues. |
| DecodeWMI | /DecodeWMI/{wmi}?format=json | Who owns a manufacturer code such as 1HG. |
| GetAllMakes | /GetAllMakes?format=json | Every make in the database. |
| GetModelsForMake | /GetModelsForMake/{make}?format=json | All models of a make. |
| GetModelsForMakeYear | /GetModelsForMakeYear/make/{make}/modelyear/{y}?format=json | Models of a make in one model year; add /vehicletype/{type} to narrow. |
| GetVehicleTypesForMake | /GetVehicleTypesForMake/{make}?format=json | Passenger car, truck, MPV, motorcycle and so on for a make. |
| GetVehicleVariableValuesList | /GetVehicleVariableValuesList/{variable}?format=json | Every allowed value of a variable, for example all body classes. |
The make and model lists are useful for dropdowns, but they are large and full of entries that never appear on a road (trailer makers, small coachbuilders). GetModelsForMakeYear with a vehicle type is the practical way to build a make, year, model picker.
GetVehicleVariableValuesList deserves a mention because it gives you the full vocabulary before you write a mapping: 71 body classes, from Sedan/Saloon to Incomplete - Chassis Cab (Single Cab), 23 drive types and 14 primary fuel types at the time of writing. Map from that list, not from the handful of values you happened to see in testing.
Reading ErrorCode and ErrorText
ErrorCode is not a number but a string that can hold several codes separated by commas, such as "1,8,400". ErrorText repeats them with descriptions, separated by semicolons, and AdditionalErrorText sometimes names the exact problem (Invalid character(s): 9:D.). Split the code string and decide per code:
| Code | Meaning | What we would do |
|---|---|---|
| 0 | Decoded clean, check digit correct. | Use the data. |
| 1 | Check digit (position 9) does not calculate. | Data still comes back. Accept it for non-US cars, flag it for US-market cars. |
| 2, 3, 4 | vPIC corrected one position; SuggestedVIN shows it (with ! where unsure). | Do not silently replace the VIN. Show the suggestion to a person. |
| 5 | Errors in several positions. | Treat the decode as unreliable. |
| 6 | Incomplete VIN. | Expected for partial VINs with *. Otherwise a typo. |
| 7 | Manufacturer not registered with NHTSA for the US. | Expect little or no data. Not an error in your code. |
| 8 | No detailed data available currently. | Keep what came back (often only the make), try again in a few months. |
| 11 | Position 10 is not a valid model year code. | Treat the year as unknown. |
| 12 | The model year you sent does not match position 10. | Check which one is wrong before storing either. |
| 14 | Some characters could not be decoded from the manufacturer data. | Use the fields that are filled. |
| 400 | Invalid characters present. | Reject the input (I, O, Q, symbols). |
The important point is that an error code does not mean an empty answer. Change one character of the Accord's VIN so the check digit fails, and vPIC still returns Honda, Accord, Coupe and six cylinders, now with error 1. Whether to keep that data is your decision, and it should depend on where the VIN came from: a typed VIN from a user deserves a second look, a VIN read from an auction lot of a European car does not.
Sending modelyear matters more than it seems. The model year code in position 10 repeats every 30 years, and NHTSA's documentation recommends always sending the year so the decoder looks in the right range. In our tests a made-up Kia VIN without the parameter decoded to 1993; with modelyear=2023 it decoded to 2023 and added error 12. If you know the year from the registration or the listing, send it.
Partial VINs and the asterisk
vPIC also decodes VINs shorter than 17 characters, with * standing for the characters you do not have. 1HGCM826*3A (the first 11 characters with the check digit replaced) returns Honda, Accord, 2003, Coupe and six cylinders, with error 6, incomplete VIN. That is useful when an old document or a photo shows only part of the VIN, or when you want the specification of a model line without a real car. The VehicleDescriptor field in every answer is exactly this pattern for the VIN you sent.
Which fields are reliable and which are often empty
vPIC holds what manufacturers file with NHTSA, and they file the identity of the vehicle carefully and the details less so. From the auction VINs we decode, this is how we would rank the fields we use:
| Field | Typical values | How often filled |
|---|---|---|
Make, Model, ModelYear | HONDA, Accord, 2003 | Almost always for US-market vehicles |
VehicleType | PASSENGER CAR, TRUCK, MULTIPURPOSE PASSENGER VEHICLE (MPV) | Almost always, even for partial decodes |
BodyClass | Sedan/Saloon, Coupe, Sport Utility Vehicle [SUV]/Multipurpose Vehicle [MPV] | Usually |
EngineCylinders, DisplacementL | 6, 2.998832712 | Usually; empty for electric cars |
FuelTypePrimary | Gasoline, Diesel, Electric, Flexible Fuel Vehicle (FFV) | Usually |
DriveType | FWD/Front-Wheel Drive, AWD/All-Wheel Drive, 4x2 | Often empty, especially before about 2010 |
Trim, Series | EX-V6, XLE | Often empty |
EngineHP, TransmissionStyle | 240, Automatic | Often empty |
Safety equipment (ABS, ESC, BlindSpotMon) | Standard, Optional | Mostly empty on older cars |
A few gotchas when you map these values into your own fields:
- `4x2` is not a drive type you can map. It says two wheels are driven, not which two. Leave front or rear empty unless another source tells you.
- Hybrids report their engine fuel first. A Prius decodes with
FuelTypePrimaryGasoline,FuelTypeSecondaryElectric andElectrificationLevelStrong HEV. Read all three before you label a car. - Body classes are combined labels.
Hatchback/Liftback/Notchbackcovers three body styles;Sedan/Saloonis one. Map from the full list of values. - `Make` is upper case and `Model` is not. Normalise case before you match against your own catalogue.
- Displacement is not rounded. 2.998832712 is a 3.0-litre engine; round for display, keep the raw value.
Decoding a VIN in JavaScript and Python
A single decode in JavaScript (Node 18 or later), with a timeout, the error codes split, and empty or Not Applicable values turned into null:
The batch call takes a POST with two form fields: format=json and DATA, a string of VINs separated by semicolons, each optionally followed by a comma and its model year. The limit is 50 VINs per request, and the results come back in the same flat shape as DecodeVinValues. In Python with requests:
Rate limits, caching and being a good citizen
NHTSA does not publish a number of requests per minute. The documentation says only that automated traffic is subject to a rate control mechanism to protect the service. In practice that means: use the batch call instead of 50 single calls, keep concurrency low (one or two requests in flight), set timeouts, and back off on errors and timeouts rather than retrying at once. While writing this guide the service answered 503 for a while after a burst of test requests, so treat 5xx answers and timeouts as temporary and make the decode step retryable later. Do not decode the same VIN twice.
Caching is easy because a VIN's decoded data hardly ever changes. The response itself comes with cache-control: no-cache, so the caching is up to you:
- Store the raw
Resultsrow per VIN (and model year, if you sent one) in your own table, with the date of the decode. - Serve repeated lookups from that table. A decode from last year is as good as one from today for make, model and body.
- Re-decode only rows with error 8 or many empty fields, every few months: manufacturers keep filing data, and a VIN that decoded to the make alone may decode fully later.
- Keep the raw row next to your mapped fields, so a better mapping later does not need a new request.
Coverage gaps: where vPIC has little to say
vPIC describes vehicles made or imported for sale in the United States. Outside that, expect gaps:
- Cars built for other markets. A European-market Renault or a Korean domestic-market car usually decodes to the make and the model year at best, often with error 7 or 8. The manufacturer never filed its descriptor scheme with NHTSA.
- Older vehicles. Detail thins out the further back you go, and VINs from before the 1981 model year are not standardised at all.
- Trucks, trailers and incomplete vehicles. Multi-stage vehicles decode the chassis, not the body built on it.
- Grey imports. A car imported privately into the US decodes as whatever the original market filed, which may be nothing.
For a European or Asian car, the reliable sources of model detail are the manufacturer's own records or the listing the car came from. vPIC is still worth a call: the make and model year it returns can confirm that the VIN is plausible. Our guide on how to decode a VIN covers what you can read from the characters without any API.
How we use vPIC for Copart and IAAI records
Copart and IAAI lots do not always carry every specification. A listing may lack the body type, the drive, the fuel or the cylinder count, depending on how the lot was entered. In the AuctionsAPI Copart and IAAI feed we fill eight such specifications from vPIC by VIN, only when they are missing at the source: body type, vehicle type, make, model, model year, drive wheels, fuel type and cylinder count.
Two rules keep this honest. A value is added only when vPIC returns one: an empty DriveType stays empty, and a cylinder count is taken only when it is a positive number. And nothing is guessed from similar cars or other model years. If vPIC has no data, the field stays null in the vehicle object, and your code should treat null as unknown, the same way you treat an empty vPIC value.
Where vPIC fits in a VIN pipeline
Validate the VIN locally first (length, allowed characters, check digit for North American cars), decode it once with vPIC, store the raw row, and map only the fields you trust. vPIC tells you what the car is; it says nothing about what happened to it. For that you need history: auction lots by VIN through /search-vin (our guide to auction history by VIN shows what to look for), or title and accident data from a report provider.
Questions people ask
Is the NHTSA VIN decoder API free?
Yes. vPIC is a public service of the US National Highway Traffic Safety Administration. It needs no key and no account. NHTSA applies an automated rate control to protect the service, so cache results, use the batch call for many VINs and keep the number of parallel requests low.
How many VINs can I decode at once with vPIC?
The DecodeVINValuesBatch call accepts up to 50 VINs per POST request. Send them in the DATA form field as vin,modelyear pairs separated by semicolons, with format=json. For larger lists, split them into chunks of 50 and send the chunks one after another rather than in parallel.
What does ErrorCode 1 mean in vPIC?
It means the check digit in position 9 does not match the calculation from the other characters. The VIN may be mistyped, or it may belong to a car built for a market where the check digit is not required. vPIC still returns whatever it could decode, so decide by the source of the VIN whether to use it.
Can vPIC decode European or Asian VINs?
Only partly. vPIC contains data that manufacturers filed for the US market. A car built for Europe, Korea or Japan usually decodes to the make and model year at most, often with error 7 or 8. For full specifications use the manufacturer's records or the original listing.
Why is DriveType empty in my vPIC result?
Because the manufacturer did not file that variable for the vehicle, which is common for older models. An empty value means NHTSA has no data, not that the car lacks the feature. Leave the field unknown in your own data rather than filling it from a similar model.


