PlateToVIN API
A single, predictable REST API for US vehicle data. Turn a license plate into a VIN, read a plate straight from a photo, decode a VIN into a full specification, check open safety recalls, and serve catalog-grade vehicle images — all with plain HTTP and JSON. No SDK required.
Authentication
The JSON endpoints (License Plate to VIN and VIN Decoding) authenticate with an
API key sent in the Authorization
header. You can find your key on your
API Keys page after signing in.
Authorization: sk_live_your_api_key_here
Base URL
All JSON endpoints live under a single HTTPS base. The Vehicle Images API is served from a dedicated image host.
https://platetovin.com/api
https://vehicle-images.platetovin.com
Quickstart
Convert your first license plate to a VIN in one request. Replace the API key with your own, then switch languages on the right.
curl https://platetovin.com/api/convert \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"state": "CA", "plate": "7ABC123"}'
const res = await fetch('https://platetovin.com/api/convert', {
method: 'POST',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ state: 'CA', plate: '7ABC123' }),
});
const data = await res.json();
console.log(data.vin);
$res = Http::withHeaders(['Authorization' => 'YOUR_API_KEY'])
->acceptJson()
->post('https://platetovin.com/api/convert', [
'state' => 'CA',
'plate' => '7ABC123',
]);
$vin = $res->json('vin');
import requests
res = requests.post(
'https://platetovin.com/api/convert',
headers={'Authorization': 'YOUR_API_KEY', 'Accept': 'application/json'},
json={'state': 'CA', 'plate': '7ABC123'},
)
print(res.json()['vin'])
License Plate to VIN
/api/convert
Convert a single US license plate and state into a VIN, along with a quick decode of the core vehicle attributes (make, model, year, trim, engine and more).
Body parameters
plate
string
required
7ABC123.state
string
required
CA, TX, NY.Response fields
success
boolean
vin
object
vin
string
name
string
2011 Mercedes-Benz M-Class.year
string
make
string
Mercedes-Benz.model
string
M-Class.trim
string
ML 350.style
string
SUV, Sedan.engine
string
3.5L V6 DOHC 24V.fuel
string
Gasoline.driveType
string
AWD, FWD.transmission
string
Automatic.GVWR
integer|string
Class 3: 10,001 - 14,000 lb. null when unavailable.color
object
name
string
Silver.abbreviation
string
SIL.curl https://platetovin.com/api/convert \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"state": "CA", "plate": "7ABC123"}'
await fetch('https://platetovin.com/api/convert', {
method: 'POST',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ state: 'CA', plate: '7ABC123' }),
});
{
"success": true,
"vin": {
"vin": "4JGBB8GB9BA648907",
"name": "2011 Mercedes-Benz M-Class",
"year": "2011",
"make": "Mercedes-Benz",
"model": "M-Class",
"trim": "ML 350",
"style": "SUV",
"engine": "3.5L V6 DOHC 24V",
"fuel": "Gasoline",
"driveType": "AWD",
"transmission": "Automatic",
"GVWR": 6063,
"color": { "name": "Silver", "abbreviation": "SIL" }
}
}
License Plate OCR
/api/ocr
Read a US license plate from a photo. We detect the plate in the scene (full photos are fine — no cropping needed), read the characters, correct them against the issuing state's plate format, and return the serial with a ranked list of candidate states, per-character confidences and the bounding box — everything you need to build your own capture or review flow. Uploaded images are never stored.
OCR never runs a VIN lookup by itself. When you want the VIN, take plate
and state.code from a trusted read and call
License Plate to VIN — and if that misses, retry with the
next state_candidates entries, using each candidate's own plate
value.
trusted before looking up.
Blurry or uncertain reads come back as low_confidence with the best
guess marked trusted: false — treat those as suggestions to confirm,
not as input for a plate lookup.
Body parameters
image
string | file
required
data: URIs accepted) in a JSON body, or a multipart file upload in the image field.state_suggestions
string[]
["NJ", "NY"] for a camera in New Jersey near the NY border. Strongly improves accuracy. It's a prior, not a filter: strong visual evidence for another state still wins. A comma-separated string ("NJ,NY") also works; unknown codes are ignored and echoed back in ignored_suggestions.Statuses
ok
status
plate carries trusted: true and is safe to feed into License Plate to VIN.low_confidence
status
plate holds the best guess with trusted: false — treat it as a suggestion and re-shoot if you can.no_plate_found
status
non_us_plate
status
Response fields
success
boolean
status
string
request_id
string
image
object
width, height) — the coordinate space for bbox.plate
object
null when status is no_plate_found.plate
string
plate_display
string
ZZC-31V.confidence
float
char_confidences
float[]
plate.bbox
int[]
[x_min, y_min, x_max, y_max].detection_confidence
float
state
object
code
string
name
string
New Jersey.confidence
float
state_candidates
object[]
plate — the serial as corrected under that state's format, occasionally differing by a character. When a plate lookup misses under the top state, retry with these, highest confidence first.code · name · confidence
mixed
state.plate
string
plate_type
string
passenger, commercial, motorcycle, … or vanity_or_unrecognized.is_vanity_likely
boolean
true when the plate looks personalized. Vanity plates are real registrations and still resolve to VINs.warnings
string[]
low_text_confidence, low_state_confidence, possibly_non_us_plate: <region>. Empty for clean reads.trusted
boolean
additional_plates
object[]
plate.ignored_suggestions
string[]
state_suggestions that aren't valid jurisdiction codes.Billing
Plans include a monthly OCR call allowance (unlimited on enterprise). Once it's used up,
additional calls are billed per call from your account balance at your plan's overage
rate — or refused with a 403 on plans without overage. Every processed image
counts, whatever its status; rejected uploads and service errors don't. OCR itself never
triggers a plate lookup — resolving a VIN through
/api/convert is a separate call, billed as usual.
curl https://platetovin.com/api/ocr \
-H "Authorization: YOUR_API_KEY" \
-F "[email protected]" \
-F "state_suggestions=NJ,NY"
curl https://platetovin.com/api/ocr \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "'$(base64 -i driveway.jpg)'",
"state_suggestions": ["NJ", "NY"]
}'
const form = new FormData();
form.append('image', fileInput.files[0]);
form.append('state_suggestions', 'NJ,NY');
const res = await fetch('https://platetovin.com/api/ocr', {
method: 'POST',
headers: { 'Authorization': 'YOUR_API_KEY' },
body: form,
});
const data = await res.json();
if (data.status === 'ok') {
// Trusted read — resolve the VIN with /api/convert when you need it
const { plate, state } = data.plate;
console.log(plate, state.code);
}
{
"success": true,
"status": "ok",
"request_id": "9e177840deec",
"image": { "width": 3913, "height": 2678 },
"plate": {
"plate": "ZZC31V",
"plate_display": "ZZC-31V",
"confidence": 0.985,
"char_confidences": [0.999, 0.999, 0.996, 0.996, 0.999, 0.995],
"bbox": [2690, 1753, 3076, 2059],
"detection_confidence": 0.91,
"state": {
"code": "NJ",
"name": "New Jersey",
"confidence": 0.98
},
"state_candidates": [
{ "code": "NJ", "name": "New Jersey", "confidence": 0.98, "plate": "ZZC31V" },
{ "code": "NY", "name": "New York", "confidence": 0.01, "plate": "ZZC31V" }
],
"plate_type": "passenger",
"is_vanity_likely": false,
"warnings": [],
"trusted": true
}
}
{
"success": true,
"status": "low_confidence",
"request_id": "b41c09aa77f2",
"image": { "width": 1920, "height": 1080 },
"plate": {
"plate": "8XK4O2",
"plate_display": "8XK4O2",
"confidence": 0.41,
"char_confidences": [0.93, 0.88, 0.52, 0.61, 0.22, 0.35],
"bbox": [811, 540, 1004, 634],
"detection_confidence": 0.87,
"state": { "code": "CA", "name": "California", "confidence": 0.44 },
"state_candidates": [
{ "code": "CA", "name": "California", "confidence": 0.44, "plate": "8XK4O2" },
{ "code": "AZ", "name": "Arizona", "confidence": 0.21, "plate": "8XK402" }
],
"plate_type": "passenger",
"is_vanity_likely": false,
"warnings": ["low_text_confidence", "low_state_confidence"],
"trusted": false
}
}
VIN Decoding
/api/vin-lookup
Decode a VIN into a complete vehicle specification: engine & drivetrain, performance, economy, dimensions, warranty, colors and standard/optional features. Requires a plan that includes VIN decoding.
Body parameters
vin
string
required
WBAAV53431JR78115.Response object groups
data contains the full specification. Depending on
coverage for the vehicle, it may include the following nested objects — fields without
data for a given vehicle are null:
year · make · model · trim · body_type
string
trim_description · platform_code · domestic_import · manufacturer_country
string
330i Rwd 4dr Sedan (3.0L 6cyl 5M), E46, Imported, Germany.msrp · base_invoice
number
engine_drivetrain
object
cylinder_layout
string
I6, V8.engine_displacement
number
engine_type
string
gas, diesel, hybrid, electric.fuel_type
string
premium unleaded (required).valves
integer
valve_timing
string
Variable.cam_type
string
Double overhead cam (DOHC).drivetrain
string
rear wheel drive, all wheel drive.transmission
string
5-speed manual, Automatic.performance
object
horsepower
integer
horsepower_rpm
integer
torque
integer
torque_rpm
integer
zero_to_sixty_time
number
economy
object
fuel_tank_size
number
mpg_city
integer
mpg_highway
integer
mpg_combined
integer
range_city
integer
range_highway
integer
range_combined
integer
body
object
gross_weight
integer|string
Class 3: 10,001 - 14,000 lb.curb_weight
integer
length
integer
width
integer
height
integer
wheelbase
integer
ground_clearance
number
turning_circle
number
doors
integer
seats
integer
towing_capacity
integer
warranty
object
4 yr./ 50000 mi.basic
string
drivetrain
string
roadside
string
rust
string
colors
object
exterior
object
"R,G,B" value, e.g. "Jet Black": "0,0,0".interior
object
"R,G,B" value.features
object
suspension
string[]
front_seats
string[]
rear_seats
string[]
features
string[]
safety
string[]
curl https://platetovin.com/api/vin-lookup \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"vin": "WBAAV53431JR78115"}'
await fetch('https://platetovin.com/api/vin-lookup', {
method: 'POST',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ vin: 'WBAAV53431JR78115' }),
});
{
"success": true,
"data": {
"year": "2001",
"make": "BMW",
"model": "3 Series",
"trim": "330i - Sedan",
"trim_description": "330i Rwd 4dr Sedan (3.0L 6cyl 5M)",
"body_type": "Sedan",
"msrp": 33990,
"base_invoice": 30750,
"domestic_import": "Imported",
"manufacturer_country": "Germany",
"platform_code": "E46",
"engine_drivetrain": {
"cylinder_layout": "I6",
"engine_displacement": 3,
"engine_type": "gas",
"fuel_type": "premium unleaded (required)",
"valves": 24,
"valve_timing": "Variable",
"cam_type": "Double overhead cam (DOHC)",
"drivetrain": "rear wheel drive",
"transmission": "5-speed manual"
},
"performance": {
"horsepower": 225,
"horsepower_rpm": 5900,
"torque": 214,
"torque_rpm": 3500,
"zero_to_sixty_time": 4.76
},
"economy": {
"fuel_tank_size": 16.6,
"mpg_city": 18,
"mpg_highway": 27,
"mpg_combined": 22,
"range_city": 298,
"range_highway": 448,
"range_combined": 366
},
"body": {
"gross_weight": 3931,
"curb_weight": 3318,
"length": 176,
"width": 68,
"height": 55,
"wheelbase": 107,
"ground_clearance": null,
"turning_circle": 34,
"doors": null,
"seats": 5,
"towing_capacity": 1000
},
"warranty": {
"basic": "4 yr./ 50000 mi.",
"drivetrain": "4 yr./ 50000 mi.",
"roadside": null,
"rust": null
},
"colors": {
"exterior": {
"Steel Gray Metallic": "60,60,65",
"Orient Blue Metallic": "30,94,130",
"Jet Black": "0,0,0",
"Alpine White": "255,255,255",
"Bright Red": "159,14,5",
"Titanium Silver Metallic": "171,172,175"
},
"interior": {
"Sand": "191,147,98",
"Gray": "134,143,142",
"Black": "0,0,0"
}
},
"features": {
"suspension": ["Four-wheel independent suspension"],
"front_seats": ["Bucket front seats", "Vinyl"],
"rear_seats": ["Split-folding rear seatback"],
"features": [
"1 one-touch power windows",
"Remote keyless power door locks",
"Heated mirrors",
"Power mirrors"
],
"safety": [
"Child seat anchors",
"Daytime running lights",
"Dual front side-mounted airbags",
"Engine immobilizer",
"Stability control",
"Traction control",
"4-wheel ABS"
]
}
}
}
Safety Recalls
/api/recalls
Look up open NHTSA safety recall campaigns for a vehicle. Send a VIN and we decode it in-house, then return every recall campaign filed for that year, make and model — newest first. You can also query by year, make and model directly.
Recall lookups are free per call on plans that include them — they never consume credit and have no monthly cap.
Body parameters
vin
string
required
1FTFW1ED0MFA63198. Not required when make, model and year are supplied instead.make
string
Ford. Required together with model and year when vin is omitted.model
string
F-150. Required together with make and year when vin is omitted.year
integer
2021. Required together with make and model when vin is omitted.Response fields
success
boolean
data
object
vin
string
null when querying by make/model/year.year · make · model
string
count
integer
0 with an empty recalls array means no recalls on file.recalls
object[]
campaign_number
string
22V412000.report_date
string
YYYY-MM-DD.component
string
SEAT BELTS.summary
string
consequence
string
remedy
string
notes
string
manufacturer
string
Ford Motor Company.park_it
boolean
true when NHTSA advises the vehicle should not be driven.park_outside
boolean
true when NHTSA advises parking outdoors due to fire risk.over_the_air_update
boolean
true when the remedy is delivered as an over-the-air update.curl https://platetovin.com/api/recalls \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"vin": "1FTFW1ED0MFA63198"}'
await fetch('https://platetovin.com/api/recalls', {
method: 'POST',
headers: {
'Authorization': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ vin: '1FTFW1ED0MFA63198' }),
});
{
"success": true,
"data": {
"vin": "1FTFW1ED0MFA63198",
"year": 2021,
"make": "Ford",
"model": "F-150",
"count": 2,
"recalls": [
{
"campaign_number": "22V412000",
"report_date": "2022-06-08",
"component": "SEAT BELTS",
"summary": "Ford Motor Company (Ford) is recalling certain 2021 F-150 vehicles. The front seat belt buckle assembly may be improperly secured.",
"consequence": "A seat belt that does not properly restrain the occupant increases the risk of injury in a crash.",
"remedy": "Dealers will inspect and replace the seat belt buckle assembly as necessary, free of charge.",
"notes": "Owners may also contact the NHTSA Vehicle Safety Hotline at 1-888-327-4236.",
"manufacturer": "Ford Motor Company",
"park_it": false,
"park_outside": false,
"over_the_air_update": false
},
{
"campaign_number": "21V576000",
"report_date": "2021-07-28",
"component": "ELECTRICAL SYSTEM",
"summary": "...",
"consequence": "...",
"remedy": "...",
"notes": "...",
"manufacturer": "Ford Motor Company",
"park_it": false,
"park_outside": false,
"over_the_air_update": true
}
]
}
}
Vehicle Images
vehicle-images.platetovin.com/image
Serve catalog-grade vehicle images straight from an <img> tag. Every
image is a transparent PNG rendered at a fixed, consistent framing so vehicles line up
perfectly across a page. Five angles, six colors, three sizes — all from one HTTP GET.
Referer of the whitelisted domains
on your account. Add your domains in your dashboard, then hotlink the URLs directly.
tier=free to any request for the public watermarked preview — front angle,
low-resolution, no referer or key required — or browse and download previews from the
image library. A vehicle
without a generated preview returns 404.
Query parameters
search
string
required
BMW 3 Series. Matching resolves make, model and generation server-side.year
integer
required
2022. Years newer than our latest imagery fall back to the closest available model year, so current-generation vehicles always render.angle
string
front, front_angle, side, rear, rear_angle. Defaults to front_angle.color
string
base, silver, black, white, gray, red. Defaults to base.size
string
large, medium, small. Defaults to large.tier
string
free for the public, no-auth watermarked preview — front angle only, low-resolution, with a forced background. All other parameters are ignored; a vehicle without a generated preview returns 404. Omit for licensed full-resolution images.Want the full walkthrough with a live playground? See the Vehicle Images page →
<img
src="https://vehicle-images.platetovin.com/image?search=BMW+3+Series&year=2022&angle=front_angle&color=red"
alt="2022 BMW 3 Series"
/>
Errors
The JSON API returns a consistent shape on failure — success: false
with a human-readable message — alongside a standard HTTP status code.
| Status | Meaning |
|---|---|
200 |
Success. Note: validation failures may also return 200 with success: false — always check the success flag. |
402 |
Payment required — your account balance is too low to run the lookup. Top up or enable auto-deposit. |
403 |
Forbidden — no billing plan assigned, the account is suspended, your plan doesn't include this endpoint (e.g. VIN decoding), or a monthly limit was reached on a plan without overage. |
404 |
No result found for the supplied plate or VIN. |
413 |
The uploaded image exceeds the 15 MB limit (OCR). Downscale or re-encode it. |
422 |
The upload couldn't be decoded as an image (OCR). |
503 |
The service behind this endpoint is temporarily unavailable — safe to retry shortly. |
{
"success": false,
"message": "Not enough credit"
}
Billing & limits
API access requires an active billing plan. License plate lookups are billed per request and cached for seven days — repeated lookups of the same plate within that window are free. VIN decoding and vehicle images are included on the plans that offer them, up to any monthly ceiling on your tier. Safety recall lookups are free per call on plans that include them — no credit charge and no monthly cap.
License plate OCR comes with a monthly call allowance per plan (unlimited on enterprise). Past the allowance, calls are billed individually from your balance at your plan's overage rate; plans without an overage rate pause OCR until the allowance resets. OCR never runs a plate lookup itself — resolving a read to a VIN is a separate /api/convert call, billed as usual.
Pick a plan or create an account to get your API key.