The ninth character of a North American VIN is not part of the vehicle description. It is a check digit: a value computed from the other 16 characters so that a typo or a crude alteration is likely to be caught. If you build anything that accepts VINs (a form, a scraper, an inventory import), validating it is a few lines of code and saves you a wasted API call.
The rules
Since the 1981 model year, vehicles sold in the U.S. use a 17-character VIN defined by NHTSA (49 CFR Part 565). The letters I, O and Q are never used, so they cannot be confused with 1 and 0.
The check digit works like this:
- Transliterate each character to a number. Digits keep their value. Letters map as follows:
| Letters | Value |
|---|---|
| A, J | 1 |
| B, K, S | 2 |
| C, L, T | 3 |
| D, M, U | 4 |
| E, N, V | 5 |
| F, W | 6 |
| G, P, X | 7 |
| H, Y | 8 |
| R, Z | 9 |
- Multiply each value by its position weight:
8 7 6 5 4 3 2 10 0 9 8 7 6 5 4 3 2
Position 9 has weight 0, because it is the check digit itself.
- Sum the products and take the remainder mod 11.
- A remainder of 0–9 is the check digit. A remainder of 10 is written as X.
Worked example
Take the commonly cited sample VIN 1M8GDM9AXKP042788. The weighted sum is 351, and 351 mod 11 = 10, so the check digit should be X, which is exactly what sits in position 9. Valid.
Code
const MAP = { A:1,B:2,C:3,D:4,E:5,F:6,G:7,H:8,J:1,K:2,L:3,M:4,N:5,P:7,R:9,S:2,T:3,U:4,V:5,W:6,X:7,Y:8,Z:9 };
const WEIGHTS = [8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2];
function isValidVin(raw) {
const vin = raw.trim().toUpperCase();
if (!/^[A-HJ-NPR-Z0-9]{17}$/.test(vin)) return false;
let sum = 0;
for (let i = 0; i < 17; i++) {
const c = vin[i];
const v = /\d/.test(c) ? Number(c) : MAP[c];
sum += v * WEIGHTS[i];
}
const r = sum % 11;
return vin[8] === (r === 10 ? 'X' : String(r));
}
console.log(isValidVin('1M8GDM9AXKP042788')); // true
What a passing check digit does and does not tell you
- It catches most single-character typos and many transpositions, which is why it is worth running before you call a decoder API.
- It does not prove the VIN belongs to a real vehicle, and it does not prove the plate on the dash matches the car. A cloned VIN copied from a legitimate vehicle will pass perfectly.
- The check-digit requirement is a North American rule. Many vehicles built only for other markets do not compute position 9 this way, so treat a failure on an imported car as "verify by hand", not "fake".
Next step: decoding
Once a VIN passes, the rest of the string can be decoded: positions 1–3 identify the manufacturer, position 10 is the model year, and position 11 is the assembly plant. NHTSA publishes this data for free through its vPIC API. If you just want to see the result for a single VIN without writing code, I built Free VIN Lookup, which runs the same public NHTSA data with no signup.
Disclosure: I build Free VIN Lookup.
Top comments (0)