Vehicles.dev API · v1

Vehicle data API reference

11 synchronous data endpoints plus an asynchronous vehicle-history workflow over one authenticated surface: VIN identity, factory specifications, recalls, market value, depreciation, ownership costs, dealer listings, photos, and account-owned history-report results. Every response is JSON and every error is a problem document.

Base URL
https://api.vehicles.dev
Auth
Authorization: Bearer <key>
Errors
application/problem+json
Price catalog
2026-08-18

Overview

The Vehicles.dev API is a paid, server-to-server HTTP API. Every machine operation lives under /v1/vehicles/ on https://api.vehicles.dev and returnsapplication/json. The 11 data lookups use GET; vehicle history reports start with an idempotent POST and continue through account-owned list, status, and result resources. There is exactly one API version and no version header — the path prefix is the contract.

It is deliberately not browser-callable. No CORS headers are served, cookie authentication is rejected, and a request carrying an Origin header is refused outright. Call it from your backend and keep your key there.

What the data actually is

The synchronous data endpoints are built on a continuously crawled US vehicle dataset, joined against public federal sources where those are authoritative. The separate vehicle-history workflow preserves the canonical JSON returned by its report provider and keeps that result scoped to the ordering account.

  • 1,721,693normalized dealer listings across 1,719,030 distinct vehicles
  • 4,686,766price and mileage observations, appended on every crawl pass, powering valuation and marketplace analysis
  • 3.7%median absolute percentage error of the gradient-boosted asking-price model on holdout data
  • NHTSA + EPAvPIC for factory specifications and VIN fallback decoding, the recalls API for safety campaigns, and fueleconomy.gov for ownership costs

Provenance is explicit rather than implied. Every successful body carries source, VIN-keyed lookups add origin ("store" when we served it from our own dataset, "vpic" when we decoded it live), and valuation responses echo the exact feature vector the model scored.

What it is not

Market value is an asking price derived from live dealer listings, not a realized transaction price. Depreciation curves are per make and model, not per trim or VIN. Recalls are matched at the year/make/model level, so a campaign listing does not by itself prove a specific VIN is affected, and no public source tells us whether this car was repaired. Specifications are VIN-pattern decodes, not per-car build records, so they carry no factory option codes. Photo galleries hold what the source published and gave us — compare galleryCount against photoCount before assuming a gallery is complete. Each endpoint below states its own coverage limits — read them before you build on the numbers.

Quickstart

From zero to a real, billable response in about a minute. The calls below are copy and paste ready.

  1. Sign up

    Create an account. Every new workspace lands on the free Starter plan with 1,000 included calls per month across all eleven synchronous vehicle data endpoints, plus a one-time $10 credit balance for usage after that shared allowance.

    Create a free accountOpen the dashboard

  2. Mint an API key

    Open the dashboard, find the API keys panel, name a key and select "Create API key". The secret is displayed exactly once and is never recoverable — copy it immediately. Keys are scoped to Vehicles.dev; a key minted for another product is rejected with 401 invalid_credential.

  3. Log in

    Install the CLI and log in once. vehicles login saves your key to ~/.vehicles/credentials.json — the MCP server reads the same file, so a single login covers both. Prefer raw HTTP or a server SDK? Export VEHICLES_API_KEY instead; the Bearer examples throughout these docs use it.

    shell
    npm install -g vehicles-dev-cli
    vehicles login
  4. Make your first call

    Decode a VIN. This is a real, billable call against production data — it draws from your Starter plan's included calls first. (Raw HTTP: GET /v1/vehicles/vin/{vin} with an Authorization: Bearer header, shown under every endpoint below.)

    Request
    vehicles decode 5TDDZRBHXNS221317
    200 · application/json
    {
      "origin": "store",
      "source": "carscrape",
      "vehicle": {
        "year": 2022,
        "make": "Toyota",
        "model": "Highlander",
        "trim": "Limited",
        "body_style": "SUV",
        "drivetrain": "AWD",
        "fuel": "Gasoline",
        "transmission": "Automatic",
        "cylinders": 6,
        "doors": 4
      },
      "vin": "5TDDZRBHXNS221317"
    }
  5. Read the response

    Read the response. "origin": "store" means the VIN matched a row in our scraped store and was served without a live external round trip; it does not guarantee that every field was enriched by vPIC. "vpic" means we decoded it live against NHTSA. Null fields are omitted rather than emitted as null, so treat every key inside vehicle as optional. The response also carried an x-request-id header — keep it if you need to open a support ticket about this call.

What it costVIN Decode is one of the eleven synchronous vehicle data endpoints covered by the Starter plan’s 1,000 included monthly calls, so that first request drew from your entitlement rather than your credit balance. Had it failed, it would have cost nothing at all — billing only settles on 2xx.

Official SDKs

Use the official, typed server-side clients when you do not want to manage bearer headers, query serialization, problem documents, timeouts, or history-report polling yourself. Both SDKs cover all 11 synchronous endpoints and the complete durable history-report workflow.

The first releases are installed from immutable GitHub v0.1.1 tags. npm and PyPI publication are not enabled yet, so use the exact commands below rather than an unversioned registry package.

Server-side onlyKeep VEHICLES_API_KEY in a server-side secret store. Never expose it in browser JavaScript, a mobile application, a public bundle, or a NEXT_PUBLIC_* variable. The API rejects browser Origin andCookie headers by design.
TypeScriptvehicles-dev/typescript-sdkv0.1.1

TypeScript SDK

Node.js 22+ (ESM). A dependency-free ESM client with typed methods for every synchronous lookup and the durable history-report workflow. It uses Node's built-in fetch and defaults to a 30-second request timeout.

Install

shell
pnpm add "github:vehicles-dev/typescript-sdk#v0.1.1"

Decode a VIN

quickstart.mts
import { Vehicles } from "@vehicles-dev/sdk";

const vehicles = new Vehicles({
  apiKey: process.env.VEHICLES_API_KEY!
});

const decoded = await vehicles.decodeVin("1HGCM82633A004352");
console.log({
  year: decoded.vehicle["year"],
  make: decoded.vehicle["make"],
  model: decoded.vehicle["model"],
  trim: decoded.vehicle["trim"]
});

The example prints only an allowlisted identity summary. Do not log API keys, VINs, raw responses, report identifiers, complete history reports, or full error objects.

View on GitHubv0.1.1 release

Pythonvehicles-dev/python-sdkv0.1.1

Python SDK

Python 3.11+. Typed synchronous and asynchronous clients powered by httpx. Use Vehicles in regular applications or AsyncVehicles with an async context manager; both expose the same API surface.

Install

shell
pip install "vehicles-dev @ git+https://github.com/vehicles-dev/python-sdk.git@v0.1.1"

Decode a VIN

quickstart.py
import os

from vehicles_dev import Vehicles

with Vehicles(os.environ["VEHICLES_API_KEY"]) as vehicles:
    decoded = vehicles.decode_vin("1HGCM82633A004352")
    vehicle = decoded["vehicle"]
    print({field: vehicle.get(field) for field in ("year", "make", "model", "trim")})

The example prints only an allowlisted identity summary. Do not log API keys, VINs, raw responses, report identifiers, complete history reports, or full error objects.

View on GitHubv0.1.1 release

Runnable examplesClone the Vehicles.dev examples repository for VIN decode, listing search, and explicitly guarded, billable history-report flows in both languages.

Authentication

Every /v1 request must carry a product API key as an HTTP bearer credential. There is no other scheme: no query-string keys, no basic auth, no cookies.

Required header
Authorization: Bearer $VEHICLES_API_KEY

The scheme token is case-sensitive Bearer followed by exactly one space. bearer, a double space, or any other scheme is rejected as 401 invalid_credential.

Keys

  • Mint and revoke keys in the dashboard. The plaintext secret is returned exactly once at creation and is never recoverable — we store only a hash and the last four characters.
  • Vehicles keys are prefixed vdev_. Keys are product-scoped: presenting a key minted for another product returns 401 invalid_credential.
  • Rotation is create-then-revoke. Create the replacement key, deploy it, then revoke the old one — revocation takes effect immediately.
  • Rate limits and credit balances are enforced per account, not per key, so splitting traffic across several keys does not raise your throughput ceiling.

An unauthenticated request

Calling any endpoint without a key returns a problem document. Note the x-request-id header, which is present on every response the API sends, successful or not.

401 · application/problem+json
HTTP/2 401
content-type: application/problem+json; charset=utf-8
x-request-id: 1f9fcd64-c8b4-4d8f-98b1-3b7ed12cae38

{
  "code": "authentication_required",
  "detail": "Bearer authentication is required.",
  "request_id": "1f9fcd64-c8b4-4d8f-98b1-3b7ed12cae38",
  "retryable": false,
  "status": 401,
  "title": "Unauthorized",
  "type": "https://api.data-platform.dev/problems/authentication-required",
  "instance": "/v1/vehicles/listings"
}
OrderingAuthentication runs before schema validation. A request that is both unauthenticated and malformed returns 401, never 400 — fix the credential first, then the parameters.

Errors

Every failure is an RFC 9457 problem document served as application/problem+json. The envelope is closed — no fields beyond the ones below ever appear — but key order is not stable, so parse the JSON rather than pattern-matching the string.

Problem document fields
FieldTypeDescription
codestringStable, machine-readable error slug in snake_case. Branch on this, never on the prose in detail.
detailstringHuman-readable explanation of this specific occurrence.
instancestring (optional)The request path, including the query string, that produced the error.
invalid_paramsarray (optional)Present only on 400 request_validation_failed. Each entry is { name, pointer, reason }: the failing schema keyword, a JSON pointer into the request, and the validator message.
request_idstringServer-generated UUID, identical to the x-request-id response header. Quote it in support requests.
retryablebooleanWhether repeating the identical request can plausibly succeed. Every 503 and the 429 are true; other 4xx are false.
statusintegerMirrors the HTTP status code.
titlestringShort status-level summary, for example "Unauthorized".
typestring (URI)Opaque problem-type URI. Use it as an identifier; it is not a documentation link. It is normally the code with underscores replaced by hyphens, but branch on code rather than on the URI: request_validation_failed is emitted with type .../problems/request-validation, without the -failed suffix.

Shared error codes

These can be returned by any endpoint. Branch on code, which is stable, rather than on detail, which is prose.

