Enterprise API documentation

Run vehicle-history reports in bulk and pull the results straight into your own system as clean JSON. One endpoint in, one report out.

The Enterprise API is available on invitation. Create an account and contact us to get your API key and pricing.

Key-based auth

One Authorization header. No OAuth dance.

Sync when fast

Most reports return in one request. Slow ones give you a poll URL.

Built for bulk

Requests run in parallel — burst hundreds of VINs at a time.

Predictable JSON

A stable, versioned response shape you can map once.

The basics

Base URL: https://carmareports.com/api/v1

Authentication: every request needs your API key in the Authorization header: Bearer crk_live_…. Keys are issued per account and can be rotated on request. Keep them server-side — never ship a key in a browser or mobile app.

Format: JSON in, JSON out (Content-Type: application/json). Every response carries an X-Api-Version header. All monetary values are integer USD cents.

Rate limits: each key has a per-minute request limit sized to your contract. Exceeding it returns 429 with a Retry-After header (seconds). Requests otherwise run fully in parallel — there is no forced spacing between calls.

Billing: every successful report request is billable at your contracted per-call rate, including repeat pulls of the same VIN. Failed lookups, not-found VINs, and rejected requests are never billed. Repeat requests for a VIN you pulled within the last 10 days return instantly from cache (marked "cached": true); after 10 days we fetch fresh data.

Quick start

One call. If the report is ready within ~55 seconds (the common case), you get the full report back with HTTP 200. If it needs longer, you get HTTP 202 and a poll URL.

Request
curl -X POST https://carmareports.com/api/v1/fullreport \
  -H "Authorization: Bearer crk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"vin": "1HGCM82633A004352"}'
Response · 200 OK (abridged)
{
  "report_id": "cml7x2ab40001rt9k",
  "status": "complete",
  "tier": "basic",
  "vin": "1HGCM82633A004352",
  "cached": false,
  "generated_at": "2026-07-29T18:04:11.000Z",
  "report": {
    "vin": "1HGCM82633A004352",
    "generatedAt": "2026-07-29T18:04:11.000Z",
    "vehicle": {
      "year": 2015, "make": "Jeep", "model": "Grand Cherokee",
      "trim": "Limited", "bodyStyle": "SUV", "engine": "3.6L V6",
      "transmission": "Automatic", "fuelType": "Gasoline",
      "exteriorColor": "Granite Crystal", "msrpCents": 4139500
      /* …plus drive type, MPG, doors, seating, origin */
    },
    "mileage": { "lastReported": 87450, "lastReportedDate": "2026-03-14", "estimated": 91200 },
    "scores": { "carmaScore": 82, "condition": "Good", "riskScore": 24, "riskLabel": "Low risk" },
    "summary": { /* accident / title-brand / odometer flags */ },
    "market": { "estimateCents": 1650000, "lowCents": 1480000, "highCents": 1820000 },
    "timeline":         [ /* chronological history events */ ],
    "brandChecks":      [ /* salvage, flood, lemon, junk checks */ ],
    "odometerReadings": [ /* dated readings with source */ ],
    "accidents":        [ /* date, severity, impact area, est. damage */ ],
    "titleBrands":      [ /* brand code, description, state, date */ ],
    "salesRecords":     [ /* historical listings and sale prices */ ],
    "recalls":          [ /* open recall campaigns */ ],
    "findings":         [ /* notable findings with severity */ ]
    /* Intelligent tier adds an "aiEnhanced" block: web findings,
       image assessment, valuation, and a buyer verdict. */
  }
}

Endpoints

POST/v1/fullreport

Runs (or reuses) a vehicle-history report for a VIN.

FieldTypeDescription
vinstring · required11–17 characters. Case and whitespace are normalized for you.
tierstring · optional"basic" (default) or "intelligent". Intelligent adds AI research and always returns 202 first (allow 1–3 minutes).

Responses: 200 full report · 202 still generating (below) · error envelope otherwise.

Response · 202 Accepted
{
  "report_id": "cml7x2ab40001rt9k",
  "status": "processing",
  "tier": "intelligent",
  "vin": "1HGCM82633A004352",
  "poll": "/api/v1/reports/cml7x2ab40001rt9k"
}
GET/v1/reports/{report_id}

Fetch a report by id — used to poll after a 202, or to re-download any report you have already run. Returns 202 while generating, 200 with the full payload when complete. Polling is free; we suggest a 3-second interval.

