VIN Decoder API

By Vehicle API
VIN Decoder API

You need to turn a 17-character VIN into concrete specs your app can trust—year, make, model, trim, engine, transmission, and where it was built. By the end of this guide, you’ll call the VIN Decoder API endpoint, parse the envelope response consistently, handle nulls, cache results, and understand coverage so you know when to fall back or enrich with other vehicle data sources in your stack.

What the VIN Decoder returns

The VIN Decoder API resolves a 17-character VIN into structured attributes sourced from NHTSA vPIC. Coverage is worldwide for decoding, with the most complete data for US-market vehicles. Safety datasets such as recalls, owner complaints, and NCAP crash ratings are US-market only and are matched by make, model, and year—those datasets are not part of this endpoint, but you’ll often pair them downstream using the fields returned here.

Illustration: VIN Decoder API

Expect a consistent JSON envelope with top-level status and source keys, and a data object with the decoded fields. If NHTSA doesn’t publish a field for the vehicle, it will be returned as null rather than omitted. Electrification attributes (for example, electrification_level or battery_kwh) will be null for non-EVs and older vehicles where such fields are not relevant. This is not a vehicle history product: there is no title, accident, or ownership history in this endpoint.

If you plan to automate with AI agents or tools, there is also an MCP server at MCP that exposes the same decoding capability. You can learn more about the platform at vehicles-api.com.

Endpoint and authentication

Base URL: https://vehicles-api.com/api/v1

VIN decoding endpoint: POST /vin/decode

  • Authentication: pass your key via X-API-Key: YOUR_API_KEY (Authorization: Bearer YOUR_API_KEY also works).
  • Content-Type: application/json
  • Body: a JSON object with a single vin field containing a 17-character VIN.

Official request example you can paste into a terminal:

curl -X POST "https://vehicles-api.com/api/v1/vin/decode" -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" -d '{"vin":"1HGCM82633A004352"}'

That test VIN is a known control example to demonstrate field coverage and null handling. It is not your user’s car; always supply the user’s VIN at runtime from your own form, DMS, or ingestion pipeline.

Response envelope and field guide

Every response is wrapped in a predictable envelope with status and source. On success, data contains the decoded vehicle. On error, you’ll receive an envelope with status set to error, and error.code and error.message describing the issue. You should handle both cases uniformly in your client code.

Official JSON example (from the docs):

{
"status": "success",
"source": "nhtsa",
"data": {
"vin": "1HGCM82633A004352",
"valid": true,
"year": 2003,
"make": "HONDA",
"model": "Accord",
"trim": "EX-V6",
"body": "Coupe",
"doors": 2,
"vehicle_type": "PASSENGER CAR",
"engine": {
"cylinders": 6,
"displacement_l": 3,
"configuration": "V-Shaped",
"model": "J30A4",
"hp": 240,
"fuel": "Gasoline"
},
"fuel_type": "Gasoline",
"electrification_level": null,
"ev_drive_unit": null,
"battery_kwh": null,
"charger_level": null,
"charger_power_kw": null,
"drive": null,
"transmission": {
"style": "Automatic",
"speeds": 5
},
"plant": {
"country": "UNITED STATES (USA)",
"city": "MARYSVILLE",
"state": "OHIO"
},
"manufacturer": "AMERICAN HONDA MOTOR CO., INC."
}
}

Key fields you will typically use:

  • status: "success" or "error". Always check this before parsing data.
  • source: "nhtsa" in this endpoint. Useful for observability and attribution in logs.
  • data.vin: Echoes the submitted VIN so you can correlate requests and cache by VIN.
  • data.valid: Boolean checksum validation of the VIN format. If false, downstream lookups should be skipped.
  • data.year, data.make, data.model, data.trim: Core identity attributes used across dealer tooling, listings, underwriting, and quoting pipelines.
  • data.body, data.doors, data.vehicle_type: Helpful for UI labels and eligibility filters (e.g., coupe vs. sedan).
  • data.engine: Nested object with cylinders (count), displacement_l (liters), configuration (e.g., Inline, V-Shaped), model (engine code), hp (horsepower, integer), fuel (fuel type string).
  • data.fuel_type: Duplicate top-level indicator of fuel; normalize to your internal enums if needed.
  • data.electrification_level, data.ev_drive_unit, data.battery_kwh, data.charger_level, data.charger_power_kw: EV/hybrid-specific fields. Will be null where not applicable or not published.
  • data.drive: Drivetrain (e.g., FWD, RWD, AWD) when available. Can be null if not published for the VIN.
  • data.transmission: style (e.g., Automatic) and speeds (integer gear count).
  • data.plant: country, city, state for final assembly. Useful for internal compliance or analytics.
  • data.manufacturer: OEM or US distributor name as published.

