API documentation
Authenticate with your API key, POST a VIN, and receive structured vehicle data as JSON. Structural fields are computed deterministically from the VIN standard; attribute fields come from data providers and are null when unknown — never guessed.
Authentication
Create a key in your dashboard and send it as a bearer token (or the x-api-key header). Keep it secret — treat it like a password.
Authorization: Bearer vin_live_...
Decode a VIN
POST /api/v1/decodeBody: { "vin": "<17 chars>" }
curl https://www.vinapi.app/api/v1/decode \
-H "Authorization: Bearer vin_live_..." \
-H "Content-Type: application/json" \
-d '{"vin":"1HGCM82633A004352"}'const res = await fetch("https://www.vinapi.app/api/v1/decode", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ vin: "1HGCM82633A004352" }),
});
const { data } = await res.json();
console.log(data.manufacturer, data.vehicle.modelYear, data.vehicle.bodyClass);import os, requests
res = requests.post(
"https://www.vinapi.app/api/v1/decode",
headers={"Authorization": f"Bearer {os.environ['VIN_API_KEY']}"},
json={"vin": "1HGCM82633A004352"},
)
print(res.json()["data"])Response
{
"data": {
"vin": "1HGCM82633A004352",
"squishVin": "1HGCM8263",
"valid": true,
"standard": "iso-3779",
"checkDigit": { "expected": "3", "actual": "3", "valid": true, "applicable": true },
"wmi": "1HG",
"manufacturer": "Honda",
"region": "North America",
"country": "United States",
"vehicleClass": null,
"vehicle": {
"make": "HONDA",
"model": "Accord",
"modelYear": 2003,
"yearAmbiguous": false,
"yearCandidates": [],
"series": null,
"trim": "EX-V6",
"bodyClass": "Coupe",
"vehicleType": "PASSENGER CAR",
"doors": 2
},
"engine": { "cylinders": 6, "displacementL": 2.998832712, "fuelType": "Gasoline", "powerKW": null },
"plant": "A",
"serialNumber": "004352",
"errors": [],
"sources": { "structural": "internal", "attributes": "vpic" }
}
}| Field | Meaning |
|---|---|
| squishVin | Positions 1-8 + 10 — the vehicle pattern used for caching. Contains no serial number. |
| valid | Structural validity (17 chars, allowed charset). NOT gated on the check digit. |
| standard | iso-3779 (modern 17-char), pre-1981, or non-standard. |
| checkDigit | Computed vs. actual (position 9). valid:false is a data-quality signal — many valid EU/Asian VINs fail it, so it is never used to reject a VIN. |
| vehicle / engine | Attribute fields from a data provider. Any unknown value is null — never guessed. |
| sources.attributes | Where attributes came from: cache, vpic, commercial, or null when unavailable (structural still returns). |
Batch decode
POST /api/v1/decode/batchUp to 50 VINs per call. Metered per VIN. A malformed VIN doesn't fail the batch — it returns with valid:false and its errors.
curl https://www.vinapi.app/api/v1/decode/batch \
-H "Authorization: Bearer vin_live_..." \
-H "Content-Type: application/json" \
-d '{"vins":["1HGCM82633A004352","WVWZZZ1JZ3W386752"]}'
# → { "data": [ {…}, {…} ], "count": 2 }Rate limits & quota
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Per-minute and monthly limits depend on your plan; the monthly quota resets on the 1st (UTC). Only successful (2xx) decodes count against your quota.
Errors
Errors return { "error": { "code", "message" } }.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_body / invalid_vin / invalid_batch | Body isn't JSON, the VIN isn't 17 valid chars, or the batch is empty / over 50. |
| 401 | missing_api_key / invalid_api_key | No key provided, or the key is wrong/revoked. |
| 402 | subscription_inactive | The account's subscription isn't active. |
| 429 | rate_limited / quota_exceeded | Per-minute limit or monthly quota reached (see Retry-After). |
| 5xx | decode_failed | Temporary decode error — retry with backoff. |