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 a complaint_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.

Search vehicles

Find vehicles by year, make, and/or model. Results are paginated.

Query paramTypeDescription
year numberFilter by model year.
make stringFilter by make (case-insensitive).
model stringFilter by model (case-insensitive).
limit numberResults per page. Default 25, max 100.
offset numberPagination offset. Default 0.
Request
$ curl "https://api.carvector.io/v1/vehicles?make=Toyota&model=Tacoma&limit=2" \
  -H "Authorization: Bearer cv_your_key"
200 OK
{
  "count": 2,
  "limit": 2,
  "offset": 0,
  "results": [
    { "id": "1laqdklflb3hfav", "year": 2018, "make": "Toyota", "model": "Tacoma Access Cab", "...": "..." }
  ]
}
GET /v1/vehicles/{id}

Vehicle detail

Full specs for a single vehicle by its id.

Path paramTypeDescription
id requiredstringOpaque vehicle id returned by the search endpoint, e.g. 1laqdklflb3hfav.
Request
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav \
  -H "Authorization: Bearer cv_your_key"
200 OK
{
  "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.
GET /v1/vehicles/{id}/recalls

Recalls for a vehicle

Federal recall campaigns mapped to a vehicle's year/make/model, newest first.

Path paramTypeDescription
id requiredstringVehicle id (same format as vehicle detail).
Request
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/recalls \
  -H "Authorization: Bearer cv_your_key"
200 OK
{
  "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.
GET /v1/vehicles/{id}/complaints

Complaints for a vehicle Pro plan and up

Aggregate owner-complaint signal plus the most recent individual complaints, newest first.

ParamTypeDescription
id requiredstringPath — vehicle id (same format as vehicle detail).
limit numberQuery — recent complaints to return. 1–10, default 10.
since stringQuery — only complaints on/after this ISO date (YYYY-MM-DD).
Request
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/complaints \
  -H "Authorization: Bearer cv_your_key"
200 OK
{
  "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

The aggregate 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.
GET /v1/vehicles/{id}/tsbs

Technical service bulletins Business plan and up

The manufacturer service-bulletin index for a vehicle — bulletin metadata, not the documents — newest first.

ParamTypeDescription
id requiredstringPath — vehicle id.
limit numberQuery — bulletins to return. 1–50, default 50.
since stringQuery — only bulletins on/after this ISO date (YYYY-MM-DD).
Request
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/tsbs \
  -H "Authorization: Bearer cv_your_key"
200 OK
{
  "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 empty communication_date ("") at the source. summary is clipped at ~150 chars. Returns 404 for an unknown vehicle id.
GET /v1/vehicles/{id}/investigations

Investigations Business plan and up

NHTSA defect investigations mapped to a vehicle — often a leading indicator ahead of a recall — newest first.

ParamTypeDescription
id requiredstringPath — vehicle id.
limit numberQuery — investigations to return. 1–100, default 100.
since stringQuery — only investigations opened on/after this ISO date (YYYY-MM-DD).
Request
$ curl https://api.carvector.io/v1/vehicles/1laqdklflb3hfav/investigations \
  -H "Authorization: Bearer cv_your_key"
200 OK
{
  "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.
GET /v1/dtc/{code}

DTC reference

Look up a diagnostic trouble code's reference data by its identifier.

Path paramTypeDescription
code requiredstringOBD-II code, e.g. P0420 (case-insensitive).
Request
$ curl https://api.carvector.io/v1/dtc/P0420 \
  -H "Authorization: Bearer cv_your_key"
200 OK
{
  "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.
GET /v1/status

Status no auth

Service health and data freshness. Useful for an uptime check; requires no key.

Request
$ curl https://api.carvector.io/v1/status
200 OK
{
  "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.

ToolTypeDescription
search_vehicles GET/v1/vehicles
get_vehicle GET/v1/vehicles/{id}
get_recalls GET/v1/vehicles/{id}/recalls
lookup_dtc GET/v1/dtc/{code}
OpenAPI
$ curl https://api.carvector.io/v1/openapi.json

Vercel AI SDK 5+ (TypeScript)

carvector-tools.ts
// 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.

tools
[
  {
    "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)

carvector_tools.py
# 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.