Docs · Endpoints
API reference
Endpoints
All requests are GET over HTTPS and return JSON. Base URL https://api.carvector.io/v1. Every endpoint except /v1/status requires a bearer key.
Forward compatibility — tolerate unknown fields
We add new optional fields to existing responses without notice — a vehicle may gain acomplaint_count, a recall a completion_rate. Parse leniently and ignore keys you don't recognise. Existing v1 fields are never removed, renamed, or retyped — those would be a /v2./v1/vehiclesSearch vehicles
Find vehicles by year, make, and/or model. Results are paginated.
| Query param | Type | Description |
|---|---|---|
year | number | Filter by model year. |
make | string | Filter by make (case-insensitive). |
model | string | Filter by model (case-insensitive). |
limit | number | Results per page. Default 25, max 100. |
offset | number | Pagination offset. Default 0. |
$ curl "https://api.carvector.io/v1/vehicles?make=Toyota&model=Tacoma&limit=2" \ -H "Authorization: Bearer cv_your_key"
{ "count": 2, "limit": 2, "offset": 0, "results": [ { "id": "1laqdklflb3hfav", "year": 2018, "make": "Toyota", "model": "Tacoma Access Cab", "...": "..." } ] }
/v1/vehicles/{id}Vehicle detail
Full specs for a single vehicle by its id.
| Path param | Type | Description |
|---|---|---|
id required | string | Opaque vehicle id returned by the search endpoint, e.g. 1laqdklflb3hfav. |
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav \ -H "Authorization: Bearer cv_your_key"
{ "id": "1laqdklflb3hfav", "year": 2018, "make": "Toyota", "model": "Tacoma Access Cab", "trim": null, "submodel": "Pickup", "horsepower": 278, "cylinders": 6, "displacement_l": 3.5, "transmission": "Automatic 6-spd", "drive_type": "4x2", "fuel_type": "Gasoline", "body_class": "Small Pickup Trucks 4WD", "doors": 4, "image_url": "https://vehicle.s3.us-east-005.backblazeb2.com/vehicles/2018/Toyota/Tacoma%20Access%20Cab/2018_Toyota_Tacoma_Access_Cab_7f4cea94_00001_.png", "image_type": "illustration", "recall_count": 0, "complaint_count": 12, "tsb_count": 8, "investigation_count": 1 }
Images are illustrations
image_url points to a representative illustration of the year/make/model — image_type is always "illustration" (or null when none exists), never a photo of a specific unit./v1/vehicles/{id}/recallsRecalls for a vehicle
Federal recall campaigns mapped to a vehicle's year/make/model, newest first.
| Path param | Type | Description |
|---|---|---|
id required | string | Vehicle id (same format as vehicle detail). |
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/recalls \ -H "Authorization: Bearer cv_your_key"
{ "vehicle_id": "1laqdklflb3hfav", "count": 1, "recalls": [ { "campaign_id": "19V123000", "component": "STRUCTURE:FRAME AND MEMBERS", "summary": "Description of the defect…", "consequence": "Effect of the defect on safety…", "remedy": "How dealers will fix it…", "report_received": "14/03/2019", "potentially_affected": 12000, "completion_rate": null } ] }
Date format + completion_rate
report_received on recalls is DD/MM/YYYY — a frozen legacy format; every other endpoint and /v1/status use ISO YYYY-MM-DD. completion_rate is optional — the recall-completion rate published quarterly by NHTSA, or null when not yet published./v1/vehicles/{id}/complaintsComplaints for a vehicle Pro plan and up
Aggregate owner-complaint signal plus the most recent individual complaints, newest first.
| Param | Type | Description |
|---|---|---|
id required | string | Path — vehicle id (same format as vehicle detail). |
limit | number | Query — recent complaints to return. 1–10, default 10. |
since | string | Query — only complaints on/after this ISO date (YYYY-MM-DD). |
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/complaints \ -H "Authorization: Bearer cv_your_key"
{ "vehicle_id": "1laqdklflb3hfav", "aggregate": { "total": 7018, "by_component": { "STEERING": 1639, "SERVICE BRAKES": 820 }, "by_year": { "2018": 412, "2019": 503 }, "crash_count": 174, "fire_count": 16, "injured_count": 109, "deaths_count": 3, "trend_90d_count": 12 }, "count": 10, "results": [ { "received_date": "2017-12-27", "fail_date": "2017-12-22", "component": "SUSPENSION", "complaint_type": "EVOQ", "crash": false, "fire": false, "injured": 0, "deaths": 0, "summary": "Owner-reported suspension failure…", "summary_truncated": true } ], "limit": 10, "since": null }
Aggregate first; narratives clipped
Theaggregate object is the headline failure signal; results holds the most-recent complaints. The narrative summary is clipped at build (~500 chars) — summary_truncated: true flags it. Empty or unknown fields are returned as ""/0, never omitted. Returns 404 for an unknown vehicle id./v1/vehicles/{id}/tsbsTechnical service bulletins Business plan and up
The manufacturer service-bulletin index for a vehicle — bulletin metadata, not the documents — newest first.
| Param | Type | Description |
|---|---|---|
id required | string | Path — vehicle id. |
limit | number | Query — bulletins to return. 1–50, default 50. |
since | string | Query — only bulletins on/after this ISO date (YYYY-MM-DD). |
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/tsbs \ -H "Authorization: Bearer cv_your_key"
{ "vehicle_id": "1laqdklflb3hfav", "count": 50, "results": [ { "tsb_id": "TUCC_MAY2025_28", "communication_date": "2025-05-09", "communication_type": "Other", "component": "UNKNOWN OR OTHER", "component_system": "", "component_subsystem": "", "summary": "Service-bulletin summary…", "summary_truncated": false } ], "limit": 50, "since": null }
Index only
This is bulletin metadata (id, date, type, components, clipped summary) — not the bulletin PDF. Some records have an emptycommunication_date ("") at the source. summary is clipped at ~150 chars. Returns 404 for an unknown vehicle id./v1/vehicles/{id}/investigationsInvestigations Business plan and up
NHTSA defect investigations mapped to a vehicle — often a leading indicator ahead of a recall — newest first.
| Param | Type | Description |
|---|---|---|
id required | string | Path — vehicle id. |
limit | number | Query — investigations to return. 1–100, default 100. |
since | string | Query — only investigations opened on/after this ISO date (YYYY-MM-DD). |
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/investigations \ -H "Authorization: Bearer cv_your_key"
{ "vehicle_id": "1laqdklflb3hfav", "count": 12, "results": [ { "action_number": "AQ10001", "component": "STRUCTURE", "subject": "Fleet Vehicle Recall Completion Audit", "open_date": "2010-11-18", "close_date": "2015-12-28", "campaign_id": "", "summary": "Investigation summary…", "summary_truncated": true } ], "limit": 100, "since": null }
Open investigations
close_date and campaign_id are "" while an investigation is open or not yet linked to a recall. summary is clipped at ~500 chars. Returns 404 for an unknown vehicle id./v1/dtc/{code}DTC reference
Look up a diagnostic trouble code's reference data by its identifier.
| Path param | Type | Description |
|---|---|---|
code required | string | OBD-II code, e.g. P0420 (case-insensitive). |
$ curl https://api.carvector.io/v1/dtc/P0420 \ -H "Authorization: Bearer cv_your_key"
{ "code": "P0420", "title": "P0420 Catalyst System Efficiency Below Threshold (Bank 1)", "category": "Powertrain", "system": "P", "severity": "Medium", "safety_risk": false, "emissions_related": true, "related_codes": [], "symptoms": [] }
Reference, not repair economics
v1 returns the code's identity and flags — title, category, severity, and safety/emissions flags. It does not return parts cost or labor estimates./v1/statusStatus no auth
Service health and data freshness. Useful for an uptime check; requires no key.
$ curl https://api.carvector.io/v1/status
{ "status": "ok", "data_updated": "2026-05-27T04:00:00Z", "schema_version": 2 }
Use with your agent framework
The whole API is described in an OpenAPI 3.1 document at https://api.carvector.io/v1/openapi.json (no key needed). Import it into any tool that reads OpenAPI, or paste one of the blocks below. They define the same four tools as the MCP server, each a single GET.
| Tool | Type | Description |
|---|---|---|
search_vehicles | GET | /v1/vehicles |
get_vehicle | GET | /v1/vehicles/{id} |
get_recalls | GET | /v1/vehicles/{id}/recalls |
lookup_dtc | GET | /v1/dtc/{code} |
$ curl https://api.carvector.io/v1/openapi.json
Vercel AI SDK 5+ (TypeScript)
// npm i ai zod · CARVECTOR_API_KEY=cv_…
import { tool } from 'ai';
import { z } from 'zod';
const BASE = 'https://api.carvector.io/v1';
async function cv(path: string, query: Record<string, string | number | undefined> = {}) {
const url = new URL(BASE + path);
for (const [k, v] of Object.entries(query)) if (v !== undefined) url.searchParams.set(k, String(v));
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.CARVECTOR_API_KEY}` } });
return res.json(); // errors are JSON too ({ "error": "not_found" }) — let the model read them
}
export const carvectorTools = {
search_vehicles: tool({
description: "Search the CarVector catalog by year, make and/or model. Returns matching vehicles with their id and specs. Call this first to get a vehicle id, then call get_vehicle or get_recalls with it.",
inputSchema: z.object({
year: z.number().int().optional().describe("Model year, e.g. 2020."),
make: z.string().optional().describe("Make, e.g. Jeep (case-insensitive)."),
model: z.string().optional().describe("Model, e.g. Wrangler (case-insensitive prefix match)."),
limit: z.number().int().optional().describe("Max results (default 25; the free plan caps at 10)."),
}),
execute: async ({ year, make, model, limit }) => cv('/vehicles', { year, make, model, limit }),
}),
get_vehicle: tool({
description: "Get full specifications for one vehicle by its id (from search_vehicles): engine, drivetrain, body, a representative illustration and recall count.",
inputSchema: z.object({ id: z.string().describe("Vehicle id from search_vehicles, e.g. 1laqdklflb3hfav.") }),
execute: async ({ id }) => cv(`/vehicles/${encodeURIComponent(id)}`),
}),
get_recalls: tool({
description: "Get federal recall campaigns for a vehicle by its id: component, summary, consequence and remedy for each.",
inputSchema: z.object({ id: z.string().describe("Vehicle id from search_vehicles.") }),
execute: async ({ id }) => cv(`/vehicles/${encodeURIComponent(id)}/recalls`),
}),
lookup_dtc: tool({
description: "Look up an OBD-II diagnostic trouble code (e.g. P0420): title, category, severity and safety/emissions flags. Reference only, no repair cost estimates.",
inputSchema: z.object({ code: z.string().describe("OBD-II code, e.g. P0420.") }),
execute: async ({ code }) => cv(`/dtc/${encodeURIComponent(code)}`),
}),
};
// const { text } = await generateText({ model, tools: carvectorTools, stopWhen: stepCountIs(5), prompt: 'Any recalls on a 2020 Jeep Wrangler?' });
OpenAI function calling
Pass this as tools. When the model calls one, make the matching GET from the table above with your key and return the JSON as the tool result.
[ { "type": "function", "function": { "name": "search_vehicles", "description": "Search the CarVector catalog by year, make and/or model. Returns matching vehicles with their id and specs. Call this first to get a vehicle id, then call get_vehicle or get_recalls with it.", "parameters": { "type": "object", "properties": { "year": { "type": "integer", "description": "Model year, e.g. 2020." }, "make": { "type": "string", "description": "Make, e.g. Jeep (case-insensitive)." }, "model": { "type": "string", "description": "Model, e.g. Wrangler (case-insensitive prefix match)." }, "limit": { "type": "integer", "description": "Max results (default 25; the free plan caps at 10)." } }, "required": [], "additionalProperties": false } } }, { "type": "function", "function": { "name": "get_vehicle", "description": "Get full specifications for one vehicle by its id (from search_vehicles): engine, drivetrain, body, a representative illustration and recall count.", "parameters": { "type": "object", "properties": { "id": { "type": "string", "description": "Vehicle id from search_vehicles, e.g. 1laqdklflb3hfav." } }, "required": [ "id" ], "additionalProperties": false } } }, { "type": "function", "function": { "name": "get_recalls", "description": "Get federal recall campaigns for a vehicle by its id: component, summary, consequence and remedy for each.", "parameters": { "type": "object", "properties": { "id": { "type": "string", "description": "Vehicle id from search_vehicles." } }, "required": [ "id" ], "additionalProperties": false } } }, { "type": "function", "function": { "name": "lookup_dtc", "description": "Look up an OBD-II diagnostic trouble code (e.g. P0420): title, category, severity and safety/emissions flags. Reference only, no repair cost estimates.", "parameters": { "type": "object", "properties": { "code": { "type": "string", "description": "OBD-II code, e.g. P0420." } }, "required": [ "code" ], "additionalProperties": false } } } ]
LangChain (Python)
# pip install langchain-core requests · CARVECTOR_API_KEY=cv_…
import os
from typing import Optional
from urllib.parse import quote
import requests
from langchain_core.tools import tool
BASE = "https://api.carvector.io/v1"
def _cv(path: str, **params):
r = requests.get(
BASE + path,
params={k: v for k, v in params.items() if v is not None},
headers={"Authorization": f"Bearer {os.environ['CARVECTOR_API_KEY']}"},
timeout=15,
)
return r.json() # errors are JSON too ({"error": "not_found"}) — let the model read them
@tool
def search_vehicles(year: Optional[int] = None, make: Optional[str] = None,
model: Optional[str] = None, limit: Optional[int] = None) -> dict:
"Search the CarVector catalog by year, make and/or model. Returns matching vehicles with their id and specs. Call this first to get a vehicle id, then call get_vehicle or get_recalls with it."
return _cv("/vehicles", year=year, make=make, model=model, limit=limit)
@tool
def get_vehicle(id: str) -> dict:
"Get full specifications for one vehicle by its id (from search_vehicles): engine, drivetrain, body, a representative illustration and recall count."
return _cv(f"/vehicles/{quote(id, safe='')}")
@tool
def get_recalls(id: str) -> dict:
"Get federal recall campaigns for a vehicle by its id: component, summary, consequence and remedy for each."
return _cv(f"/vehicles/{quote(id, safe='')}/recalls")
@tool
def lookup_dtc(code: str) -> dict:
"Look up an OBD-II diagnostic trouble code (e.g. P0420): title, category, severity and safety/emissions flags. Reference only, no repair cost estimates."
return _cv(f"/dtc/{quote(code, safe='')}")
carvector_tools = [search_vehicles, get_vehicle, get_recalls, lookup_dtc]
# llm.bind_tools(carvector_tools) · or create_react_agent(llm, carvector_tools)
Errors reach the model as JSON
Every snippet returns the response body as-is, including errors such as{ "error": "not_found" } or rate_limit_exceeded, so the agent can read what went wrong. Complaints, service bulletins and investigations need a paid plan and are not in these blocks; the OpenAPI document covers them.