Errors common to every endpoint
StatuscodeRetryableWhen it happens
400request_validation_failedNoA path, query or body value failed its schema. Unknown query parameters are rejected rather than ignored. The problem document carries an invalid_params array naming each failure.
401authentication_requiredNoNo Authorization header was sent.
401invalid_credentialNoThe bearer value is malformed, or the key is unknown, revoked, or minted for a different product.
401cookie_credentials_rejectedNoThe request carried a Cookie header. Cookie authentication is never accepted on /v1 — send only the Authorization header.
401invalid_originNoThe request reached /v1 without resolving to a product.
402insufficient_creditsNoIncluded calls are exhausted and the credit balance cannot cover this request. Add credits or upgrade the plan. Metered data fees are prepaid from credits on every plan — a paid plan buys the rate limit, the lower unit prices, and endpoint access, never an overage bill.
403plan_upgrade_requiredNoThe requested operation is not sold on the current plan. Upgrade before retrying.
403subscription_inactiveNoThe account's subscription is not active or trialing.
404route_not_foundNoUnknown path — or a request whose Host header did not resolve to a product, or that carried an Origin / Access-Control-Request-Method header. The API is not browser-callable and serves no CORS headers.
429rate_limit_exceededYesThe plan's requests-per-second limit was exceeded. A retry-after header is returned and the billing reservation is released, so the rejected call is free.
500internal_errorYesAn unmapped server failure. Retry with backoff and keep the request id.
503authentication_unavailableYesThe API-key verifier is unreachable. Retry with backoff.

Validation failures

A 400 request_validation_failed adds an invalid_params array pointing at each offending value. Unknown query parameters are an error, not a silently ignored extra — every query schema is closed.

400 · application/problem+json
{
  "code": "request_validation_failed",
  "detail": "The request did not match the operation contract.",
  "invalid_params": [
    { "name": "minLength", "pointer": "/state", "reason": "must NOT have fewer than 2 characters" }
  ],
  "request_id": "d890d9c9-e6f9-411b-9e47-e0d947868003",
  "retryable": false,
  "status": 400,
  "title": "Bad Request",
  "type": "https://api.data-platform.dev/problems/request-validation",
  "instance": "/v1/vehicles/market-value?make=Toyota&model=Camry&year=2021&state=T"
}

Retrying

Use the retryable boolean rather than the status code. Every 503 and the 429 set it to true; the 429 additionally carries a retry-after header in whole seconds. Client errors other than 429 are false — retrying them unchanged will fail identically. Retry with exponential backoff and jitter, and note that none of these endpoints mutate anything, so a retried GET is always safe.

Getting support on a failure

Every response carries an x-request-id header, and on failures the same UUID appears as request_id in the body. It is generated server-side — sending your own x-request-id has no effect and it will not be echoed. Log it. Because Authorization and Cookie headers are redacted from our logs, the request id is the only way we can locate your exact call.

Pricing & limits

Pricing is per successful call, metered at the endpoint level. There are no bundles and no minimums beyond the plan fee.

Only 2xx is billedEvery endpoint is successOnly. A credit reservation is placed before the handler runs and settled only when the response is 2xx; any 4xx or 5xx — including rate-limit rejections and upstream outages — releases the reservation. A failed call costs nothing.

Plans

Vehicles.dev plans
PlanPlatform feeIncluded callsRate limit
Starter$0 / month1,000 / month5 req / second
Pro$299 / monthNone — every call metered10 req / second
Scale$599 / monthNone — every call metered50 req / second

“Included calls” means calls with no per-call charge. Only Starter has any: 1,000 per UTC month, shared by all eleven synchronous vehicle data endpoints listed below. Pro and Scale include none. Those plans have no monthly call ceiling, but that is a volume allowance, not free usage — every successful call is metered at its per-endpoint rate on top of the monthly platform fee, and the fee buys throughput, endpoint access and support rather than calls.

Rate limiting is a token bucket keyed to your account, refilling at the plan’s requests-per-second rate. Exceeding it returns 429 rate_limit_exceeded with a retry-after header, and the reservation is released so the rejected request is free. There are no x-ratelimit-* headers.

Per-endpoint prices

Prices are per billing unit. The second figure in each cell is the equivalent cost per 1,000 billing units. A dash means the endpoint is not sold on that plan and returns403 plan_upgrade_required.

Vehicles.dev per-endpoint pricing by plan
EndpointBilling unitStarterProScale
VIN DecodeIncluded calls applysuccessful lookup$0.004$4.00 / 1k$0.0025$2.50 / 1k$0.0015$1.50 / 1k
Vehicle SpecificationsIncluded calls applysuccessful lookup$0.0015$1.50 / 1k$0.0015$1.50 / 1k$0.001$1.00 / 1k
Recalls & SafetyIncluded calls applysuccessful lookup$0.01$10.00 / 1k$0.01$10.00 / 1k$0.007$7.00 / 1k
Market ValueIncluded calls applymodel estimate$0.015$15.00 / 1k$0.015$15.00 / 1k$0.01$10.00 / 1k
DepreciationIncluded calls applymodel forecast$0.015$15.00 / 1k$0.015$15.00 / 1k$0.01$10.00 / 1k
Ownership CostsIncluded calls applymodel estimate$0.045$45.00 / 1k$0.045$45.00 / 1k$0.035$35.00 / 1k
Total Cost of OwnershipIncluded calls applymodel estimate$0.06$60.00 / 1k$0.045$45.00 / 1k$0.035$35.00 / 1k
Loan & PaymentsIncluded calls applycalculation$0.001$1.00 / 1k$0.0007$0.70 / 1k$0.0005$0.50 / 1k
Purchase Tax & FeesIncluded calls applycalculation$0.001$1.00 / 1k$0.0007$0.70 / 1k$0.0005$0.50 / 1k
Search Vehicle ListingsIncluded calls applysuccessful API call$0.002$2.00 / 1k$0.0015$1.50 / 1k$0.001$1.00 / 1k
Vehicle PhotosIncluded calls applysuccessful lookup$0.001$1.00 / 1k$0.0009$0.90 / 1k$0.0007$0.70 / 1k
Vehicle History Reportreport / 1k$1.99$1,990.00 / 1k$0.99$990.00 / 1k

Included calls, credits, and running out

  • The Starter plan’s 1,000 included monthly calls are shared by all eleven synchronous vehicle data endpoints above: VIN Decode, Search Vehicle Listings, Vehicle Photos, Specifications, Recalls & Safety, Market Value, Depreciation, Ownership Costs, Total Cost of Ownership, Loan & Payments, and Purchase Tax & Fees. The entitlement resets on the UTC calendar month.
  • Every new account also receives a one-time $10 credit balance. It never resets, and after the shared allowance is used it funds successful calls at the published Starter rate. Plan-gated operations remain unavailable regardless of credit balance.
  • When entitlement and credits together cannot cover a call, the request is refused with 402 insufficient_credits. That applies on every plan, not just Starter: metered data fees are always prepaid from credits, and a paid plan buys the rate limit, the lower unit prices and endpoint access rather than an overage bill.
  • There is no machine-readable balance today. Your entitlement, credit balance and usage are visible only in the dashboard: the control-plane endpoints behind it require a dashboard session token, and a vdev_ API key presented there is rejected with 401 invalid_credential. Metered responses carry no balance or usage headers either — the only response headers this API sets beyond the standard ones are x-request-id, retry-after and cache-control. So a server-to-server integration cannot pre-flight or monitor burn-down; 402 insufficient_credits is the only programmatic signal, and you should handle it as an expected outcome rather than trying to predict it.
  • Concurrency is safe. Reservations hold credit while a request is in flight, so parallel callers cannot oversubscribe a balance.

Vehicle history reports

Vehicle history is an asynchronous, account-owned workflow because provider coverage and preparation time vary by VIN. Creating the report requires a machine key with reports:order, as does explicitly retrying a durable local submitting job. Polling and reading the result require reports:read. A report ordered by one account is not visible to another.

Pricing

Pro: $1.99 per completed report. Scale: $0.99 per completed report. Starter accounts cannot order reports and receive 403 plan_upgrade_required. The charge settles only after canonical report JSON is stored; invalid or unsupported VINs, failed generations, and action_required outcomes cost $0.

List

Recover recent account-owned report IDs, VINs, statuses, and polling cadence. This read never submits or retries provider work.

GET/v1/vehicles/history-reports

Create

Send a canonical 17-character VIN and UUID Idempotency-Key. The response is 202 with a stable ID, Location, and Retry-After.

POST/v1/vehicles/history-reports

Retry submission

Use only when a durable report remains submitting after an uncertain create response. The server preserves the stored provider idempotency key; the machine route requires reports:order and the dashboard route requires billing:write.

POST/v1/vehicles/history-reports/{id}/retry

Poll

Read status at the server-provided cadence. Valid states are submitting, queued, processing, action_required, and completed.

GET/v1/vehicles/history-reports/{id}

Read or print

Fetch canonical JSON after completed and hasResult=true. The dashboard renders the same account-owned result as a branded view that can be viewed or printed; there is no separate file-artifact endpoint.

GET/v1/vehicles/history-reports/{id}/result

Create a report
curl -i https://api.vehicles.dev/v1/vehicles/history-reports \
  -X POST \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"vin":"1FTFW3L57TKD09376"}'
202 · accepted
HTTP/2 202
location: /v1/vehicles/history-reports/7ae77ee8-94af-4c25-85f1-45a2ab1c6475
retry-after: 3

{
  "id": "7ae77ee8-94af-4c25-85f1-45a2ab1c6475",
  "vin": "1FTFW3L57TKD09376",
  "status": "queued",
  "hasResult": false,
  "retryAfterSeconds": 3,
  "replayed": false,
  "createdAt": "2026-08-16T12:00:00.000Z",
  "updatedAt": "2026-08-16T12:00:00.000Z"
}
Retry if submitting, then poll and fetch
# Poll status at the Retry-After cadence
curl -i https://api.vehicles.dev/v1/vehicles/history-reports/{id} \
  -H "Authorization: Bearer $VEHICLES_API_KEY"

# Only for a durable local status=submitting after an uncertain create response
curl -i -X POST https://api.vehicles.dev/v1/vehicles/history-reports/{id}/retry \
  -H "Authorization: Bearer $VEHICLES_API_KEY"

# Fetch canonical JSON only after status=completed and hasResult=true
curl -i https://api.vehicles.dev/v1/vehicles/history-reports/{id}/result \
  -H "Authorization: Bearer $VEHICLES_API_KEY"

Result data and coverage

The fixed result envelope is { "metadata": ReportMetadata, "report": Record<string, unknown> }. Metadata binds the stored result to its canonical VIN, provider, retrieval timestamp, schema version, and available coverage sections. The report value is the canonical provider object; Vehicles.dev does not rename its fields or discard provider-specific keys. Process report keys defensively and use metadata coverage rather than treating an absent section as a clean finding.

