The Used Car Index
Vehicle records · Developer API · MCP

The record behind
the vehicle.

The Used Car Index data feed brings together NHTSA vehicle records, verified sales-denominator rates where available, and observed listing changes in a JSON API and remote MCP server.

Live playground

Start with a real request.

Edit the query, then run it against this origin. The free vehicle lookup uses a shared, rate limited demo quota. Compare and listing examples require your own key. Responses include the HTTP status, elapsed time and current rate-limit window.

01

One vehicle, the full record

GET · demo/vehicle_risk

Ready to run.

{ "status": "ready" }
02

Compare recorded facts

GET · compare_vehicles

Ready to run.

{ "status": "ready" }
03

Follow listing changes

GET · listing_changes

Ready to run.

{ "status": "ready" }
HTTP interface

Small surface. Explicit fields.

Authentication

Send Authorization: Bearer <key> or X-API-Key: <key>. Keep private keys on your server. The free lookup keeps its demo credential on the server. Real use needs your own key; the playground never stores it.

Authorization: Bearer <key>
Accept: application/json

Public discovery routes: /, /healthz, /openapi.json, /llms.txt /.well-known/mcp/server-card.json and /v1/demo/vehicle_risk.

Response conventions

All data endpoints return JSON. Every response includes X-Request-Id. Authenticated responses are not cached. A missing, unprocessed or unverified value stays null or has an explicit reason; it is never an inferred zero.

OpenAPI 3.1 specification · Agent reference

GET /v1/demo/vehicle_risk
Rate limited free lookup for trying the API, with make, model and year. No credential needed. Visitors share the demo quota. Real use needs a key.
GET /v1/vehicle_risk
Required: make and model (trimmed, 1–64 characters), year (2000–2027). Model spellings resolve through stored model mappings, then an exact family key. Unknown vehicles return 404 and up to ten database suggestions.
GET or POST /v1/compare_vehicles
GET: vehicles=toyota:rav4:2018,honda:cr-v:2018 (URL-encode the query). POST: {"vehicles":[{"make":"Toyota","model":"RAV4","year":2018},{"make":"Honda","model":"CR-V","year":2018}]}. Supply 2–5 vehicles, bounded by the plan’s max_compare. Each vehicle costs one unit. Unresolved items are marked not_found and remain unrated.
GET /v1/listing_changes
Optional: make, model, year, uppercase 17-character vin, event_type (new, price_drop, relisted, delisted), inclusive since and until (YYYY-MM-DD), limit (1–200; default 50), cursor. Reuse next_cursor with the same filters. Newest date first, then descending event id. No active collectors returns an empty feed on a fresh database.
GET /v1/usage
Your plan and current UTC minute, day and month counters. This endpoint does not charge usage. Unknown and duplicate query parameters on data routes are rejected.

Read the data precisely.

vehicle & complaints
Canonical labels, family key, matched model spellings and how they resolved. Complaint totals include crash/fire/injury flags, injuries, deaths, source overlap and coverage. Top systems include count and share; component categories can overlap.
recalls & investigations
Counts, special recall flags, maximum affected units (not a sum), up to ten newest recall records and all investigation records. Official excerpts preserve truncation flags; missing document links remain null.
manufacturer_communications
Total, component system/subsystem counts with source_field, and up to ten recent communications. Metadata arrays remain arrays.
rate & model_tags
Stored complaints_per_100k_sold with units_sold, sales_year, basis, source and source_url. The rate is null with null_reason if unavailable. It uses a calendar-year sales proxy, not vehicles in use. Jev model_tags cover model years 2012 and newer, about 43 percent of complaints overall. They include model and schema_version, tagged_complaints, complaints_total and tagged_share. Each failure-mode and severity label has count, share of tagged complaints, stored mean_confidence, high_confidence_count (at least 0.8) and low_confidence_count (below 0.5). Confidence is the model's own probability for its chosen label (0 to 1), not accuracy. Labels are separate from NHTSA's own flags in complaints. No tagged complaints returns model_tags null with model_tags_null_reason.
comparison & data
Factual orderings only: fewest_complaints_per_100k contains ordered keys and an unrated list; most_recalls and most_complaints contain resolved keys. Keys use make|model|year. Data includes built_at, sources and scope notes.
events & collectors
Event id, date, type, VIN, make, model, year, old/new price, mileage, link and source only. Prices are the dealer's displayed vehicle price on its own page; whether dealer fees or add-ons are included differs by dealer, and a price drop compares the same listing day to day. next_cursor is opaque. Collector status, active-source count and last_run_at explain coverage.

