Mapping NHTSA vPIC Fields: A Developer Guide to the DecodeVinValues Response
If you've called NHTSA's free vPIC API, you've seen a response with 150+ fields, most of them empty. This guide covers which fields matter, what they mean, and the traps to avoid when mapping them into your own data mode
If you've called NHTSA's free vPIC API, you've seen a response with 150+ fields, most of them empty. This guide covers which fields matter, what they mean, and the traps to avoid when mapping them into your own data model.
The endpoint
GET https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValues/{VIN}?format=json
DecodeVinValues returns a flat object (one key per variable), which is easier to map than DecodeVin, which returns a list of {Variable, Value, VariableId} rows. There's also DecodeVinValuesBatch (POST, up to 50 VINs) for bulk work.
Optionally pass &modelyear=2020 when you already know the year; it helps for pre-1980 or incomplete VINs.
Always check ErrorCode first
"ErrorCode": "0",
"ErrorText": "0 - VIN decoded clean. Check Digit (9th position) is correct"
-
ErrorCodeis a string and can hold several comma-separated codes, e.g."1,400". -
0means clean. -
1means the check digit is wrong (the rest may still decode). - Other codes cover invalid characters, incomplete VINs, or no matching pattern.
Treat anything other than "0" as "decoded with warnings" and show ErrorText to the user instead of silently trusting the fields.
Identity fields
| Field | Notes |
|---|---|
Make, Model, ModelYear
|
Core identity. ModelYear is the product year, not build date. |
Manufacturer |
Legal manufacturer name; differs from Make for many brands. |
Trim, Series
|
Often empty; manufacturers don't always encode trim in the VIN. |
VehicleType |
e.g. PASSENGER CAR, MULTIPURPOSE PASSENGER VEHICLE (MPV), TRUCK. |
BodyClass |
e.g. Sedan/Saloon, Sport Utility Vehicle (SUV)/Multi-Purpose Vehicle (MPV). |
Trim is the field users want most and get least. Don't make it required in your UI.
Powertrain
| Field | Notes |
|---|---|
EngineCylinders, DisplacementL, DisplacementCC
|
Strings; DisplacementL may have many decimals. Round for display. |
FuelTypePrimary, FuelTypeSecondary
|
Secondary is set for flex-fuel or hybrids. |
ElectrificationLevel |
e.g. BEV, HEV, PHEV. Useful for EV filters. |
DriveType |
e.g. 4WD/4-Wheel Drive/4x4, FWD/Front-Wheel Drive. |
TransmissionStyle |
Frequently empty. |
EngineModel, EngineHP
|
Inconsistent coverage. |
Plant fields
PlantCity, PlantState, PlantCountry, PlantCompanyName. These come from the manufacturer's plant code (VIN position 11). Country of assembly here can differ from the country implied by the WMI.
Safety equipment
Fields like AirBagLocFront, AirBagLocSide, ABS, ESC, TPMS, BlindSpotMon, AdaptiveCruiseControl. Coverage is uneven, especially for advanced driver-assistance features. An empty field means "not reported," not "not equipped." Never render empty as "No."
Mapping tips
-
Empty strings, not nulls. Most unset fields come back as
"". Normalize tonullon ingest. -
Everything is a string. Parse numbers (
ModelYear,EngineCylinders,Doors) yourself and keep the raw value if parsing fails. - "Not Applicable". Some fields return this literal text. Treat it as null for display logic.
- Cache by VIN pattern, not just full VIN. The first 11 characters (minus check digit) largely determine the decode; caching on positions 1-8 + 10-11 can save a lot of requests for fleets.
- Be polite. It's a free public service. Batch where you can, cache aggressively, and back off on errors.
-
Keep the source. Store
ErrorCode,ErrorText, and a timestamp so you can re-decode when NHTSA updates manufacturer data.
Minimal TypeScript mapper
type Decoded = {
vin: string; make: string | null; model: string | null;
modelYear: number | null; bodyClass: string | null;
fuel: string | null; plantCountry: string | null;
warnings: string | null;
};
const n = (v?: string) => (!v || v === 'Not Applicable' ? null : v);
export function mapVpic(r: Record<string, string>): Decoded {
return {
vin: r.VIN,
make: n(r.Make), model: n(r.Model),
modelYear: r.ModelYear ? Number(r.ModelYear) : null,
bodyClass: n(r.BodyClass), fuel: n(r.FuelTypePrimary),
plantCountry: n(r.PlantCountry),
warnings: r.ErrorCode === '0' ? null : r.ErrorText,
};
}
What vPIC doesn't give you
No title brands, accident history, owner count, mileage, or recall status. Recalls have their own NHTSA API (by make/model/year) and a VIN lookup on nhtsa.gov; title history comes from NMVTIS-approved providers.
Disclosure: I build VIN Lookup, a free decoder that uses this same vPIC data.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.