200 - illustrative completed result
{
  "metadata": {
    "schemaVersion": "vehicle-history-report/v1",
    "provider": "vinexposed",
    "sources": ["vinexposed"],
    "vin": "1FTFW3L57TKD09376",
    "retrievedAt": "2026-08-16T12:03:00.000Z",
    "dataAsOf": null,
    "coverage": {
      "status": "partial",
      "availableSections": ["accidents", "equipment", "ownership"]
    }
  },
  "report": {
    "vehicle": { "year": 2012, "make": "Bentley", "model": "Continental GT" },
    "equipment": ["Navigation", "Heated seats"],
    "ownership": [{ "ownerNumber": 1, "country": "US" }],
    "titleBrands": ["rebuilt"],
    "odometerHistory": [{ "date": "2025-03-01", "mileage": 48110 }],
    "accidents": [{ "date": "2012-05-19", "severity": "minor" }],
    "theft": { "status": "clear" },
    "junkSalvageInsurance": { "records": [] },
    "recalls": [],
    "salesHistory": [{ "date": "2021-01-12", "amount": 52000 }],
    "marketAnalysis": { "averagePrice": 41200, "confidence": "high" }
  }
}

The example is representative, not exhaustive. Depending on provider coverage, a report may include these categories:

Possible report categories
CategoryWhat may be returned
Vehicle & equipmentDecoded identity, specifications, installed equipment, options, and feature details.
OwnershipAvailable owner sequence, registration, location, and use records.
Title & brandsTitle events and provider-reported brands such as rebuilt or flood.
Odometer & mileageReported mileage events and possible odometer inconsistency signals.
Damage & accidentsReported collision, damage, severity, and event details.
TheftAvailable theft records and recovery status.
Junk, salvage & insuranceJunk, salvage, insurance, and total-loss records when available.
Safety recallsProvider-returned safety recall records.
Sales & auction historySale, auction, and listing events, sometimes including price or media.
Market analysisAvailable market, valuation, and depreciation context.
Coverage is not a clean-record guaranteeA missing category means the provider did not return coverage for it, not that no incident exists. An empty category that is present means no records were returned in that category. Objects, arrays, labels, dates, and nested fields can vary, and additional provider fields may appear without notice. Preserve unknown fields and do not turn missing data into a “clear” claim.

Idempotency, retries, and billing

  • The workflow is available on Pro and Scale. Starter accounts receive 403 plan_upgrade_required; credits cannot unlock reports on Starter. Reports do not consume standard included calls.
  • Reusing one Idempotency-Key with the same VIN returns the existing report. Reusing it for a different VIN returns 409 idempotency_conflict.
  • If an uncertain create response leaves a durable local report in submitting, call POST /v1/vehicles/history-reports/{id}/retry. This endpoint resubmits with the original provider idempotency key; read-only status polling never performs a submission.
  • Respect Retry-After. Reading the result before it is ready returns the retryable 409 report_not_ready; a provider throttle returns retryable429 report_rate_limited.
  • Billing settles when a completed result is stored. Invalid or unsupported VINs, failed generations, and action_required outcomes do not settle a report charge.
  • Coverage is conditional. When a title, accident, odometer, or ownership section is absent, present it as unavailable — never as a clean record.

Endpoint reference

11 synchronous endpoints in three groups. Each one lists its parameters, a runnable request, a real response captured from production, its own error codes, and the coverage caveats that matter when you build on it. The asynchronous history-report contract is documented separately above because it spans three resources.

Identity & safety

Resolve a VIN into who the vehicle is, what the factory built, and what has been recalled since.

GET/v1/vehicles/vin/{vin}vin_decode

VIN Decode

Decodes a VIN into canonical, normalized vehicle identity: year, make, model, trim, body style, drivetrain, fuel, transmission, cylinders, doors and engine.

Starter
$0.004
Pro
$0.0025
Scale
$0.0015
Per
successful lookup
Timeout
10 s

Data source

Store-first. We look the VIN up in our scraped-listings Postgres store and return a matching row with origin: "store". Store fields can come from the listing source or prior enrichment, and completeness varies. When the VIN is unknown to us we fall back to a live NHTSA vPIC DecodeVinValuesBatch call and return origin: "vpic".

Path parameters

VIN Decode path parameters
ParameterTypeRequiredDescription
vinstringExactly 17 letters or digits; I, O and Q are not allowedRequiredVehicle Identification Number. Case-insensitive — the API upper-cases it before the lookup and echoes the upper-cased form back in vin. The decoder also rejects a bad check digit for North American VINs (first character 1–5).

Example

Request
curl -sS https://api.vehicles.dev/v1/vehicles/vin/5TDDZRBHXNS221317 \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -H "Accept: application/json"
200 · 2022 Toyota Highlander Limited
{
  "origin": "store",
  "source": "carscrape",
  "vehicle": {
    "year": 2022,
    "make": "Toyota",
    "model": "Highlander",
    "trim": "Limited",
    "body_style": "SUV",
    "drivetrain": "AWD",
    "fuel": "Gasoline",
    "transmission": "Automatic",
    "cylinders": 6,
    "doors": 4
  },
  "vin": "5TDDZRBHXNS221317"
}

Response fields

VIN Decode response fields
FieldTypeDescription
origin"store" | "vpic""store" = matched and served from our scraped store, whose field sources and completeness can vary. "vpic" = decoded live against NHTSA for this request.
source"carscrape"Constant provenance marker for the backing data service.
vehicleobjectCanonical decoded fields. Possible keys: year, make, model, trim, body_style, drivetrain, fuel, transmission, cylinders, doors, engine. Null values are omitted.
vinstringThe upper-cased VIN that was decoded.

Errors

VIN Decode endpoint-specific errors
StatuscodeRetryableWhen it happens
400invalid_vinNoThe backing decoder rejected the VIN. The public route requires exactly 17 VIN-safe letters or digits (never I, O or Q); North American VINs whose first character is 1–5 must also pass their check digit.
404vin_not_decodableNoNeither our store nor NHTSA vPIC could resolve a make or model for that VIN.
503decode_upstream_unavailableYesThe decode service was unreachable or the 10 s budget elapsed.
503decode_upstream_errorYesThe decode service returned an unexpected status — typically when live NHTSA vPIC is down for a VIN we have never seen.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • Keys with null values are omitted entirely, so the key set inside vehicle varies per VIN. Always treat every field as optional — vPIC commonly returns no trim or door count, especially for EVs.
  • engine is a derived string composed from vPIC displacement, engine configuration and cylinder count (for example "3.5L V-Shaped 6cyl") and is frequently absent on store-origin rows.
  • Values are normalized to a controlled vocabulary: drivetrain resolves to AWD, FWD, RWD, 2WD or 4WD, and unmapped vPIC axle notation is dropped rather than passed through. Use Vehicle Specifications when you want raw vPIC vocabulary instead.
  • origin: "vpic" responses depend on a live external call, so they are slower and track NHTSA availability.

VIN Decode: overview, worked examples and pricing →

GET/v1/vehicles/specifications/{vin}specifications

Vehicle Specifications

Returns the full manufacturer spec sheet for a VIN: body class, doors, seats, drive type, cylinders, displacement, horsepower, engine configuration, transmission style and speeds, GVWR class, plant country and manufacturer.

Starter
$0.0015
Pro
$0.0015
Scale
$0.001
Per
successful lookup
Timeout
15 s

Data source

A live NHTSA vPIC DecodeVinValuesBatch call on every request — no store cache is consulted. The raw vPIC row is projected through a fixed 20-field allowlist and blank values are dropped.

Path parameters

Vehicle Specifications path parameters
ParameterTypeRequiredDescription
vinstringExactly 17 letters or digits; I, O and Q are not allowedRequiredVehicle Identification Number. Case-insensitive; echoed back upper-cased. North American VINs whose first character is 1–5 must pass their check digit.

Example

Request
curl -sS https://api.vehicles.dev/v1/vehicles/specifications/5TDDZRBHXNS221317 \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -H "Accept: application/json"
200 · full vPIC spec sheet
{
  "source": "carscrape",
  "specifications": {
    "make": "TOYOTA",
    "model": "Highlander",
    "year": "2022",
    "trim": "Limited",
    "series": "75 Series",
    "body_class": "Sport Utility Vehicle [SUV]/Multipurpose Vehicle [MPV]",
    "doors": "5",
    "seats": "7",
    "drive_type": "4WD/4-Wheel Drive/4x4",
    "cylinders": "6",
    "displacement_l": "3.5",
    "engine_hp": "295",
    "fuel_primary": "Gasoline",
    "engine_config": "V-Shaped",
    "transmission_style": "Automatic",
    "transmission_speeds": "8",
    "gvwr": "Class 1D: 5,001 - 6,000 lb (2,268 - 2,722 kg)",
    "plant_country": "UNITED STATES (USA)",
    "manufacturer": "TOYOTA MOTOR MANUFACTURING, INDIANA, INC.",
    "vehicle_type": "MULTIPURPOSE PASSENGER VEHICLE (MPV)"
  },
  "vin": "5TDDZRBHXNS221317"
}

Response fields

Vehicle Specifications response fields
FieldTypeDescription
source"carscrape"Constant provenance marker for the backing data service.
specificationsobjectvPIC spec sheet on snake_case keys: make, model, year, trim, series, body_class, doors, seats, drive_type, cylinders, displacement_l, engine_hp, fuel_primary, engine_config, transmission_style, transmission_speeds, gvwr, plant_country, manufacturer, vehicle_type. All values are strings; blanks are omitted.
vinstringThe upper-cased VIN the sheet belongs to.

Errors

Vehicle Specifications endpoint-specific errors
StatuscodeRetryableWhen it happens
400invalid_vinNoThe backing validator or vPIC rejected the VIN after the public route accepted its exactly 17 VIN-safe characters. North American VINs must pass their check digit.
404specifications_not_foundNovPIC returned a row with neither Make nor Model for that VIN.
503specifications_upstream_unavailableYesThe specifications service was unreachable or the 15 s budget elapsed.
503specifications_upstream_errorYesAn unexpected upstream status, notably when NHTSA vPIC itself is erroring.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • Every value inside specifications is a raw vPIC string. year, doors, seats, cylinders, engine_hp, displacement_l and transmission_speeds are NOT numbers — parse them client-side.
  • Values keep unnormalized vPIC vocabulary: make comes back as "TOYOTA" and drive_type as axle notation like "4WD/4-Wheel Drive/4x4". Use VIN Decode when you want normalized values.
  • Blank fields are omitted, and coverage varies heavily by manufacturer and model year. Older VINs and EVs commonly lack trim, seats and engine_hp.
  • This is an uncached live external call, so latency tracks NHTSA vPIC against a 15 s route budget.