Nulls indicate “not published” or “not applicable.” Do not assume missing features (e.g., AWD) solely from null; use your domain logic to decide when to prompt users or call a secondary source.

Python example: decode a VIN and normalize fields

The sample below posts a VIN, checks the envelope, handles nulls, and prints a normalized summary suitable for listings or underwriting. It uses the official endpoint, headers, and body structure shown above.

import json
import requests

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://vehicles-api.com/api/v1"
VIN = "1HGCM82633A004352" # control example from docs

def decode_vin(vin: str) -> dict:
url = f"{BASE_URL}/vin/decode"
headers = {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
}
payload = {"vin": vin}
resp = requests.post(url, headers=headers, data=json.dumps(payload), timeout=10)
resp.raise_for_status() # transport-level errors
body = resp.json()

# Envelope handling
if body.get("status") != "success":
err = body.get("error", {})
code = err.get("code", "unknown_error")
msg = err.get("message", "Request failed")
raise RuntimeError(f"VIN decode failed: {code} - {msg}")

data = body.get("data", {}) or {}

# Safety: ensure we have a valid VIN before trusting fields
if not data.get("valid", False):
raise ValueError(f"Invalid VIN checksum: {data.get('vin')}")

# Normalize a subset of commonly used fields
engine = data.get("engine") or {}
transmission = data.get("transmission") or {}
plant = data.get("plant") or {}

summary = {
"vin": data.get("vin"),
"year": data.get("year"),
"make": data.get("make"),
"model": data.get("model"),
"trim": data.get("trim"),
"body": data.get("body"),
"doors": data.get("doors"),
"vehicle_type": data.get("vehicle_type"),
"engine_cylinders": engine.get("cylinders"),
"engine_displacement_l": engine.get("displacement_l"),
"engine_configuration": engine.get("configuration"),
"engine_model": engine.get("model"),
"engine_hp": engine.get("hp"),
"fuel_type": data.get("fuel_type") or engine.get("fuel"),
"drive": data.get("drive"),
"transmission_style": transmission.get("style"),
"transmission_speeds": transmission.get("speeds"),
"plant_country": plant.get("country"),
"plant_state": plant.get("state"),
"plant_city": plant.get("city"),
"manufacturer": data.get("manufacturer"),
# EV-related fields can be None for non-EVs; propagate as-is
"electrification_level": data.get("electrification_level"),
"ev_drive_unit": data.get("ev_drive_unit"),
"battery_kwh": data.get("battery_kwh"),
"charger_level": data.get("charger_level"),
"charger_power_kw": data.get("charger_power_kw"),
}
return summary

if __name__ == "__main__":
decoded = decode_vin(VIN)
# Print a compact, stable string for caching/logging
print(json.dumps(decoded, ensure_ascii=False, separators=(",", ":")))

Where to go next: join this decoded payload with US-specific safety datasets in your system using the year, make, and model fields. If you work with Dutch registrations from RDW, note that Dutch plates do not include a VIN; handle those lookups with a separate plate flow and do not expect VIN to be present.

Coverage notes you should plan for

  • Worldwide decoding: The endpoint decodes any standard 17-character VIN globally. You will see the strongest attribute coverage for US-market vehicles.
  • US safety datasets: Recalls, complaints, and NCAP ratings apply to US-market vehicles and are matched by make, model, and year—not by VIN in this endpoint. This guide focuses on the VIN Decoder only.
  • Dutch plates: RDW open data does not include the VIN. If you serve the Dutch market, design your app so plate flows never assume a VIN is available.
  • Nulls are expected: Fields that NHTSA does not publish for a given VIN are returned as null (for example, drive on some records, or all EV-specific fields for internal combustion vehicles).

You can learn more about the platform and explore other capabilities at vehicles-api.com. For endpoint details, see the Documentation.

Error handling, rate limits, quotas, and caching

Response envelope and errors

All responses use a consistent envelope. On success, status is "success" and data contains the decoded structure. On failure, status is "error" and the response includes error.code and error.message. Typical causes include malformed JSON, a VIN that is not 17 characters, or quota/rate-limit violations. Your client should:

  • Check status before reading data.
  • Surface error.message to logs or support tooling.
  • Branch on error.code if you implement user-facing guidance (e.g., prompt for a 17-character VIN on validation errors).

Rate limits and monthly quota