Request
curl https://carmareports.com/api/v1/reports/cml7x2ab40001rt9k \
  -H "Authorization: Bearer crk_live_YOUR_API_KEY"
GET/v1/account/usage

Your month-to-date billable usage — handy for reconciling before the invoice arrives. Optional ?period=YYYY-MM for past months.

Request
curl "https://carmareports.com/api/v1/account/usage?period=2026-07" \
  -H "Authorization: Bearer crk_live_YOUR_API_KEY"
Response · 200 OK
{
  "period": "2026-07",
  "billable_calls": 1240,
  "total_cents": 620000,
  "currency": "usd",
  "monthly_minimum_cents": 50000,
  "included_calls": 100,
  "by_tier": [
    { "tier": "basic", "billable_calls": 1180, "total_cents": 590000 },
    { "tier": "intelligent", "billable_calls": 60, "total_cents": 30000 }
  ]
}

Errors

Every error uses the same envelope and a stable machine-readable code:

Error envelope
{
  "error": {
    "code": "vehicle_not_found",
    "message": "No vehicle-history data was found for this VIN."
  }
}
CodeHTTPMeaningBilled
unauthorized401Missing, malformed, or revoked API key.No
invalid_request400Request body is not valid JSON or fails validation.No
invalid_vin400The VIN failed format checks (11–17 chars, no I/O/Q).No
tier_unavailable400The requested report tier is not currently offered.No
vehicle_not_found404No history data exists for this VIN.No
report_not_found404No report with that id exists on your account.No
rate_limited429Too many requests — check the Retry-After header.No
provider_unavailable502Vehicle data is temporarily unavailable. Safe to retry with backoff.No
provider_account_error502An account issue on our side is blocking vehicle data. Retrying will not help — we are alerted automatically.No
internal_error500Something went wrong on our side.No

Integration examples

Both examples handle the sync-or-poll pattern, so they work for Basic and Intelligent reports alike.

Node.js (18+, built-in fetch)
const BASE = "https://carmareports.com/api/v1";
const KEY = process.env.CARMA_API_KEY; // crk_live_…

async function getFullReport(vin) {
  const res = await fetch(`${BASE}/fullreport`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ vin }),
  });

  if (res.status === 200) return res.json();          // ready now

  if (res.status === 202) {                           // still generating — poll
    const { report_id } = await res.json();
    while (true) {
      await new Promise((r) => setTimeout(r, 3000));
      const poll = await fetch(`${BASE}/reports/${report_id}`, {
        headers: { Authorization: `Bearer ${KEY}` },
      });
      if (poll.status === 200) return poll.json();
      if (poll.status !== 202) throw new Error(`Report failed: ${poll.status}`);
    }
  }

  const { error } = await res.json();
  throw new Error(`${error.code}: ${error.message}`);
}

const report = await getFullReport("1HGCM82633A004352");
console.log(report.report.vehicle, report.report.scores);
Python (requests)
import os, time, requests

BASE = "https://carmareports.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['CARMA_API_KEY']}"}

def get_full_report(vin: str, tier: str = "basic") -> dict:
    res = requests.post(f"{BASE}/fullreport", headers=HEADERS,
                        json={"vin": vin, "tier": tier}, timeout=90)
    if res.status_code == 200:
        return res.json()                       # ready now
    if res.status_code == 202:                  # still generating — poll
        report_id = res.json()["report_id"]
        while True:
            time.sleep(3)
            poll = requests.get(f"{BASE}/reports/{report_id}",
                                headers=HEADERS, timeout=30)
            if poll.status_code == 200:
                return poll.json()
            if poll.status_code != 202:
                poll.raise_for_status()
    res.raise_for_status()

report = get_full_report("1HGCM82633A004352")
print(report["report"]["vehicle"], report["report"]["scores"])

Best practices

  • Store the API key in a secret manager or environment variable — never in client-side code or a repo.
  • Treat report_id as your receipt: log it with each request so support and billing questions are easy to trace.
  • On 429, wait the Retry-After seconds before retrying. On provider_unavailable, retry with backoff. Do not retry provider_account_error — it means the problem is on our side and we are already working on it. You are not billed for failures either way.
  • De-duplicate VINs on your side within a batch; identical VINs within 10 days return the same cached report and each request is still billable.
  • Need a higher rate limit or a second key for staging? Contact us — keys and limits are managed per account.