Vehicle Specifications: overview, worked examples and pricing →

GET/v1/vehicles/recalls/{vin}recalls

Recalls & Safety

Resolves a VIN to its year, make and model, then returns every NHTSA safety recall campaign for that vehicle — component, summary, consequence, remedy, campaign number and report date.

Starter
$0.01
Pro
$0.01
Scale
$0.007
Per
successful lookup
Timeout
15 s

Data source

Two stages. We resolve year/make/model store-first, falling back to a live vPIC decode when the VIN is unknown to us, then query the NHTSA recallsByVehicle API and project each campaign onto six fields.

Path parameters

Recalls & Safety path parameters
ParameterTypeRequiredDescription
vinstringExactly 17 letters or digits; I, O and Q are not allowedRequiredVehicle Identification Number. Case-insensitive; echoed back upper-cased. North American VINs whose first character is 1–5 must pass their check digit.

Example

Request
curl -sS https://api.vehicles.dev/v1/vehicles/recalls/5TDDZRBHXNS221317 \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -H "Accept: application/json"
200 · truncated to 2 of 5 campaigns
{
  "count": 5,
  "make": "Toyota",
  "model": "Highlander",
  "recalls": [
    {
      "component": "EQUIPMENT:OTHER:LABELS",
      "summary": "Gulf States Toyota, Inc. (GST) is recalling certain 2022 4Runner, Tacoma, Highlander, and Highlander Hybrid vehicles. The load carrying capacity modification labels may not be permanent and can fade, becoming illegible.",
      "consequence": "An illegible label may allow the vehicle to be overloaded, increasing the risk of a crash.",
      "remedy": "GST will notify owners by mail, including a replacement load carrying capacity modification label for their vehicle and detailed replacement instructions, free of charge.",
      "campaign_number": "22V310000",
      "report_date": "06/05/2022"
    },
    {
      "component": "STRUCTURE:BODY:BUMPERS",
      "summary": "Toyota Motor Engineering & Manufacturing (Toyota) is recalling certain 2020-2023 Highlander & Highlander Hybrid vehicles. During normal vehicle operation, minor impact to the front lower bumper cover may result in the cover coming loose or detaching.",
      "consequence": "A detached front bumper cover can become a road hazard, increasing the risk of a crash.",
      "remedy": "Dealers will repair or replace the upper and lower front bumper covers as necessary, free of charge.",
      "campaign_number": "23V720000",
      "report_date": "26/10/2023"
    }
  ],
  "source": "carscrape",
  "vin": "5TDDZRBHXNS221317",
  "year": 2022
}

Response fields

Recalls & Safety response fields
FieldTypeDescription
countintegerNumber of campaigns returned; always equal to recalls.length.
makestringMake the lookup ran against, as resolved from the store or vPIC.
modelstringModel the lookup ran against, as resolved from the store or vPIC.
recallsarray<object>One entry per NHTSA campaign with component, summary, consequence, remedy, campaign_number and report_date. Individual values may be null.
source"carscrape"Constant provenance marker for the backing data service.
vinstringThe upper-cased VIN that was looked up.
yearintegerModel year the lookup ran against.

Errors

Recalls & Safety endpoint-specific errors
StatuscodeRetryableWhen it happens
400invalid_vinNoThe backing validator rejected the VIN. North American VINs must pass their check digit.
404recalls_not_resolvableNoA valid VIN could not be resolved to year/make/model from either the store or vPIC, so no recall query could be made.
503recalls_upstream_unavailableYesThe recalls service was unreachable or the 15 s budget elapsed.
503recalls_upstream_errorYesAn unexpected upstream status, notably when the NHTSA recalls API or the fallback vPIC decode is down.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • Recalls are matched at the year/make/model level, not per VIN. A campaign may name sibling models or a narrower build range ("certain 2022 4Runner, Tacoma, Highlander…") and does not prove this specific VIN is affected — confirm the campaign number with the manufacturer.
  • "count": 0 with HTTP 200 is a normal, chargeable result meaning the vehicle resolved and NHTSA lists no campaigns for it.
  • report_date is passed through verbatim from NHTSA and its formatting is inconsistent across campaigns — both 06/05/2022 and 26/10/2023 appear in live data. Do not assume a single date format.
  • Any of the six per-recall fields can be null when NHTSA omits them, and resolution depends on our normalized make/model matching NHTSA's vocabulary.

Recalls & Safety: overview, worked examples and pricing →

Valuation & ownership

What the vehicle is worth today, how that value decays, and what it costs to run.

GET/v1/vehicles/market-valuemarket_value

Market Value

Returns an ML-predicted current market asking price in USD for a year/make/model, optionally sharpened with mileage, trim, state and other attributes, plus the model's median absolute percentage error.

Starter
$0.015
Pro
$0.015
Scale
$0.01
Per
model estimate
Timeout
5 s

Data source

A gradient-boosted asking-price model trained on our scraped US dealer-listings store. The estimate is a market ASKING price from live dealer listings, not a realized transaction price.

Query parameters

Market Value query parameters
ParameterTypeRequiredDescription
makestring1–64 charactersRequiredVehicle make. Canonicalized upstream, so casing is forgiving here.
modelstring1–64 charactersRequiredVehicle model. NOT canonicalized — must match the store's canonical casing ("Camry", "F-150", "4Runner") or the model treats it as an unseen category.
yearinteger1900–2100RequiredModel year. Vehicle age is derived as max(currentYear − year, 0).
milesintegerminimum 0OptionalOdometer reading. Also drives a derived miles_per_year feature.
trimstring1–64 charactersOptionalTrim level, for example "SE" or "Limited".
statestringexactly 2 charactersOptionalUS state code. Must be UPPERCASE ("TX"); lowercase is an unseen category and behaves identically to omitting it.
conditionstring1–16 charactersdefault usedOptionalVehicle condition.
drivetrainstring1–16 charactersOptionalDrivetrain, for example "awd", "fwd" or "4wd".
fuelstring1–32 charactersOptionalFuel type, for example "gasoline", "hybrid" or "electric".
transmissionstring1–32 charactersOptionalTransmission type, for example "automatic".
body_stylestring1–32 charactersOptionalBody style, for example "sedan", "suv" or "pickup".
colorstring1–32 charactersOptionalExterior color; normalized upstream to a base color.
base_msrpintegerminimum 0OptionalOriginal MSRP in USD, used as a model feature when known.

Example

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/market-value \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d make=Toyota -d model=Camry -d year=2021 \
  -d miles=45000 -d trim=SE -d state=TX
200 · 2021 Toyota Camry SE, 45,000 mi, TX
{
  "currency": "USD",
  "estimateUsd": 26096,
  "inputs": {
    "year": 2021,
    "age": 5,
    "make": "Toyota",
    "model": "Camry",
    "trim": "SE",
    "miles": 45000,
    "miles_per_year": 9000,
    "condition": "used",
    "seller_type": "dealer",
    "state": "TX"
  },
  "medianApePct": 3.7,
  "source": "carscrape"
}

Response fields

Market Value response fields
FieldTypeDescription
currencystringISO currency of the estimate. Always "USD" today.
estimateUsdintegerPredicted market asking price in whole USD.
inputsobjectEcho of the non-null feature vector the model actually scored — the fastest way to confirm which of your inputs were used. Null features are omitted, and miles_per_year can be fractional.
medianApePctnumberMedian absolute percentage error of the loaded pricing model on holdout data. 3.7 means a typical estimate lands within about 3.7%.
source"carscrape"Constant provenance marker for the backing data service.

Errors

Market Value endpoint-specific errors
StatuscodeRetryableWhen it happens
500valuation_upstream_errorNoThe valuation service returned an unexpected non-2xx status.
503valuation_model_unavailableYesNo trained pricing model is currently loaded.
503valuation_upstream_unavailableYesThe valuation service was unreachable or the 5 s budget elapsed.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • There is no 404 path. The model always returns a number, even for a make/model it has never seen — always inspect inputs to confirm which of your parameters were actually recognized.
  • Casing matters. Verified on live data for a 2021 Camry SE at 45,000 miles: model="Camry" with state="TX" returns $26,096, model="camry" returns $23,882, and state="tx" returns $21,248 (identical to omitting state). Send Title-Case model and uppercase state.
  • Omitting miles materially lowers accuracy: it also drives a derived miles_per_year feature.
  • medianApePct is a model-wide holdout metric, not a per-vehicle confidence score.

Market Value: overview, worked examples and pricing →

GET/v1/vehicles/depreciationdepreciation

Depreciation

Returns a per-model resale-value curve: MSRP retention observed by model year, a smoothed log-linear retention curve extrapolated across ages 0–12, and a single fitted annual decay rate.

Starter
$0.015
Pro
$0.015
Scale
$0.01
Per
model forecast
Timeout
5 s

Data source

A precomputed depreciation table built from median scraped listing prices joined against model-year MSRP. Live coverage today is 1,732 model-year rows across 223 make/model pairs and 28 makes, model years 2013–2026.

Query parameters

Depreciation query parameters
ParameterTypeRequiredDescription
makestring1–64 charactersRequiredVehicle make. Matched with exact, case-sensitive equality, so canonical Title Case is required: "Toyota", "Mercedes-Benz", "Jeep". "toyota" returns 404.
modelstring1–64 charactersRequiredVehicle model, also exact and case-sensitive: "Camry", "Grand Cherokee", "4Runner", "S-Class". No fuzzy matching or trim stripping.

Example

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/depreciation \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d make=Toyota -d model=Camry
200 · arrays trimmed (real response has 12 model years and 13 curve points)
{
  "annualDecay": 0.0652,
  "byModelYear": [
    { "year": 2026, "age": 0, "msrp": 33058, "median_price": 34779, "n_listings": 4038, "retention": 0.9788, "curve_beta": -0.06739, "confidence": 0.82 },
    { "year": 2025, "age": 1, "msrp": 32263, "median_price": 29399, "n_listings": 3416, "retention": 0.9588, "curve_beta": -0.06739, "confidence": 0.82 },
    { "year": 2024, "age": 2, "msrp": 30014, "median_price": 26370, "n_listings": 1841, "retention": 0.9233, "curve_beta": -0.06739, "confidence": 0.82 },
    { "year": 2016, "age": 10, "msrp": 25715, "median_price": 13490, "n_listings": 78, "retention": 0.5116, "curve_beta": -0.06739, "confidence": 0.99 },
    { "year": 2015, "age": 11, "msrp": 27605, "median_price": 13694, "n_listings": 87, "retention": 0.4741, "curve_beta": -0.06739, "confidence": 0.98 }
  ],
  "curveByAge": [
    { "age": 0, "retention": 0.979 },
    { "age": 1, "retention": 0.915 },
    { "age": 2, "retention": 0.855 },
    { "age": 5, "retention": 0.699 },
    { "age": 10, "retention": 0.499 },
    { "age": 12, "retention": 0.436 }
  ],
  "make": "Toyota",
  "model": "Camry",
  "source": "carscrape"
}