Limits you can inspect.

These plans are read from this database on every page request. Vehicle and listing calls cost one unit; comparisons cost the number of requested vehicles. Input errors cost nothing. Admitted requests, including not-found results, increment all three UTC counters together. Over-limit requests do not change counters or daily accounting.

Plan/ minute/ day/ monthCompare maxMCP
Free2020020005Yes
Builder6050001000005Yes
Demo6050001000005Yes
Scale3005000010000005Yes

X-RateLimit-Limit, Remaining and Reset describe the window with the lowest fraction remaining (earliest reset breaks ties). Reset is a Unix timestamp in seconds. X-RateLimit-Window names it. Public discovery and unauthenticated responses use 0 / 0 / 0 and window none. The demo lookup reports shared plan headers. A 429 adds Retry-After in seconds until the earliest exceeded window resets; another exceeded window may still block the next call.

Errors are structured, too.

HTTP errors use RFC 9457 application/problem+json with a stable code. Input problems list field names without echoing values. MCP uses a JSON-RPC error envelope; tool failures carry the problem in error.data. Binding denials use problem+json and Retry-After.

{
  "type": "urn:carindex:problem:vehicle_not_found",
  "title": "vehicle not found",
  "status": 404,
  "detail": "No vehicle matches the supplied make, model and year.",
  "code": "vehicle_not_found",
  "suggestions": []
}

400 invalid input · 401 missing/invalid/revoked key · 403 MCP unavailable on plan · 404 unknown route or vehicle · 405 method not allowed · 413 body too large · 415 wrong media type · 429 cap exceeded · 500 internal error · 503 service or data unavailable. Public request bodies are capped at 64 KB.

For agent clients

The same data, through MCP.

Endpoint: https://api.theusedcarindex.com/mcp. Stateless Streamable HTTP, POST only, JSON responses. initialize, ping and tools/list are public; tools/call requires a key and consumes the same quota as HTTP. No session id or SSE subscription is required.

Claude Desktop / Cursor

Use this mcp-remote bridge configuration, replacing <key> with your API key:

{
  "mcpServers": {
    "carindex": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://api.theusedcarindex.com/mcp",
        "--header",
        "Authorization: Bearer <key>"
      ]
    }
  }
}

Generic remote client

{
  "url": "https://api.theusedcarindex.com/mcp",
  "transport": "streamable-http",
  "headers": {
    "Authorization": "Bearer <key>",
    "Accept": "application/json, text/event-stream"
  }
}

Send Content-Type: application/json and a JSON-RPC 2.0 body. Discovery: server card.

Read-only tools: vehicle_risk {make, model, year}, compare_vehicles {vehicles}, listing_changes {make?, model?, year?, vin?, event_type?, since?, until?, limit?, cursor?}. Each returns JSON text and matching structuredContent, without a verdict.

Provenance & boundaries

Data and sources

  • NHTSA complaints: API plus the bulk complaints file merged and deduplicated by ODI number because the API alone misses records.
  • NHTSA recalls, investigations and manufacturer communications: official records with component system/subsystem fields; manufacturer communications retain whether the field came from the manufacturer or NHTSA.
  • Sales from goodcarbadcar.net: complaints-per-100k-sold only where verified. Missing or mismatched denominators stay null; the Worker never calculates a rate.
  • Jev model tags: model-generated failure-mode and severity labels on tagged complaints only, model years 2012 and newer, about 43 percent of complaints overall. NHTSA’s own crash, fire and injury flags remain separate.

No photos, no dealer text. Delisted is never sold. No repair advice, no rating or verdict. A listing’s disappearance does not establish a completed transaction. The feed does not claim complete coverage where inputs are partial.