Plans include rate limits and a monthly quota. The Starter plan begins at $19/month, and every plan includes a 7-day free trial. Exact rate limits and usage details are available in your dashboard and the Documentation. Build your client to back off or queue requests when you approach your plan’s limits.

Caching decoded VINs

  • VINs are immutable identifiers for a vehicle’s build attributes. Cache by VIN for long durations to minimize round trips and save your quota.
  • Include the envelope’s source ("nhtsa") and the normalized payload in your cache key schema if you aggregate multiple providers elsewhere.
  • If you store an internal Vehicle record, treat the VIN decoder as write-once for build attributes. Update only if NHTSA publishes a corrected record, which is uncommon.

Nulls and downstream logic

Null fields are common and intended. If your UI requires a value (for example, drivetrain), add a fallback path: prompt the user, or use a rules engine based on trim and market for a best-effort guess. Do not substitute default strings in your canonical store; keep nulls to preserve data lineage and correctness.

Request construction and transport details

  • HTTP method: POST
  • URL: https://vehicles-api.com/api/v1/vin/decode
  • Headers: X-API-Key: YOUR_API_KEY; Content-Type: application/json. Authorization: Bearer YOUR_API_KEY is also supported if that fits your client middleware.
  • Payload: {"vin": "YOUR_17_CHAR_VIN"} where the VIN must be 17 characters. Reject and correct early on the client to avoid unnecessary requests.
  • Transport errors: Handle non-2xx responses and network failures (timeouts, DNS) separately from application-level errors. In the Python sample above, resp.raise_for_status() addresses transport failures; then the code checks the JSON envelope for application errors.

Data modeling tips for dealer, marketplace, fleet, and insurance apps

  • Identity: Persist year, make, model, trim as your canonical identity tuple. Retain the raw case (e.g., HONDA) as well as a display-cased variant if your UI standards require it.
  • Engine and transmission: Store nested objects as separate structures to allow targeted updates. Units are liters for displacement_l and horsepower for hp; both are numeric.
  • Body and vehicle_type: Use these to drive listing facets and underwriting rules. For example, body=Coupé vs. Sedan changes eligibility in some programs.
  • Plant and manufacturer: Keep as-is for compliance, analytics, or regional labeling. They are strings and may include uppercase names as published by NHTSA.
  • EV fields: Treat EV attributes as optional. If you need charging-related logic, check electrification_level and battery_kwh explicitly and branch when null.
  • Downstream safety data: When you associate recalls, complaints, or NCAP ratings, match on year + make + model for US-market vehicles.

MCP server for AI agents

If you’re building an AI agent or tool that runs in an MCP-aware runtime, you can integrate the same decoder via the MCP server at MCP. This allows you to invoke decoding as a tool call without maintaining HTTP plumbing in the agent itself. The decoded fields and envelopes mirror the REST API so you can share the same post-processing logic across both clients.

Operational checks before production

  • Validation: Enforce 17-character VINs client-side. Consider a simple checksum validation to avoid unnecessary requests.
  • Observability: Log status, source, and error.code/error.message on failures. Include the VIN with a one-way hash if you need privacy in logs.
  • Resilience: Set sane timeouts and a small retry budget for transient network errors. Do not retry on validation or quota errors.
  • Security: Treat the API key as a secret. Prefer server-to-server calls from your backend when possible. If you must call from a client, use a thin proxy that injects the key.
  • Caching: Cache successes and also cache “invalid VIN” results briefly to prevent repeated misuse. Invalidate cache entries if your users correct a mistyped VIN.

Frequently asked questions

Does the VIN Decoder work outside the United States?
Yes. Decoding covers any standard 17-character VIN worldwide, with the most complete attribute coverage for US-market vehicles.

Will I get recalls, complaints, or crash ratings in this response?
No. This endpoint only decodes the VIN. Recalls, owner complaints, and NCAP ratings apply to US-market vehicles and are matched by make, model, and year using separate lookups.

Why are some fields null?
Fields that NHTSA does not publish for that VIN—or that do not apply to the vehicle—are returned as null. Keep nulls in your data store and handle them in your UI or rules engine as needed.

Can I send Dutch license plates instead of a VIN?
Not to this endpoint. Dutch plates are available through RDW open data but do not include a VIN. Model your RDW integration so it does not assume VIN availability from Dutch plates.

How is authentication handled?
Send X-API-Key: YOUR_API_KEY in the header. Authorization: Bearer YOUR_API_KEY is also supported if that fits your HTTP client defaults.

Ready to integrate? Start your 7-day free trial and get a live API key: Register. Explore endpoint details anytime in the Documentation and learn about agent tooling at MCP.

Keep reading