Response fields

Depreciation response fields
FieldTypeDescription
annualDecaynumber | nullFitted annual rate of value loss as a fraction: 0.0652 means about 6.5% of remaining value is lost per year. Null when the model has no fitted curve.
byModelYeararray<object>Observed rows, newest model year first, with upstream snake_case keys: year, age, msrp, median_price, n_listings, retention, curve_beta, confidence.
curveByAgearray<object> | nullSmoothed retention curve with exactly 13 points, age 0 through 12, each { age, retention }. Null when no curve was fitted for that model.
makestringEcho of the requested make.
modelstringEcho of the requested model.
source"carscrape"Constant provenance marker for the backing data service.

Errors

Depreciation endpoint-specific errors
StatuscodeRetryableWhen it happens
404depreciation_not_foundNoNo depreciation data for that make and model. Returned for any pair outside the covered set — including correct models sent with the wrong casing.
503depreciation_upstream_unavailableYesThe service was unreachable or the 5 s budget elapsed.
503depreciation_upstream_errorYesAn unexpected non-2xx, non-404 upstream status.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • This is a MODEL-level curve, not VIN- or trim-level. There are no year, mileage or trim parameters, and the same curve comes back regardless of the specific car.
  • Array elements keep upstream snake_case keys (n_listings, median_price, curve_beta) while the top-level envelope is camelCase. That asymmetry is intentional.
  • curveByAge retention is capped at 1.0, but byModelYear.retention can approach or exceed 1.0 for current model years where the median listing price runs above base MSRP — the 2026 Camry row shows median_price 34,779 against msrp 33,058.
  • A small number of stored models have no fitted curve_beta. For those, annualDecay and curveByAge are both null while byModelYear is still populated.

Depreciation: overview, worked examples and pricing →

GET/v1/vehicles/ownership-costsownership_costs

Ownership Costs

Returns EPA-sourced annual and five-year fuel cost, combined MPG, fuel type and tailpipe CO2 for a year/make/model. Fuel and operating cost only — maintenance, insurance and depreciation are excluded.

Starter
$0.045
Pro
$0.045
Scale
$0.035
Per
model estimate
Timeout
15 s

Data source

Two live calls to EPA fueleconomy.gov per request: one to resolve year/make/model to trim configurations, then one for the first configuration returned. Not scraped data, and not cached.

Query parameters

Ownership Costs query parameters
ParameterTypeRequiredDescription
makestring1–64 charactersRequiredVehicle make. EPA lookup is case-insensitive; the value is echoed back verbatim.
modelstring1–64 charactersRequiredVehicle model as EPA names it ("Camry", "F150 Pickup 2WD"). Case-insensitive; echoed back verbatim.
yearinteger1900–2100; EPA coverage starts at 1984RequiredModel year. Earlier years fail upstream rather than returning a clean 404.

Example

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/ownership-costs \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d make=Toyota -d model=Camry -d year=2021
200 · 2021 Toyota Camry
{
  "annualFuelCostUsd": 2350,
  "co2GramsPerMile": 338,
  "combinedMpg": 26,
  "config": "Auto (S8), 6 cyl, 3.5 L",
  "fiveYearFuelCostUsd": 11750,
  "fuelType": "Regular",
  "make": "Toyota",
  "model": "Camry",
  "note": "Fuel/operating cost from EPA; excludes maintenance, insurance, and depreciation.",
  "source": "carscrape",
  "trimsAvailable": 1,
  "year": 2021
}

Response fields

Ownership Costs response fields
FieldTypeDescription
annualFuelCostUsdinteger | nullEPA estimated annual fuel cost in USD. Null when EPA omits the field.
co2GramsPerMileinteger | nullTailpipe CO2 in grams per mile, truncated to an integer. 0 for battery-electric vehicles.
combinedMpginteger | nullEPA combined city/highway MPG, truncated to an integer. MPGe for EVs.
configstring | nullHuman-readable label of the specific EPA trim configuration that was priced, for example "Auto (S8), 6 cyl, 3.5 L".
fiveYearFuelCostUsdinteger | nullannualFuelCostUsd × 5. Not inflation- or discount-adjusted.
fuelTypestring | nullEPA fuel label, for example "Regular", "Premium" or "Electricity".
makestringEcho of the requested make, verbatim.
modelstringEcho of the requested model, verbatim.
trimsAvailableintegerNumber of EPA trim configurations that exist for this year/make/model. Greater than 1 means the returned numbers describe only the config trim.
notestringFixed disclaimer string, present on every response.
source"carscrape"Constant provenance marker for the backing data service.
yearintegerEcho of the requested year.

Errors

Ownership Costs endpoint-specific errors
StatuscodeRetryableWhen it happens
404ownership_not_foundNoEPA returned a well-formed menu with zero trim options. This is rarer than you would expect — see the note below.
503ownership_upstream_unavailableYesThe service was unreachable or the 15 s budget elapsed.
503ownership_upstream_errorYesIn practice this is the most common failure for bad inputs: an unknown model or a pre-1984 year makes EPA return an empty body, which surfaces here as 503 rather than 404.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • Fuel only. No maintenance, insurance, tires, taxes, fees or depreciation are included — which is why the fixed note string ships in every response. Pair this with Depreciation for a fuller total-cost-of-ownership picture.
  • The figures describe the first EPA trim configuration only. When trimsAvailable is greater than 1, treat that as a signal that costs vary across trims. For the 2021 Camry, EPA resolves the V6 configuration, not the best-selling four-cylinder.
  • Unlike Depreciation, make and model matching is case-insensitive here because EPA does the lookup — but make and model are echoed back exactly as you sent them, unnormalized.
  • annualFuelCostUsd assumes EPA's 15,000 mi/yr at current national fuel prices, and fiveYearFuelCostUsd is simply that figure times five with no inflation or discounting.
  • Two live EPA round trips per call with no caching means latency is upstream-bound against a 15 s route budget.

Ownership Costs: overview, worked examples and pricing →

GET/v1/vehicles/total-cost-ownershiptotal_cost_ownership

Total Cost of Ownership

Models the cost of owning a vehicle over a holding period, component by component, with each component tagged by how much we actually know about it.

Starter
$0.06
Pro
$0.045
Scale
$0.035
Per
model estimate
Timeout
15 s

Data source

A composite. Depreciation comes from our own dealer-asking-price curves; fuel from the EPA combined rating with the fuel price implied by the EPA annual cost; taxes and fees from the state rate table; financing from the loan calculator. Maintenance, repairs and insurance are class-level planning assumptions, not observations of the vehicle.

Query parameters

Total Cost of Ownership query parameters
ParameterTypeRequiredDescription
yearinteger1900–2100RequiredModel year of the vehicle.
makestringExact and case-sensitiveRequiredVehicle make.
modelstringExact and case-sensitiveRequiredVehicle model.
purchase_pricenumber> 0OptionalPurchase price. Defaults to the model year's median asking price when we hold one.
yearsinteger1–10; defaults to 5OptionalHolding period in years.
annual_milesinteger100–200000; defaults to 12000OptionalMiles driven per year. Fuel, maintenance and repairs all scale with it.
statestringTwo-letter state codeOptionalRegistration state, used for the purchase tax and fee component.
aprnumber0–1OptionalNominal annual loan rate as a decimal. Financing interest is omitted entirely when absent.
down_paymentnumber≥ 0OptionalCash down, used only to size the financed amount.
loan_monthsinteger1–120; defaults to 60OptionalLoan term in months.
maintenance_per_milenumber≥ 0OptionalReplace the class-level maintenance assumption with your own USD per mile.
repair_per_milenumber≥ 0OptionalReplace the class-level repair assumption with your own USD per mile.
insurance_per_yearnumber≥ 0OptionalReplace the class-level insurance assumption with your own USD per year.

Example

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/total-cost-ownership \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d year=2023 -d make=Honda -d model=Accord -d years=5 -d annual_miles=12000 -d state=TX
200 · five-year ownership estimate for a 2023 Honda Accord
{
  "components": {
    "depreciation": { "amount": 12040.0, "basis": "measured" },
    "financingInterest": { "amount": null, "basis": "unavailable" },
    "fuel": { "amount": 7000.0, "basis": "published" },
    "insurance": { "amount": 7750.0, "basis": "reference", "perYear": 1550.0 },
    "maintenance": { "amount": 5700.0, "basis": "reference", "perMile": 0.095 },
    "repairs": { "amount": 2100.0, "basis": "reference", "perMile": 0.035 },
    "taxesAndFees": { "amount": 2600.0, "basis": "reference" }
  },
  "costPerMile": 0.619,
  "costPerYear": 7438.0,
  "measuredShareOfTotal": 0.513,
  "missingComponents": ["financingInterest"],
  "source": "carscrape",
  "total": 37190.0,
  "vehicleClass": "medium_sedan",
  "years": 5
}

Response fields

Total Cost of Ownership response fields
FieldTypeDescription
componentsobjectPer-component amount and basis. Basis is one of measured, published, computed, caller_supplied, reference or unavailable.
costPerMilenumber | nullTotal divided by total miles driven over the holding period.
costPerYearnumber | nullTotal divided by the holding period in years.
measuredShareOfTotalnumber | nullShare of the total that came from measurement, computation or your own inputs rather than a class-level assumption.
missingComponentsstring[]Components excluded from the total because they could not be computed.
totalnumber | nullSum of the components we could compute. Null when none could be.
vehicleClassstringReference cost class the assumptions were drawn from.

Errors

Total Cost of Ownership endpoint-specific errors
StatuscodeRetryableWhen it happens
404ownership_not_foundNoNo purchase price could be resolved. Pass purchase_price for a model we hold no depreciation curve for.
503ownership_upstream_unavailableYesThe ownership service was unreachable or the 15 s budget elapsed.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • Read `components[].basis` before trusting a number. `measured` is our depreciation model, `published` is EPA, `computed` is loan arithmetic, `caller_supplied` is your own input, and `reference` is a national class-level assumption that is NOT an observation of this vehicle.
  • `measuredShareOfTotal` tells you what fraction of the total does not rest on a reference assumption. A low value means most of the estimate is a planning average.
  • A component we cannot compute is null with basis `unavailable` and is EXCLUDED from the total, never counted as zero. `missingComponents` lists them.
  • Maintenance, repair and insurance can each be replaced with your own figures; do that whenever you have real data, because insurance in particular is driven far more by driver and coverage than by vehicle class.
  • Depreciation inherits the make/model scope of our depreciation curves, so it is not trim- or VIN-specific.

