DEV Community

Devil Scrapes
Devil Scrapes

Posted on

NHTSA decodes any 17 characters you give it, valid or not

Quick answer

NHTSA runs a free, keyless VIN decoder — vPIC — that turns a 17-character VIN into make, model, year, trim, engine and assembly plant. No signup, no key.

It also has a property worth knowing before you build on it: it decodes any 17 characters you give it, valid or not, and hands back roughly 150 mostly-empty fields with no indication that the VIN was garbage.

That is the Bulk VIN Decoder & Recall Checker. Give it VINs; get clean flat rows, optionally joined with recall and crash-safety data.

The check digit is free, and it belongs before the network call 🔢

A VIN is not an opaque string. ISO-3779 defines position 9 as a check digit, computed from the other sixteen characters by a fixed weight-and-transliteration table. A typo'd VIN fails that check arithmetically, on your own machine, in microseconds.

vPIC will not tell you. Hand it a mistyped VIN and it returns HTTP 200 with a decode — mostly empty strings, occasionally a plausible-looking make — and no error field saying "this is not a real VIN."

So the order of operations matters commercially, not just technically:

  1. Validate the check digit locally, before any network call and before any billed event
  2. Batch-decode only the survivors
  3. Normalise the payload

Put validation after the call and your customer pays for rows describing a vehicle that does not exist. Put it before, and a typo costs nothing. Same API, same data, entirely different bill.

Empty string is not null, and pretending otherwise is a data bug 🧹

vPIC returns absent fields as "", not null, across roughly 150 columns. Loaded straight into a warehouse that is 150 columns of empty strings that every downstream query has to special-case — and WHERE trim IS NULL silently misses all of them.

We convert empty to a real null on the way out. It is a small transformation that removes a whole class of quiet analytical error later.

Recalls and safety ratings are a different join 🔗

Recall and NCAP crash-safety data are keyed by make / model / year, not by VIN — they describe a vehicle type, not your specific car. So the enrichment is a join, and it is opt-in, because it costs additional requests per distinct make/model/year rather than per VIN.

Worth being precise about in your own reporting: a recall row means "this model was recalled", not "this vehicle is unrepaired". The API cannot tell you the latter.

Who this is for 🎯

  • Dealers and marketplaces — normalise inventory from VIN alone.
  • Insurers and lenders — decode and risk-check at application time.
  • Fleet operators — reconcile a fleet's actual composition against records.
  • Automotive data teams — build make/model/year dimensions without a paid feed.

The honest limitations 🚧

  • US VINs. vPIC is NHTSA's, so coverage outside the US market is thin to absent.
  • Trim and engine detail vary by manufacturer — some populate richly, some barely.
  • Recalls and ratings are per make/model/year, not per vehicle, and not a repair status.
  • Rate limits are real; we retry 408/429/5xx with exponential backoff and honour Retry-After.

Pricing

$0.20 per run plus $0.0025 per row — $2.70 per 1,000. A VIN that fails local check-digit validation never becomes a billed row.

Bulk VIN Decoder & Recall Checker on Apify


Built by Devil Scrapes. We handle the check digit that saves you money, the 150 empty strings that should be nulls, and the join that is not keyed the way you'd expect.

Top comments (0)