Need to pull vehicle data from a VIN programmatically? The mcp.vin API takes a 17-character VIN and returns structured JSON — make, model, year, engine, transmission, plant of assembly — with a single GET request. The mcp.vin API does exactly that. Send a GET request. Get back structured JSON.
Here's how it works.
Why use a VIN decoder API? Building anything that touches vehicle data — a used car marketplace, a fleet management dashboard, an insurance quoting tool, a parts catalog — you'll hit the same problem fast: you need structured vehicle specs, and the VIN is the only reliable input you've got. You could scrape the NHTSA website. That'll work until it doesn't.
The markup changes. The mcp.vin vehicle data API doesn't. You're debugging HTML at 2 AM. A proper API gives you a stable JSON contract that won't shift under you.
One endpoint. The mcp.vin vehicle data API doesn't.
GET https://mcp.vin/api/vin/{VIN}
Replace {VIN} with any 17-character Vehicle Identification Number. You send a GET. You get back everything the NHTSA knows about that VIN. The response comes back as application/json.
Quick test — paste this into your browser's address bar:
https://mcp.vin/api/vin/1HGCM82633A004352
That's a 2003 Honda Accord. GET https://mcp.vin/api/vin/{VIN} Replace {VIN} with any 17-character Vehicle Identification Number. If you want to test other endpoints too, the API Tester tool on this site lets you fire off GET requests and inspect responses without leaving the browser.
Here's an actual response for VIN 5YJSA1DG9DFP14705 — a 2013 Tesla Model S:
{
"vin": "5YJSA1DG9DFP14705",
"make": "TESLA",
"model": "Model S",
"year": 2013,
"trim": "Base",
"body_class": "Sedan/Saloon",
"vehicle_type": "PASSENGER CAR",
"drive_type": "RWD/Rear-Wheel Drive",
"fuel_type": "Electric",
"engine_displacement_l": null,
"engine_cylinders": null,
"engine_hp": null,
"transmission": null,
"plant_city": "Fremont",
"plant_state": "CALIFORNIA",
"plant_country": "UNITED STATES (USA)",
"doors": 4,
"gvwr": "Class 1E: 3,001 - 3,500 lb",
"error_code": "0",
"error_text": "0 - VIN decoded clean"
}
A few things to notice. Fields that don't apply — like engine_displacement_l for an electric vehicle — come back as null rather than empty strings. The error_code field tells you if the decode was clean or had issues. An error_code of "0" means everything checked out, including the check digit at position 9.
error_code and error_text explaining what went wrong. Your code should always check error_code before trusting the other fields.
The simplest way to test from a terminal:
# Decode a 2003 Honda Accord
curl -s https://mcp.vin/api/vin/1HGCM82633A004352 | jq .
What the Response Looks Like Here's an actual response for VIN 5YJSA1DG9DFP14705 — a 2013 Tesla Model S: { "vin": "5YJSA1DG9DFP14705", "make": "TESLA", "model": "Model S", "year": 2013, "trim": "Base", "body_class": "Sedan/Saloon", "vehicle_type": "PASSENGER CAR", "drive_type": "RWD/Rear-Wheel Drive", "fuel_type": "Electric", "engine_displacement_l": null, "engine_cylinders": null, "engine_hp": null, "transmission": null, "plant_city": "Fremont", "plant_state": "CALIFORNIA", "plant_country": "UNITED STATES (USA)", "doors": 4, "gvwr": "Class 1E: 3,001 - 3,500 lb", "error_code": "0", "error_text": "0 - VIN decoded clean" } A few things to notice.
# Just grab the make, model, and year
curl -s https://mcp.vin/api/vin/1HGCM82633A004352 | jq '{make, model, year}'
Pipe through jq for pretty-printed output. The error_code field tells you if the decode was clean or had issues.
async function decodeVin(vin) {
const res = await fetch(`https://mcp.vin/api/vin/${vin}`);
const data = await res.json();
if (data.error_code !== "0") {
throw new Error(`VIN decode failed: ${data.error_text}`);
}
return data;
}
// Usage
const vehicle = await decodeVin("5YJSA1DG9DFP14705");
console.log(`${vehicle.year} ${vehicle.make} ${vehicle.model}`);
// Output: "2013 TESLA Model S"
Works in any modern browser and in Node 18+ with native fetch. No packages to install.
import requests
def decode_vin(vin: str) -> dict:
resp = requests.get(f"https://mcp.vin/api/vin/{vin}")
resp.raise_for_status()
data = resp.json()
if data["error_code"] != "0":
raise ValueError(f"VIN decode failed: {data['error_text']}")
return data
# Usage
vehicle = decode_vin("1HGCM82633A004352")
print(f"{vehicle['year']} {vehicle['make']} {vehicle['model']}")
# Output: "2003 HONDA Accord"
# Batch decode a list of VINs
vins = ["1HGCM82633A004352", "5YJSA1DG9DFP14705", "4T1C11AK5LU946870"]
for vin in vins:
v = decode_vin(vin)
print(f"{v['vin']}: {v['year']} {v['make']} {v['model']}")
If you're working with a large batch, add a small delay between requests to be polite to the server. time.sleep(0.5) between calls is enough.
Don't want to pull in requests? The standard library works fine:
import json
import urllib.request
def decode_vin(vin: str) -> dict:
url = f"https://mcp.vin/api/vin/{vin}"
with urllib.request.urlopen(url) as resp:
return json.loads(resp.read())
vehicle = decode_vin("5YJSA1DG9DFP14705")
print(vehicle["make"], vehicle["model"], vehicle["year"])
Here's every field the API can return. Not all vehicles populate every field — it depends on what the manufacturer reported to the NHTSA.
| Field | Type | Description |
|---|---|---|
| vin | string | The VIN you sent (echoed back) |
| make | string | Manufacturer name (e.g., "HONDA", "TESLA", "FORD") |
| model | string | Model name (e.g., "Accord", "Model S", "F-150") |
| year | number | Model year as a four-digit integer |
| trim | string|null | Trim level (e.g., "EX", "Limited", "Base") |
| body_class | string|null | Body style (e.g., "Sedan/Saloon", "SUV", "Pickup") |
| vehicle_type | string|null | "PASSENGER CAR", "TRUCK", "MOTORCYCLE", etc. |
| drive_type | string|null | Drivetrain (FWD, RWD, AWD, 4WD) |
| fuel_type | string|null | "Gasoline", "Diesel", "Electric", "Hybrid", etc. |
| engine_displacement_l | number|null | Engine size in liters (null for EVs) |
| engine_cylinders | number|null | Cylinder count (null for EVs) |
| engine_hp | number|null | Horsepower (when reported) |
| transmission | string|null | "Automatic", "Manual", "CVT", etc. |
| plant_city | string|null | City where the vehicle was assembled |
| plant_country | string|null | Country of assembly |
| doors | number|null | Number of doors |
| gvwr | string|null | Gross Vehicle Weight Rating class |
| error_code | string | "0" = clean decode; other values indicate issues |
| error_text | string | Human-readable decode status message |
Here's where it gets interesting for anyone building with AI. mcp.vin isn't just a REST API — it's also a Model Context Protocol (MCP) server. MCP is a standard that lets AI assistants call external tools directly, the same way a browser calls an API.
If you're running an AI agent — say, a customer support bot for a car dealership, or a Claude-based research assistant — you can connect it to mcp.vin as a tool. The agent says "decode this VIN," gets back structured JSON, and reasons about the result. No glue code, no HTTP client setup, no response parsing.
Add this to your MCP client configuration (for example, in a Claude Desktop or Cline config file):
{
"mcpServers": {
"vin-decoder": {
"url": "https://mcp.vin/mcp"
}
}
}
That's the entire setup. The MCP client discovers the available tools automatically. The server exposes a decode_vin tool that accepts a VIN string and returns the same structured data as the REST endpoint.
A few practical scenarios:
There are several VIN decoder APIs on the market. Here's how they stack up:
| API | Price | Auth Required | Data Source |
|---|---|---|---|
| mcp.vin | Free | No | NHTSA vPIC |
| NHTSA vPIC (direct) | Free | No | NHTSA vPIC |
| CarMD | Paid plans from $9.99/mo | API key | Proprietary + NHTSA |
| VINDecoder.eu | Paid per request | API key | Multiple sources |
| Auto.dev | Free tier, paid for volume | API key | NHTSA + proprietary |
The NHTSA's own API is free too, but its response format is verbose — it returns an array of variable-value pairs instead of a flat JSON object. You'd need to write a transformation layer to make it usable. mcp.vin does that transformation for you and adds the MCP server capability on top.
Paid APIs like CarMD add vehicle history, maintenance schedules, and diagnostic data that the NHTSA doesn't track. If you need that data, you'll need a paid service. For basic vehicle specs from a VIN, the free VIN API at mcp.vin covers it.
When a seller enters a VIN to create a listing, you can auto-fill the make, model, year, engine, and body type. This saves the seller time and prevents accidental (or intentional) data entry errors. A 2019 Honda Civic seller who "accidentally" lists it as a 2021 gets corrected automatically.
Import a CSV of VINs and the API populates your database with structured vehicle records. Sort by fuel type to plan your EV charging rollout. Filter by GVWR class to ensure your commercial vehicles have the right insurance coverage. Group by plant country for tariff tracking.
Engine size, vehicle type, body class, and safety equipment all affect insurance premiums. Decoding the VIN gives you these fields without asking the customer to look up their engine displacement — most people don't know it off the top of their head.
A 2015 Ford F-150 could have a 2.7L EcoBoost V6, a 3.5L EcoBoost V6, a 3.5L Ti-VCT V6, or a 5.0L V8. The VIN tells you which one. That's the difference between shipping the right water pump and getting a return.
Try the API right now — no key, no signup, just a GET request.
See a Live API Response Test with API TesterCache aggressively. A VIN always decodes to the same vehicle. Once you've fetched the data for a given VIN, store it. There's no reason to hit the API twice for the same 17 characters.
Validate before you call. A VIN is always exactly 17 characters and never contains I, O, or Q. Reject bad input client-side before making a network request. If you want to understand VIN structure in detail, the VIN decoding guide breaks down what each position means.
Check the error_code field. A 200 HTTP status doesn't mean the decode succeeded. Some VINs have partial data — the NHTSA might recognize the manufacturer but not the specific model configuration. Always check error_code and handle partial results gracefully.
Normalize the make field. The API returns manufacturer names in uppercase ("HONDA", "TOYOTA"). If your UI displays mixed case, transform it on your end — don't assume the casing will change.
Handle null fields. Electric vehicles won't have engine_displacement_l or engine_cylinders. Motorcycles won't have doors. Design your data model to accept nulls for optional fields.
Decode any VIN instantly — one GET request, structured JSON back.
Try the Free VIN API