Total Cost of Ownership: overview, worked examples and pricing →

GET/v1/vehicles/loanloan_payments

Loan & Payments

Calculates the amortized monthly payment, total interest, and per-year interest split for an auto loan.

Starter
$0.001
Pro
$0.0007
Scale
$0.0005
Per
calculation
Timeout
5 s

Data source

Nothing. This is deterministic arithmetic over the values you send: no vehicle lookup, no rate shopping, no upstream call.

Query parameters

Loan & Payments query parameters
ParameterTypeRequiredDescription
pricenumber> 0RequiredVehicle sale price before fees, rebates and trade-in.
aprnumber0–1RequiredNominal annual rate as a decimal. 0.069 is 6.9%.
monthsinteger1–120; defaults to 60OptionalLoan term in months.
down_paymentnumber≥ 0OptionalCash down.
trade_in_valuenumber≥ 0OptionalValue allowed for the trade-in vehicle.
trade_in_payoffnumber≥ 0OptionalBalance still owed on the trade-in. Exceeding its value creates negative equity.
rebatenumber≥ 0OptionalManufacturer or dealer cash rebate applied to the price.
feesnumber≥ 0OptionalFees rolled into the amount financed.

Example

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/loan \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d price=35000 -d down_payment=5000 -d apr=0.069 -d months=60
200 · $30,000 financed at 6.9% over 60 months
{
  "amountFinanced": 30000.0,
  "apr": 0.069,
  "interestByYear": [1907.47, 1536.82, 1139.77, 714.43, 258.81],
  "monthlyPayment": 592.62,
  "months": 60,
  "source": "carscrape",
  "totalInterest": 5557.29,
  "totalPaid": 35557.29,
  "tradeInEquity": 0.0
}

Response fields

Loan & Payments response fields
FieldTypeDescription
amountFinancednumberPrincipal after down payment, rebate and trade-in equity, plus financed fees.
interestByYearnumber[]Interest paid in each 12-month block. A partial final year appears as a stub.
monthlyPaymentnumberLevel monthly payment.
totalInterestnumberTotal interest over the full term.
totalPaidnumberPrincipal plus total interest.
tradeInEquitynumberTrade-in value minus payoff. Negative when the trade is underwater.

Errors

Loan & Payments endpoint-specific errors
StatuscodeRetryableWhen it happens
400invalid_requestNoA parameter failed validation — a non-positive price, a term outside 1–120 months, or an APR outside 0–1.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • `apr` is the nominal annual rate as a decimal compounded monthly (0.069 for 6.9%), which is how US auto loans quote and amortize.
  • Negative trade-in equity is FINANCED, not subtracted: when trade_in_payoff exceeds trade_in_value, the shortfall increases the amount financed. Getting this backwards understates the loan by twice the negative equity.
  • A price fully covered by down payment, rebate and trade equity returns a zero loan rather than a negative one.
  • Excludes gap insurance, service contracts, and lender-specific fees. Add them through `fees` if you want them financed.

Loan & Payments: overview, worked examples and pricing →

GET/v1/vehicles/purchase-costspurchase_costs

Purchase Tax & Fees

Estimates purchase sales or use tax, including the per-state trade-in credit rule, plus title, registration and documentation fees.

Starter
$0.001
Pro
$0.0007
Scale
$0.0005
Per
calculation
Timeout
5 s

Data source

A state-level reference table of motor-vehicle sales/use tax rates and trade-in credit rules, plus national planning averages for title, registration and documentation fees.

Query parameters

Purchase Tax & Fees query parameters
ParameterTypeRequiredDescription
pricenumber> 0RequiredVehicle sale price.
statestringTwo-letter state codeOptionalState of registration. Determines both the rate and the trade-in rule.
trade_in_valuenumber≥ 0OptionalTrade-in allowance. Reduces the taxable amount only where the state permits it.
rebatenumber≥ 0OptionalCash rebate applied before tax.
tax_ratenumber0–1OptionalOverride the reference rate, for example to include a known local surtax. The state's trade-in rule still applies.
title_and_registrationnumber≥ 0OptionalReplace the national title and registration planning average.
doc_feenumber≥ 0OptionalReplace the national documentation fee planning average.

Example

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/purchase-costs \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d price=40000 -d state=TX -d trade_in_value=10000
200 · $40,000 purchase in Texas with a $10,000 trade-in
{
  "documentationFee": 500.0,
  "feesTotal": 850.0,
  "rateTableAsOf": "2026-01-01",
  "salesTax": 1875.0,
  "source": "carscrape",
  "state": "TX",
  "taxRate": 0.0625,
  "taxRateBasis": "reference_table",
  "taxableBase": 30000.0,
  "titleAndRegistration": 350.0,
  "total": 2725.0,
  "tradeInReducesTaxableAmount": true
}

Response fields

Purchase Tax & Fees response fields
FieldTypeDescription
feesTotalnumberCombined title, registration and documentation fees.
rateTableAsOfstringDate the reference rate table reflects.
salesTaxnumber | nullEstimated sales or use tax. Null when no rate is available for the state.
taxRatenumber | nullState-level rate applied. Null when unknown.
taxRateBasisstringWhere the rate came from: "reference_table", "caller_supplied" or "unavailable".
taxableBasenumber | nullAmount actually taxed after any trade-in credit and rebate.
tradeInReducesTaxableAmountboolean | nullWhether this state lets a trade-in reduce the taxable amount.
totalnumber | nullTax plus fees. Null when the tax could not be estimated.

Errors

Purchase Tax & Fees endpoint-specific errors
StatuscodeRetryableWhen it happens
400invalid_requestNoA parameter failed validation — a non-positive price or a rate outside 0–1.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • This is an estimate, not tax advice. Confirm with the state revenue department or the selling dealer before relying on a figure.
  • Whether a trade-in reduces the taxable amount is a per-state rule and is the part of this worth having: it moves the tax by the trade-in value times the rate, which dwarfs rate precision on any real deal.
  • State-level rates only. County, city and special-district surtaxes are excluded and are material in several states.
  • An unknown state returns a null taxRate and a null salesTax rather than a guessed rate. Pass `tax_rate` to supply your own.
  • `rateTableAsOf` dates the table. Rates change by legislation; treat a stale date as a reason to verify.

Purchase Tax & Fees: overview, worked examples and pricing →

Market & listings

The underlying marketplace: live listings and their imagery.

GET/v1/vehicles/listingslive_listings

Search Vehicle Listings

Search the normalized US car-listings store by make, model, year, price, mileage and state, and return a paginated page of listing rows plus the total match count.

Starter
$0.002
Pro
$0.0015
Scale
$0.001
Per
successful API call
Timeout
5 s

Data source

Our normalized US car-listings store, scraped from autolist, truecar, iseecars and dealer JSON-LD sites and joined against the canonical vehicles table.

Query parameters

Search Vehicle Listings query parameters
ParameterTypeRequiredDescription
makestring1–64 charactersOptionalCanonical make, exact and case-sensitive: "Ford", "Toyota".
modelstring1–64 charactersOptionalCanonical model, exact and case-sensitive: "F-150", "Camry".
year_mininteger1900–2100OptionalMinimum model year, inclusive.
year_maxinteger1900–2100OptionalMaximum model year, inclusive.
price_minintegerminimum 0OptionalMinimum current asking price in USD, inclusive.
price_maxintegerminimum 0OptionalMaximum current asking price in USD, inclusive.
mileage_maxintegerminimum 0OptionalMaximum odometer reading in miles, inclusive.
statestringexactly 2 charactersOptionalUS state code of the listing location. Upper-cased server-side, so "ct" and "CT" behave identically.
conditionstring1–32 characters; "used", "new", "cpo" are present in the storeOptionalListing condition, exact match.
seller_typestring1–32 characters; "dealer" and "auction" are present in the storeOptionalSeller type, exact match.
sourcestring1–32 characters; "autolist", "jsonld_site", "truecar", "iseecars"OptionalScrape source, exact match.
activebooleanOptionaltrue returns only listings still live at the last crawl; false returns only listings that have disappeared.
soldbooleanOptionalRestrict to realized auction sales when true, or exclude them when false. See the coverage note below.
min_qualitynumber0–1 inclusiveOptionalMinimum per-row data-quality score — the share of expected fields parsed successfully.
valid_vinbooleanOptionaltrue returns only rows whose VIN passed check-digit and format validation.
sortstringprice | miles | year | days_on_marketdefault priceOptionalSort column.
orderstringasc | descdefault ascOptionalSort direction.
limitinteger1–500default 50OptionalPage size.
offsetintegerminimum 0default 0OptionalRow offset for pagination; combine with total to page through the match set.

Example

Request
curl -sS -G https://api.vehicles.dev/v1/vehicles/listings \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -d make=Ford -d model=F-150 -d state=CT -d year_min=2026 \
  -d sort=price -d order=desc -d limit=1
200 · most expensive 2026 F-150 listed in CT
{
  "count": 1,
  "limit": 1,
  "offset": 0,
  "results": [
    {
      "vin": "1FTFW7L83TFA89342",
      "year": 2026,
      "make": "Ford",
      "model": "F-150",
      "trim": "Platinum",
      "body_style": "Truck",
      "fuel": "Gasoline",
      "drivetrain": "4WD",
      "transmission": "Automatic",
      "color": "Gray",
      "price": 77101,
      "miles": 9,
      "condition": "new",
      "title_status": null,
      "seller_type": "dealer",
      "dealer_name": "Shaker Family Ford Lincoln",
      "city": "Watertown",
      "state": "CT",
      "zip": null,
      "lat": 41.5922,
      "lng": -73.10814,
      "segment": "mainstream",
      "is_active": 1,
      "days_on_market": 3,
      "data_quality": 1.0,
      "vdp_url": "https://www.autolist.com/ford-f+150#vin=1FTFW7L83TFA89342",
      "source": "autolist",
      "vin_valid": 1
    }
  ],
  "source": "carscrape",
  "total": 12
}

Response fields

Search Vehicle Listings response fields
FieldTypeDescription
countintegerNumber of listing rows in results for this page.
limitintegerEffective page size applied — echoes the request, or 50 when limit was omitted.
offsetintegerEffective row offset applied — echoes the request, or 0.
resultsarray<object>Listing rows passed through from the store with upstream snake_case keys: vin, year, make, model, trim, body_style, fuel, drivetrain, transmission, color, price, miles, condition, title_status, seller_type, dealer_name, city, state, zip, lat, lng, segment, is_active, days_on_market, data_quality, vdp_url, source, vin_valid.
source"carscrape"Constant provenance marker for the backing data service.
totalintegerTotal rows matching the filters, ignoring limit and offset.

