Why Build a VIN-Based Flow
Most tire-fitment checkout flows start with a year/make/model/trim picker. It works, but it's friction: four or five dropdowns before a customer even sees a tire. Most vehicle owners don't reliably know their trim level — but they do have their VIN, printed right on their registration and their door jamb.
This tutorial walks through replacing that picker with a single VIN field, using the Tire API's /by_vin endpoint (shipped in v1.2.0) to decode the vehicle and pull its factory tire sizes in one call.
What You'll Need
- A Tire API key (any plan — VIN lookup is included on Free, Starter, Pro, and Business)
- A place to make server-side requests (this endpoint should be called from your backend, not the browser, to keep your API key off the client)
Step 1: Make the Request
The endpoint is a simple GET with the VIN in the path:
curl -H "x-api-key: your-api-key-here" \\
"https://tire.vdim.app/api/v1/by_vin/1HGCM82633A004352"
In Node.js, that looks like:
async function lookupByVin(vin) {
const res = await fetch(`https://tire.vdim.app/api/v1/by_vin/${vin}`, {
headers: { "x-api-key": process.env.TIRE_API_KEY }
});
if (!res.ok) {
return handleVinError(res.status, await res.json());
}
const data = await res.json();
return data; // decoded vehicle fields + indexed tireSizes list
}
Under the hood, the VIN is decoded against the NHTSA vPIC dataset, then matched to the fitment database by year, make, and model. The response gives you the decoded vehicle plus an indexed tireSizes list ready to render.
Step 2: Validate Before You Call
A VIN is always 17 characters, and the letters I, O, and Q never appear in a valid VIN (they're excluded to avoid confusion with 1 and 0). Catching an obviously malformed VIN client-side saves a round trip:
function isPlausibleVin(vin) {
return /^[A-HJ-NPR-Z0-9]{17}$/i.test(vin);
}
Worth knowing: the API itself checks length and character set server-side too, and returns a 400 before consuming any quota if the VIN is syntactically invalid. What it does not check in this version is the VIN's check digit (position 9) — so a well-formed but fictional VIN can still pass basic validation and come back as a decode failure. Your UI should treat both cases (client-side format failure and a server-side decode miss) with the same fallback, covered in Step 4.
Step 3: Handle the Three Failure Modes
There are three distinct outcomes to design for beyond a clean 200:
- 400 — the VIN isn't 17 valid characters. This is a format problem, not a lookup problem — show a validation message right on the field.
- 404 with code
DECODE_EMPTY— the VIN is well-formed but couldn't be decoded (unrecognized manufacturer sequence, VIN doesn't map to a known fitment, etc). This is the case to fall back gracefully on, not treat as a hard error. - 429 — your VIN quota for the month is exhausted. This is an operational issue on your end, not the customer's — log it and alert yourself, don't surface raw API errors to the customer.
function handleVinError(status, body) {
switch (status) {
case 400:
return { fallback: "manual", reason: "invalid_format" };
case 404:
return { fallback: "manual", reason: "decode_empty" };
case 429:
console.error("VIN quota exhausted — check /api/v1/usage");
return { fallback: "manual", reason: "quota_exhausted" };
default:
return { fallback: "manual", reason: "unknown" };
}
}
Step 4: Always Keep a Manual Fallback
Don't make VIN entry the only path. Some customers won't have their VIN handy, and some VINs won't decode. The most reliable pattern is: VIN field first, with a "don't know your VIN? Search by year/make/model instead" link that drops back to your existing picker. That way VIN lookup is a shortcut for the customers who can use it, not a wall for the ones who can't.
Step 5: Watch Your Quota
VIN lookups are metered separately from your regular daily request limit — one call to /by_vin uses one daily request unit and one VIN quota unit. Monthly VIN quotas by plan:
| Plan | Monthly VIN Quota |
|---|---|
| Free | 10 (+ 50 one-time welcome bonus on signup) |
| Starter | 500 |
| Pro | 5,000 |
| Business | 20,000 |
Every response carries quota telemetry in its headers — X-VIN-Quota-Limit, X-VIN-Quota-Remaining, X-VIN-Quota-Reset, and (on Free) X-VIN-Bonus-Remaining. If you're running VIN lookup at checkout scale, read X-VIN-Quota-Remaining on every response and alert yourself well before it hits zero — not after your checkout flow starts returning 429s to real customers. You can also poll GET /api/v1/usage, which returns a full vinQuota block. Quotas reset on the first of each month, America/Toronto time.
Putting It Together
A complete flow looks roughly like:
1. Customer enters VIN
2. Client-side format check (17 chars, no I/O/Q)
3. If format check fails → show manual picker
4. Otherwise, call /by_vin server-side
5. On 200 → render decoded vehicle + tire sizes, let customer confirm
6. On 400/404/429 → fall back to manual picker, log the reason
7. Customer proceeds to checkout with confirmed tire size
The confirm step in #5 is worth keeping even on a successful decode — showing "2021 Honda Civic LX, is this right?" catches the rare mismatched VIN before it turns into a returned order.
Try It
- Swagger UI: https://tire.vdim.app/api/docs/v1
- API guide: https://tire.vdim.app/api/docs
- Sign up: https://account-tire.vdim.app/signin/signup
Support & Resources
Need help? Contact support at support@vdimtech.com or visit the API portal at https://tire.vdim.app for interactive documentation.
Conclusion
VIN-based lookup won't replace manual entry entirely, but for the customers who have their VIN on hand, it turns a multi-step form into a single field and a confirmation click. Pair it with a solid fallback and a bit of quota monitoring, and it's a straightforward win for checkout conversion.