Errors

Search Vehicle Listings endpoint-specific errors
StatuscodeRetryableWhen it happens
500listings_upstream_errorNoThe listings service returned a non-2xx status.
503listings_upstream_unavailableYesThe listings service was unreachable or the 5 s budget elapsed.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • An empty search is not a 404. It returns 200 with total 0, count 0 and results [] — and it is still a billable successful call.
  • The route always applies the mainstream, priced segment. Auction sources (bringatrailer, carsandbids) and call-for-price rows are never returned, and there is no geo-radius search. Consequently sold=true currently matches nothing, because realized auction sales only exist in the excluded auction segment.
  • titleStatus reflects the marketplace's own salvage flag, not a DMV title record: "clean" means the source explicitly did not flag the car as salvage, which is weaker than a verified clean title. It is null where the source said nothing either way. Frame damage is deliberately not folded in, because a frame-damaged car can still hold a clean title.
  • zip is null for nearly all rows because our sources do not expose it, and miles is null on some rows.
  • days_on_market counts days since we first observed the listing, not the dealer's own listing age.
  • Paginate with limit and offset against total for a single page, or pass the previous response's nextCursor as cursor to walk the whole set. The cursor is keyset-based, so rows written between pages cannot duplicate or skip results the way offset can. There are no Link headers.
  • Set facets=true to get the top values and counts per field over the same filtered set, in the same call.

Search Vehicle Listings: overview, worked examples and pricing →

GET/v1/vehicles/photos/{vin}vehicle_photos

Vehicle Photos

Returns the photo gallery for a VIN in the source's own order, the dealer's advertised photo count, and a link back to the source listing.

Starter
$0.001
Pro
$0.0009
Scale
$0.0007
Per
successful lookup
Timeout
5 s

Data source

The listings table. We pick the VIN's best current listing that has an image — active listings win, then the most recently ingested. Image URLs are CDN links owned by the source marketplace and are not rehosted by us.

Path parameters

Vehicle Photos path parameters
ParameterTypeRequiredDescription
vinstringExactly 17 letters or digits; I, O and Q are not allowedRequiredVehicle Identification Number. Upper-cased before the lookup. North American VINs whose first character is 1–5 must pass their check digit.

Example

Request
curl -sS https://api.vehicles.dev/v1/vehicles/photos/1FTFW3L57TKD09376 \
  -H "Authorization: Bearer $VEHICLES_API_KEY" \
  -H "Accept: application/json"
200 · partial gallery for a 2026 Ford F-150
{
  "galleryComplete": false,
  "galleryCount": 3,
  "listingSource": "autolist",
  "listingUrl": "https://www.autolist.com/ford-f+150#vin=1FTFW3L57TKD09376",
  "photoCount": 39,
  "photos": [
    "https://static.cargurus.com/images/forsale/2026/07/18/07/13/2026_ford_f-150-pic-6182805071162814707-1024x768.jpeg",
    "https://static.cargurus.com/images/forsale/2026/07/18/07/13/2026_ford_f-150-pic-3355019284471120388-1024x768.jpeg",
    "https://static.cargurus.com/images/forsale/2026/07/18/07/13/2026_ford_f-150-pic-8821004417720935512-1024x768.jpeg"
  ],
  "primaryImage": "https://static.cargurus.com/images/forsale/2026/07/18/07/13/2026_ford_f-150-pic-6182805071162814707-1024x768.jpeg",
  "source": "carscrape",
  "vin": "1FTFW3L57TKD09376"
}

Response fields

Vehicle Photos response fields
FieldTypeDescription
galleryCompletebooleanTrue only when galleryCount accounts for every photo photoCount reports. False means the source withheld part of the gallery, or the listing predates gallery capture.
galleryCountintegerNumber of URLs in photos. This is what we can actually serve, as opposed to what the source claims exists.
listingSourcestring | nullScrape source of the listing the photo came from, for example "autolist".
listingUrlstring | nullVehicle detail page URL on the source marketplace.
photoCountinteger | nullNumber of photos the source listing advertises. Null when the source exposed no count — this is NOT the number of URLs we return.
photosstring[]The gallery in the source's own order; photos[0] is the hero image. Empty when no gallery was captured, which is not the same as the vehicle having no photos.
primaryImagestringAbsolute URL of the hero image. Never null on a 200.
source"carscrape"Constant provenance marker for the backing data service.
vinstringThe upper-cased VIN.

Errors

Vehicle Photos endpoint-specific errors
StatuscodeRetryableWhen it happens
400invalid_vinNoThe backing validator rejected the VIN. North American VINs must pass their check digit.
404photos_not_foundNoNo listing with a primary image exists for that VIN.
503photos_upstream_unavailableYesThe photos service was unreachable or the 5 s budget elapsed.
503photos_upstream_errorYesAn unexpected upstream status other than 404 or 422. This route returns no 500 — every unexpected status maps to 503.

In addition to the shared error codes, which every endpoint can return.

Notes & coverage

  • Check galleryComplete before treating photos as the whole set. photoCount is the count the SOURCE advertised; galleryCount is how many URLs we can actually serve. They differ when the source publishes only a hero image, or when the listing was last crawled before gallery capture shipped.
  • An empty photos array with a non-zero photoCount means we know photos exist but do not hold their URLs. It does not mean the vehicle has no photos — follow listingUrl for the rest.
  • Gallery coverage grows as listings are re-crawled; older listings keep their hero image until they are seen again.
  • A VIN that appears in Search Vehicle Listings can still 404 here if its listing row has no primary image.
  • Image URLs point at third-party CDNs and can rot when the listing is taken down. Do not treat them as permanent — mirror anything you need to keep.

Vehicle Photos: overview, worked examples and pricing →

MCP server

Connect Vehicles.dev to any MCP-capable AI agent — Claude Desktop, Claude Code, Cursor, or one you write yourself — so it can look up vehicle data by being asked, instead of by being programmed.

The server is a local process that speaks the Model Context Protocol over stdio. It exposes the 11 synchronous endpoints plus the durable report workflow as 15 tools, each carrying a description precise enough for a model to pick the right one and fill its arguments unaided. Your key stays in the server’s own environment: the agent never sees it, and every tool call is a direct request from your machine to https://api.vehicles.dev with nothing proxied in between.

Tool calls are metered exactly like direct callsThe eleven synchronous lookup tools use the same per-endpoint prices and successOnly rule as direct API calls. Report list, status, and result reads do not create another report charge. Ordering is a separate Pro or Scale action: the MCP tool requires an explicit confirmation and caller-supplied UUID, then billing settles only after canonical JSON completes. An agent answering one question may make several lookups, so usage is visible in the dashboard.

Setup

The package is vehicles-dev-mcp, published on npm under the MIT licence. The configurations below run it with npx, so there is nothing to install or build first — your MCP client fetches it on launch. Node.js 20 or newer is required.

shell · optional
npm install -g vehicles-dev-mcp

Installing globally puts a vehicles-dev-mcp binary on your PATH, which you can use in place of npx in any configuration below. It is only worth doing if you would rather pin the version yourself than let your client fetch the latest on launch.

Environment

MCP server environment variables
VariableTypeRequiredDescription
VEHICLES_API_KEYstringOptionalYour Vehicles.dev API key, sent as Authorization: Bearer <key> on every tool call. Optional if you have run vehicles login — the server then reads the key from ~/.vehicles/credentials.json; set it explicitly for servers and CI. With neither, the server prints how to fix it on stderr and exits non-zero rather than failing every call with a 401. The key never reaches stdout, and it is redacted out of any text a tool returns.
VEHICLES_API_BASE_URLstringabsolute http or https URLdefault https://api.vehicles.devOptionalOrigin the /v1/vehicles/ paths are resolved against. Only useful for pointing the server at a staging deployment.
VEHICLES_API_TIMEOUT_MSinteger1–600000default each tool's own budget: 10, 15 or 20 sOptionalReplaces every per-tool timeout. By default each tool allows its endpoint's upstream budget plus 5 s of headroom, so a slow call still returns the API's problem document instead of a bare client-side timeout.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS (on Windows, %APPDATA%\Claude\claude_desktop_config.json), then restart the app — it reads this file only at launch.

~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "vehicles-dev": {
      "command": "npx",
      "args": ["-y", "vehicles-dev-mcp"]
    }
  }
}

If the tools do not appear, the reason is on stderr in ~/Library/Logs/Claude/mcp-server-vehicles-dev.log. A missing or blank VEHICLES_API_KEY is the usual cause, and the server names the fix there rather than starting in a broken state.

Claude Code

shell
claude mcp add --transport stdio vehicles-dev \
  -- npx -y vehicles-dev-mcp

Add --scope project to write a committable .mcp.json instead of your personal configuration. Verify with claude mcp list, or /mcp inside a session. Note the argument ordering: never put the server name directly after --env — keep another flag (here --transport) between them.

Cursor, VS Code and everything else

Any client that launches a stdio MCP server works. Cursor reads .cursor/mcp.json using the same object shape as Claude Desktop above; VS Code 1.99+ in Agent mode reads .vscode/mcp.json, which nests servers under a servers key and wants an explicit "type": "stdio" alongside command, args and env. The command, arguments and environment are identical in all three.

Tools

14 read-only GET tools are annotated readOnlyHint. The remaining report-order tool is idempotent and makes no API call unless confirm_order=true; callers must supply and reuse a stable UUID. Successful results preserve the API JSON as both text and structured content. A non-2xx becomes an isError result carrying structured status, machine code, request_id, retry timing, and a recovery hint so an agent can repair or resume the workflow safely.

Vehicles.dev MCP tools
ToolEndpointWhat it answers
decode_vinVIN DecodeGET /v1/vehicles/vin/{vin}What car is this VIN? Cheapest identity lookup; start here.
get_specificationsVehicle SpecificationsGET /v1/vehicles/specifications/{vin}What did the factory build? Raw NHTSA vPIC spec sheet — seats, horsepower, GVWR, plant country.
get_recallsRecalls & SafetyGET /v1/vehicles/recalls/{vin}Has this vehicle been recalled? Every NHTSA campaign for its year/make/model.
get_vehicle_photosVehicle PhotosGET /v1/vehicles/photos/{vin}What does it look like? The captured gallery plus a link to the source listing.
search_listingsSearch Vehicle ListingsGET /v1/vehicles/listingsWhat is for sale, and what are they asking? A page of live dealer listings plus the total match count.
get_market_valueMarket ValueGET /v1/vehicles/market-valueWhat is this car worth today? ML-predicted asking price for a year/make/model.
get_depreciationDepreciationGET /v1/vehicles/depreciationHow fast does this model lose value? Model-level retention curve and decay rate.
get_ownership_costsOwnership CostsGET /v1/vehicles/ownership-costsWhat does it cost to run? EPA annual and five-year fuel cost, combined MPG, CO2. Fuel only.
get_total_cost_ownershipTotal Cost of OwnershipGET /v1/vehicles/total-cost-ownershipWhat will it really cost to own? Depreciation, fuel, upkeep, tax and finance — each labelled by how much of it we actually measured.
get_loan_paymentsLoan & PaymentsGET /v1/vehicles/loanWhat is the monthly payment? Amortized payment and total interest, including negative trade-in equity.
get_purchase_costsPurchase Tax & FeesGET /v1/vehicles/purchase-costsWhat tax and fees on top? Per-state rate and trade-in credit rule, plus title, registration and doc fees.
list_vehicle_history_reportsList vehicle history reportsGET /v1/vehicles/history-reportsWhich durable reports does this account already own? Recover recent IDs, VINs, states, and polling cadence without ordering anything.
get_vehicle_history_report_statusGet vehicle history report statusGET /v1/vehicles/history-reports/{id}Is this account-owned report ready? Poll its durable state without submitting or billing another report.
get_vehicle_history_report_resultGet completed vehicle history reportGET /v1/vehicles/history-reports/{id}/resultWhat canonical history data and VIN-bound provenance did this completed report return?
order_or_resume_vehicle_history_reportOrder or resume vehicle history reportPOST /v1/vehicles/history-reportsOrder a confirmed Pro or Scale report, or resume the same logical order with its stable idempotency key.

A worked example

Ask your agent, in whatever words you like:

You
What's a 2022 F-150 with 20k miles worth?
  • The agent matches the question against the 15 tool descriptions and selects get_market_value on its own. You never name the tool, and nothing about the wording has to be exact.
  • It fills the tool's schema from your sentence. The schema states that model is matched case-sensitively against our canonical naming, so it sends "F-150" rather than "f150", and it converts "20k miles" to the integer 20000.
  • The server issues one GET to https://api.vehicles.dev/v1/vehicles/market-value with your key on the Authorization header, and returns the response body to the agent verbatim.
  • The agent reads estimateUsd for the number and the inputs echo to confirm which of its arguments the model actually scored, then answers in prose — normally flagging that this is an asking price from live dealer listings rather than a sale price, because the tool description says so.
  • Add a state and it improves: "…worth in Texas?" makes the agent send state: "TX", uppercase, as the schema requires. Ask "and what is actually listed near me?" and it follows up with search_listings — a second tool call, and a second billable request.
The tool call the agent emits
{
  "name": "get_market_value",
  "arguments": {
    "make": "Ford",
    "model": "F-150",
    "year": 2022,
    "miles": 20000
  }
}

Remote MCP via the Claude API

The Claude API can attach a remote MCP server to a message — Anthropic makes the connection server-side, so no local process is involved. That requires an MCP endpoint reachable over HTTPS. The Vehicles.dev deployment is configured for https://mcp.vehicles.dev/mcp; the Render service, DNS and TLS must be live before clients can use it.

Your key, your accountSet authorization_token to your vdev_ API key. Anthropic forwards it as a Bearer credential on each MCP request; the hosted service forwards that same key to the Vehicles.dev API. It does not use a shared account or retain your key between requests, so your own plan, rate limits, credits and successful-call metering still apply.

The request has two halves that must agree. mcp_servers declares the connection; a matching mcp_toolset entry in tools grants access to it. Both are required — omitting the toolset is a validation error, not a silent no-op — and the beta header mcp-client-2025-11-20 gates the whole feature.

shell
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: mcp-client-2025-11-20" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 1024,
    "mcp_servers": [
      {
        "type": "url",
        "name": "vehicles-dev",
        "url": "https://mcp.vehicles.dev/mcp",
        "authorization_token": "vdev_your_key_here"
      }
    ],
    "tools": [
      { "type": "mcp_toolset", "mcp_server_name": "vehicles-dev" }
    ],
    "messages": [
      { "role": "user", "content": "What is a 2022 Ford F-150 with 20,000 miles worth?" }
    ]
  }'

mcp_server_name must match the name on the server entry exactly; that string is how the toolset binds to the connection. Keep the credential in authorization_token, never in the prompt. If you prefer to keep the key and MCP connection entirely on your own machine, the stdio setup above remains available.

Conventions

  • Money. Data endpoints return whole US dollars — estimateUsd, annualFuelCostUsd, listing price. Billing and pricing figures are quoted in USD micros elsewhere in the platform, where 1,000,000 micros is $1. Do not mix the two.
  • Naming. The top-level envelope is camelCase. Pass-through objects and arrays — vehicle, specifications, inputs, results, observations, recalls, identity, byModelYear elements — keep their upstream snake_case keys. That asymmetry is deliberate and stable.
  • Nulls. null is a first-class “unknown”. Some objects omit null keys entirely rather than emitting them, so treat every nested field as optional.
  • Dates. ISO-8601 strings throughout. The one exception is recall report_date, which NHTSA supplies in inconsistent formats and we pass through verbatim.
  • Pagination. Only Search Vehicle Listings paginates, via limit (1–500, default 50) and offset against the total match count. No cursors, no Link headers. Every other endpoint is a single-object lookup.
  • Query handling. Query strings are coerced to the declared integer and boolean types, and unknown parameters are rejected rather than dropped. VINs in the path are upper-cased server-side.
  • Timeouts. Each endpoint has an upstream budget of 5, 10 or 15 seconds, listed on the endpoint. Set your client timeout above the endpoint’s budget so you receive the API’s 503 rather than timing out blind.
  • Idempotency. Every data endpoint is a GET and safe to repeat. The metering layer is internally idempotent on the request id, so a retry never double-charges a single request.

Versioning, availability & freshness

What we will change, what it costs when we change prices, what we do not promise about uptime, and how current the data actually is. Read this before you put the API on a critical path.

Versioning & changes

There is one API version. The /v1/ path prefix is the contract — there is no version header, no date-pinned version, and no per-account pinning.

  • Additive changes ship without notice. New endpoints, new fields on an existing response, new optional query parameters and new code values on an existing status can appear at any time. Parse defensively: ignore unknown JSON fields, and fall back to the HTTP status when you meet a code you do not recognize.
  • Breaking changes are removing an endpoint, removing or renaming a response field, adding a required parameter, tightening an existing parameter’s constraints, changing a field’s type, or changing what an existing code means. Note that a nested value going null — or a key disappearing from an object that omits nulls — is not a breaking change; every nested field is documented as optional for that reason.
  • There is no changelog page and no contractual deprecation window on self-serve plans today. If your integration needs advance written notice before a breaking change, email support@vehicles.dev and we will agree a notice period in writing rather than leaving you to discover it in production.

When prices change

The 2026-08-18 shown as Price catalog in the header is the version of the pricing catalog this page was generated from. Prices are not quoted live at call time: your subscription pins a catalog version, every metered event records the unit price that was actually applied, and a new catalog is adopted at the start of your next UTC monthly period. A price change therefore never reprices calls you have already made, and it never takes effect mid-period.

Availability

We publish no uptime target, status page or incident channel today, and self-serve plans carry no SLA. What you get instead is explicit failure: every dependency outage surfaces as a documented 503 with retryable: true rather than as a hang or a silently degraded body, upstream calls are bounded by the per-endpoint timeout listed above, and the x-request-id on every response is how we trace an incident. Report an outage to support@vehicles.dev with a request id and a UTC timestamp. If you need a contractual uptime commitment, ask before you build.

Data freshness

  • Live upstream, uncached. Vehicle Specifications, Recalls & Safety and Ownership Costs call NHTSA vPIC, the NHTSA recalls API and EPA fueleconomy.gov on every request, as does the "vpic" fallback inside VIN Decode. They are exactly as current — and as available — as those agencies are.
  • Store-backed, crawl-paced. Listings, Photos and the store-origin path of VIN Decode come from our own continuously running crawl. We do not publish a maximum staleness, and no listings field carries a crawl timestamp: is_active tells you the row was still live at the last crawl pass that covered it, not when that pass ran, and days_on_market counts from our first observation rather than the dealer’s listing date.
  • Precomputed. Depreciation is a periodically rebuilt table and Market Value is a periodically retrained model; both are refreshed on our schedule rather than per request, so two identical calls minutes apart will agree.
  • If your use case needs a hard freshness bound or a per-row crawl timestamp, say so before you build on these endpoints — neither is available through the API today.

Support

Email support@vehicles.dev. To get a fast, specific answer, include:

  • the x-request-id (or request_id) of a failing call — this is the single most useful thing you can send;
  • the approximate UTC timestamp of the request;
  • the endpoint and the parameters you sent, with the API key redacted to its last four characters.

Never send us a full API key. If one has leaked, revoke it in the dashboard and mint a replacement — revocation is immediate.

Usage terms

  • Standard plans cover use inside your own product or internal systems. Bulk redistribution or resale of raw records needs a separate written agreement.
  • Photo URLs point at CDNs owned by the source marketplace. We link to them; we do not license the imagery, and it is not ours to sublicense.
  • Specifications, recalls and ownership costs are derived from public NHTSA and EPA data and are passed through with their own accuracy limits, which are described on each endpoint.
  • This page is the current statement of usage rights — there is no separate terms-of-service, acceptable-use or DPA page yet. For a redistribution licence, a data-processing agreement, or vendor-security paperwork, email support@vehicles.dev and we will put it in writing.

Machine-readable spec

The OpenAPI 3.1 document is generated from the same schemas that validate every request and is served unauthenticated at https://api.vehicles.dev/openapi.json. It declares https://api.vehicles.dev as its server, so you can import it into Postman or Insomnia, or point a client generator at it, without hand-editing a base URL.

shell
curl -sS https://api.vehicles.dev/openapi.json -o vehicles-openapi.json
It describes the whole platform, not only VehiclesOne document covers every route the API process exposes: the 11 synchronous /v1/vehicles/* operations documented here, the asynchronous /v1/vehicles/history-reports workflow, plus the /v1/control/* dashboard control plane, the /v1/ops/* operator routes, the payment webhook, the health probes, and the other product’s data endpoints. Only /v1/vehicles/* is callable with a vdev_ key — the control-plane and operator routes require a dashboard session token and reject an API key with 401 invalid_credential. Generate clients from the /v1/vehicles/ paths and drop the rest.

Get an API keyCompare plans