// API REFERENCE

OtoIndex Car Data API reference

The OtoIndex Car Data API returns maintenance-grade vehicle data as JSON: fluid types and capacities, the spec sheet, tire and wheel fitment, service intervals, OEM part numbers matched to the config with dated prices and labor per service, labor time estimates, and sanitized per-VIN service history. Every value carries its source and a confidence score. Identify a vehicle by vin, by year + make + model (+ optional trim), or by an exact config_key. Every read endpoint below except vehicle media and service history can be tried for free with no key on the sandbox at /quickstart; authenticated calls go to vinspeclookup.com/api.

20endpoints documented below.
13v1 endpoints — stable, additive-only.
7v0 endpoints — preview, changes with notice.
OpenAPI 3.1machine-readable spec, served through the gateway.
// V1 — STABLE

Fields are never removed or redefined within v1; we only add. Clients must ignore fields they do not recognise. Policy →

GET

GET /v1/decode

stableIdentity

Decode a vehicle (identity only)

Resolve a VIN (or config_key / YMMT) to its canonical identity — config + engine — with no group joins. The lightest lookup.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
yearqueryintegernoModel year.e.g. 2019
makequerystringnoMake (case-insensitive).e.g. Honda
modelquerystringnoModel (case-insensitive).e.g. CR-V
trimquerystringnoOptional trim filter to narrow YMMT matches.e.g. EX
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be

Pass exactly one identifier mode: a vin, a config_key, or year + make + model (with an optional trim). A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Vehicle identity.

  • object: "decode"
  • config: VehicleConfig
  • 400 — No usable identifier was provided. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 409 — More than one config matched a YMMT lookup — retry with a config_key from `matches` (or add &trim=). (error, message, matches)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/decode?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v1/configs

stableIdentity

List candidate configs for a YMMT

Every enriched config that fits a year/make/model[/trim], always as a list. Turns the 409 disambiguation into a first-class lookup — pick a config_key and switch to exact access.

// Parameters

NameInTypeRequiredDescription
yearqueryintegernoModel year.e.g. 2019
makequerystringnoMake (case-insensitive).e.g. Honda
modelquerystringnoModel (case-insensitive).e.g. CR-V
trimquerystringnoOptional trim filter to narrow YMMT matches.e.g. EX

// Responses

200 Candidate configs.

  • object: "config_list"
  • year: integer
  • make: string
  • model: string
  • trim: string | null
  • count: integer
  • configs: ConfigCandidate[]
  • 400 — Missing YMMT. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No config matches that YMMT. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/configs?year=2019&make=Honda&model=CR-V'
GET

GET /v1/catalog

stableIdentity

The enriched catalog

Every config we hold that is complete (50% fill or more), as the same rows `/v1/configs` returns. No VINs. `?limit=` caps the list (default 2000).

// Parameters

NameInTypeRequiredDescription
limitqueryintegernoMaximum rows (1–5000).e.g. 50

// Responses

200 The catalog.

  • object: "config_catalog"
  • count: integer
  • configs: ConfigCandidate[]
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v1/catalog?limit=50' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
GET

GET /v1/vehicle

stableSpecifications

Full vehicle payload (field-selectable)

Everything we hold on one vehicle: identity, layer-tagged specs (served + excluded), OEM tires, service intervals, priced parts with labor per service, and (VIN lookups only, `service_history:read` keys only) sanitized service history. Use `?include=` to return only the groups you need — omit it for every group your key may see. `/v1/labor` returns labor alone, without the parts and price joins.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
yearqueryintegernoModel year.e.g. 2019
makequerystringnoMake (case-insensitive).e.g. Honda
modelquerystringnoModel (case-insensitive).e.g. CR-V
trimquerystringnoOptional trim filter to narrow YMMT matches.e.g. EX
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
includequery"specs" | "fluids" | "tires" | "intervals" | "maintenance" | "services" | "parts" | "history"[]noComma-separated groups to return (omit for every group your key may see; empty for identity only). Aliases resolve: specs·fluids·attributes·chassis→specs, tires·wheels, intervals·maintenance·schedule, services·parts·labor, history. `history` needs the `service_history:read` scope: requesting it without that scope returns 403.e.g. fluids,tires

Pass exactly one identifier mode: a vin, a config_key, or year + make + model (with an optional trim). A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 The vehicle payload.

  • object: "vehicle"
  • config: VehicleConfig
  • specs: SpecFieldV1[]
  • excluded: ExcludedField[]
  • tires: TiresV1
  • intervals: Interval[]
  • services: ServiceEntryV1[]
  • history: object | null
  • meta: MetaV1
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — No usable identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks maintenance:read, or asked for include=history without service_history:read. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 409 — More than one config matched a YMMT lookup — retry with a config_key from `matches` (or add &trim=). (error, message, matches)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/vehicle?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v1/specs

stableSpecifications

The layer-tagged spec sheet

Every populated spec field (fluids + attributes + chassis service points), each tagged with its data layer, confidence and source domain — plus the `excluded[]` list of B-licensed / X-flagged fields the gate withheld.

// Parameters

NameInTypeRequiredDescription
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Spec sheet.

  • object: "specs"
  • config: VehicleConfig
  • specs: SpecFieldV1[]
  • excluded: ExcludedField[]
  • meta: MetaV1
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/specs?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v1/fluids

stableFluids & Capacities

Fluids & capacities

The flagship depth slice: engine oil viscosity + capacity, coolant, transmission, brake, power-steering, and differential / transfer-case fluid types and capacities — layer-tagged.

// Parameters

NameInTypeRequiredDescription
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Fluid specs.

  • object: "fluids"
  • config: VehicleConfig
  • specs: SpecFieldV1[]
  • meta: MetaV1
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/fluids?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v1/maintenance-schedule

stableMaintenance

OEM service intervals

OEM maintenance intervals by miles and months, each with confidence and a mechanic-verified flag.

// Parameters

NameInTypeRequiredDescription
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Intervals.

  • object: "maintenance_schedule"
  • config: VehicleConfig
  • intervals: Interval[]
  • meta: MetaV1
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/maintenance-schedule?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v1/parts

stableParts

Parts, prices & labor per service

OEM part numbers matched to the config and grouped per service — role, position, quantity and fitment confidence — each with a reference price and its as-of date, alongside the labor answer for that service.

// Parameters

NameInTypeRequiredDescription
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
servicequerystringnoOptional service-slug filter: a snake_case slug from the vehicle's services[].service (e.g. brake_pad_replacement). On /v1/parts an unknown slug returns 404 unknown_service listing the valid slugs.e.g. brake_pad_replacement

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Parts + labor per service.

  • object: "parts"
  • config: VehicleConfig
  • services: ServiceEntryV1[]
  • meta: MetaV1
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier, or `service` names a slug this vehicle doesn't have (`unknown_service`; the valid slugs are in `available`). (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/parts?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v1/tires

stableTires

OEM tire & wheel package

Full OEM fitment: every tire option, with the top-level sizes and recommended pressures taken from the OEM-standard option, plus run-flat / staggered flags. (Battery CCA is in `/v1/specs`.)

// Parameters

NameInTypeRequiredDescription
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Tire package.

  • object: "tires"
  • config: VehicleConfig
  • tires: TiresV1
  • meta: MetaV1
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v1/tires?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v1/labor

stableLabor

Labor per service

Labor for every service that applies to the vehicle, without the parts and price joins. `source` says how each answer was made: `empirical` (measured from completed jobs), `model_estimate` (our tier model), `service_default` (the service's default time, when nothing better resolves) or `unresolved`. `empirical` carries the measured hours, p25–p75 and sample size when the config has them. Requires the `labor:read` scope.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
yearqueryintegernoModel year.e.g. 2019
makequerystringnoMake (case-insensitive).e.g. Honda
modelquerystringnoModel (case-insensitive).e.g. CR-V
trimquerystringnoOptional trim filter to narrow YMMT matches.e.g. EX
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
servicequerystringnoOptional service-slug filter: a snake_case slug from the vehicle's services[].service (e.g. brake_pad_replacement). On /v1/parts an unknown slug returns 404 unknown_service listing the valid slugs.e.g. brake_pad_replacement

Pass exactly one identifier mode: a vin, a config_key, or year + make + model (with an optional trim). A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Labor.

  • object: "labor"
  • config: VehicleConfig
  • tier: string | null
  • services: object[]
  • meta: MetaV1
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — No usable identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier, or `service` names a slug this vehicle doesn't have (`unknown_service`; the valid slugs are in `available`). (error, message, available)
  • 409 — More than one config matched a YMMT lookup — retry with a config_key from `matches` (or add &trim=). (error, message, matches)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v1/labor?config_key=2019_honda_cr_v_ex_l15be' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
GET

GET /v1/service-history

stableService History

Sanitized service history (by VIN)

Per-VIN records: completed shop visits, owner-reported maintenance, and accepted document extractions. **Sanitized by contract** — no PII, costs, shop identity or free-text notes. `confidence` is a 0–1 number where one exists (document extractions); `confidence_label` carries the qualitative label (`verified`, `self_reported`…). Requires the `service_history:read` scope.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007

// Responses

200 Service history.

  • object: "service_history"
  • vin: string
  • records: object[]
  • meta: object
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — Missing VIN. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — Unknown VIN. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v1/service-history?vin=5J6RW2H82KL000007' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
GET

GET /v1/enrich

stableEnrichment

Poll enrichment status

Free status poll for a VIN or config_key.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Status.

  • object: string
  • status: "complete" | "enriching" | "failed" | "incomplete" | "queued"
  • config_key: string | null
  • last_enriched_at: string | null
  • fill_rate: number | null
  • 304 — Not modified: the `If-None-Match` ETag still matches. No body, and not counted against the monthly quota.
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — Unknown. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v1/enrich?config_key=2019_honda_cr_v_ex_l15be' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
POST

POST /v1/enrich

stableEnrichment

Enrich a VIN on demand

POST a VIN we haven't seen (or whose data is stale) and the full enrichment pipeline runs. Fresh cache hits → 200 (free). A scheduled run → 202; poll `GET /v1/enrich?vin=…` until `complete`. Requires the `enrich:write` scope. Runs scheduled through `/v0/enrich` and `/v1/enrich` share one daily cap.

// Parameters

NameInTypeRequiredDescription
vinbodystringyes—e.g. 5J6RW2H82KL000007

// Responses

200 Cache hit (enriched in the last 180 days; free) or already in flight.

  • object: string
  • status: "complete" | "enriching" | "failed" | "incomplete" | "queued"
  • config_key: string | null
  • last_enriched_at: string | null
  • fill_rate: number | null

202 Enrichment scheduled. Uses one enrich credit (or metered overage), refunded if the run can't be scheduled.

  • object: "enrichment"
  • status: string
  • vin: string
  • run_id: string | null
  • credits_remaining: integer
  • poll: object
  • 400 — No VIN (`missing_param`), a VIN that isn't 17 characters (`invalid_vin`), or one that couldn't be decoded (`vin_decode_failed`). No credit is used. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 402 — No credit to spend (self-serve keys): out of enrich credits with metered overage off (`no_enrich_credits`, with a `topup_url`), or the monthly enrich spend cap reached (`spend_cap_reached`). (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 422 — Decoded but unsupported. No credit is used. (error, message, available)
  • 429 — The per-minute rate limit (`rate_limited`), or the daily safety cap of 500 scheduled runs per key, counted across `/v0/enrich` and `/v1/enrich` (`daily_cap`; the reserved credit is refunded). Cache hits stay free. (error, message, available)
  • 500 — The vehicle row couldn't be created. No credit is used. (error, message, available)

// Example

Writes need your own key with the enrich:write scope; the sandbox is read-only.

curl -X POST 'https://vinspeclookup.com/api/v1/enrich' \
  -H 'Authorization: Bearer otp_live_4f2a9c…' \
  -H 'Content-Type: application/json' \
  -d '{"vin":"5J6RW2H82KL000007"}'
// V0 — PREVIEW

Shape may change with notice on the changelog. Any version gets at least 90 days' notice before retirement. Policy →

GET

GET /v0/vehicle

previewSpecifications

Full vehicle payload (v0, legacy)

Legacy. The original flagship — the whole vehicle payload in one response (history only for `service_history:read` keys). Unchanged and still supported; new integrations use `/v1/vehicle`, whose shapes differ (see Versions).

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
yearqueryintegernoModel year.e.g. 2019
makequerystringnoMake (case-insensitive).e.g. Honda
modelquerystringnoModel (case-insensitive).e.g. CR-V
trimquerystringnoOptional trim filter to narrow YMMT matches.e.g. EX
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be

Pass exactly one identifier mode: a vin, a config_key, or year + make + model (with an optional trim). A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 The vehicle payload.

  • object: "vehicle"
  • config: VehicleConfig
  • specs: SpecField[]
  • excluded: ExcludedField[]
  • tires: Tires
  • intervals: Interval[]
  • services: ServiceEntry[]
  • history: object | null
  • meta: Meta
  • 400 — No usable identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 409 — More than one config matched a YMMT lookup — retry with a config_key from `matches` (or add &trim=). (error, message, matches)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v0/vehicle?config_key=2019_honda_cr_v_ex_l15be' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
GET

GET /v0/maintenance

previewFluids & Capacities

Maintenance specs (v0, legacy, strict gate)

Legacy. Layer-gated maintenance specs. Unlike `/v1/specs`, a field is only served when it has an evidence trail with a servable layer; everything else is listed in `excluded[]`.

// Parameters

NameInTypeRequiredDescription
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Maintenance specs.

  • object: "maintenance_specs"
  • config: ConfigBrief
  • fields: SpecField[]
  • excluded: ExcludedField[]
  • meta: Meta
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v0/maintenance?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v0/labor

previewLabor

Labor times & estimates (v0, legacy)

Legacy; new integrations use `/v1/labor`. `estimates[]` covers every applicable service, each labelled with its method (`estimate_source`: `empirical` or `model_estimate`) and a confidence score. In v0, `model_estimate` also covers the service's default time when nothing better resolves; `/v1/labor` labels that `service_default`. `services[]` carries hours measured from completed repair jobs (sample size, p25–p75) once a config has them. Licensed book-time blends are never served.

// Parameters

NameInTypeRequiredDescription
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
servicequerystringnoOptional service-slug filter: a snake_case slug from the vehicle's services[].service (e.g. brake_pad_replacement). On /v1/parts an unknown slug returns 404 unknown_service listing the valid slugs.e.g. brake_pad_replacement

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Labor.

  • object: "empirical_labor"
  • config_key: string | null
  • services: object[]
  • estimates: object[]
  • tier: string | null
  • note: string
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No enriched vehicle matches that identifier. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Runs against the free, keyless sandbox — no signup, rate-limited per IP.

curl 'https://vinspeclookup.com/api/sample/v0/labor?config_key=2019_honda_cr_v_ex_l15be'
GET

GET /v0/vehicle-image

previewMedia

Vehicle exterior render

The cached exterior render for a config. Cache-only: a config without a cached render returns 404 `no_image`, and no live fetch is made. Requires the `media:read` scope.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
yearqueryintegernoModel year.e.g. 2019
makequerystringnoMake (case-insensitive).e.g. Honda
modelquerystringnoModel (case-insensitive).e.g. CR-V
trimquerystringnoOptional trim filter to narrow YMMT matches.e.g. EX
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be

Pass exactly one identifier mode: a vin, a config_key, or year + make + model (with an optional trim). A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Vehicle image.

  • object: "vehicle_image"
  • config: ConfigBrief
  • image: object | null
  • meta: Meta
  • 400 — No usable identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — No vehicle or image. (error, message, available)
  • 409 — More than one config matched a YMMT lookup — retry with a config_key from `matches` (or add &trim=). (error, message, matches)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v0/vehicle-image?config_key=2019_honda_cr_v_ex_l15be' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
GET

GET /v0/service-history

previewService History

Sanitized service history (by VIN, v0, legacy)

Legacy; new integrations use `/v1/service-history`. Carfax-style per-VIN records: completed shop visits, owner-reported maintenance, and accepted document extractions. **Sanitized by contract** — no PII, costs, shop identity or free-text notes. Requires the `service_history:read` scope.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007

// Responses

200 Service history.

  • object: "service_history"
  • vin: string
  • records: object[]
  • meta: object
  • 400 — Missing VIN. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — Unknown VIN. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v0/service-history?vin=5J6RW2H82KL000007' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
GET

GET /v0/enrich

previewEnrichment

Poll enrichment status (v0, legacy)

Free status poll for a VIN or config_key.

// Parameters

NameInTypeRequiredDescription
vinquerystringno17-char VIN of a vehicle we've seen. The only mode that adds the service-history section.e.g. 5J6RW2H82KL000007
config_keyquerystringnoCanonical vehicle-config key — exact and unambiguous. 409 responses hand these back.e.g. 2019_honda_cr_v_ex_l15be

Pass exactly one identifier mode: a vin, a config_key. A year/make/model lookup that matches several configs returns 409 with the candidate config_keys.

// Responses

200 Status.

  • object: string
  • status: "complete" | "enriching" | "failed" | "incomplete"
  • config_key: string | null
  • enrichment_status: string | null
  • last_enriched_at: integer | null
  • fill_rate: number | null
  • 400 — Missing identifier. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 404 — Unknown. (error, message, available)
  • 429 — Per-key per-minute rate limit exceeded. (error, message, available)

// Example

Authenticated call through the API gateway with your own key.

curl 'https://vinspeclookup.com/api/v0/enrich?config_key=2019_honda_cr_v_ex_l15be' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
POST

POST /v0/enrich

previewEnrichment

Enrich a VIN on demand (v0, legacy)

POST a VIN we haven't seen (or whose data is stale) and the full enrichment pipeline runs. Fresh cache hits → 200 (free). A scheduled run → 202; poll `GET /v0/enrich?vin=…` until `complete`. Requires the `enrich:write` scope (granted separately).

// Parameters

NameInTypeRequiredDescription
vinbodystringyes—e.g. 5J6RW2H82KL000007

// Responses

200 Cache hit (enriched in the last 180 days; free) or already in flight.

  • object: string
  • status: "complete" | "enriching" | "failed" | "incomplete"
  • config_key: string | null
  • enrichment_status: string | null
  • last_enriched_at: integer | null
  • fill_rate: number | null

202 Enrichment scheduled. Uses one enrich credit (or metered overage), refunded if the run can't be scheduled.

  • object: "enrichment"
  • status: string
  • vin: string
  • run_id: string | null
  • credits_remaining: integer
  • poll: object
  • 400 — No VIN (`missing_param`), a VIN that isn't 17 characters (`invalid_vin`), or one that couldn't be decoded (`vin_decode_failed`). No credit is used. (error, message, available)
  • 401 — Missing, invalid, or revoked key. (error, message, available)
  • 402 — No credit to spend (self-serve keys): out of enrich credits with metered overage off (`no_enrich_credits`, with a `topup_url`), or the monthly enrich spend cap reached (`spend_cap_reached`). (error, message, available)
  • 403 — The key lacks the required scope. (error, message, available)
  • 422 — Decoded but unsupported. No credit is used. (error, message, available)
  • 429 — The per-minute rate limit (`rate_limited`), or the daily safety cap of 500 scheduled runs per key, counted across `/v0/enrich` and `/v1/enrich` (`daily_cap`; the reserved credit is refunded). Cache hits stay free. (error, message, available)
  • 500 — The vehicle row couldn't be created. No credit is used. (error, message, available)

// Example

Writes need your own key with the enrich:write scope; the sandbox is read-only.

curl -X POST 'https://vinspeclookup.com/api/v0/enrich' \
  -H 'Authorization: Bearer otp_live_4f2a9c…' \
  -H 'Content-Type: application/json' \
  -d '{"vin":"5J6RW2H82KL000007"}'

Authentication

Send your API key on every authenticated request, either as a Bearer token in the Authorization header or in an x-api-key header. Keys look like otp_live_…, are shown once when issued and stored hashed; each key carries scopes (for example maintenance:read, labor:read, media:read, service_history:read, enrich:write) and a per-minute rate limit. A missing or invalid key returns 401; a key without the endpoint's scope returns 403; over the limit returns 429.

curl 'https://vinspeclookup.com/api/v1/fluids?config_key=2019_honda_cr_v_ex_l15be' \
  -H 'Authorization: Bearer otp_live_4f2a9c…'
# or
curl 'https://vinspeclookup.com/api/v1/fluids?config_key=2019_honda_cr_v_ex_l15be' \
  -H 'x-api-key: otp_live_4f2a9c…'

The sandbox at https://vinspeclookup.com/api/sample needs no key at all: it mirrors every documented read endpoint, is rate-limited per IP address, and blocks writes. Keys for the authenticated API are issued through the request-access form; plans and limits are on the pricing page.

Versioning

The version is the first path segment. /v1 endpoints are stable: we will not remove fields or change their meaning within v1; we may add fields, and clients must ignore fields they do not recognise. /v0 endpoints are preview: their shape may change with notice on the changelog, and we do not recommend depending on them for anything you cannot adjust quickly. When we retire a version, we announce it at least 90 days ahead and keep it running for that period.

stable and preview pills on each endpoint above follow this rule. The full policy is in the Terms of Service.

How to read provenance

Each field carries a layer (the kind of source, below) and, where one is recorded, a confidence between 0 and 1 and the source_domain it was taken from. A value stored without an evidence trail is still served, tagged C, with source_domain set to null: treat it as unverified. Part prices also carry as_of, the date the price was last confirmed, so you can tell a fresh price from an old one.

LayerMeaningServed
AOEM or official: owner's manuals, manufacturer dataYes
BLicensed third-party database (public NHTSA data is the exception and is served)No
CWeb-derived: our own research. Stored values without an evidence trail are also served as CYes
DEmpirical: measured from completed repair jobsYes
EHuman-verified: confirmed by a mechanic or a directorYes
XFlagged: an anomaly, or confidence below 0.4No

The gate is visible, never silent. Every response restates the rule in meta.gate and meta.layer_formula, and the vehicle, spec-sheet and maintenance responses list each value held back as B or X in an excluded array, with the layer that blocked it.

Read confidence as a ranking signal, not a probability. It is assigned when the vehicle is enriched; below 0.4 a value becomes X and is not served. Where a decision matters (a fluid specification, a part fitment), check it against the manufacturer's service information.

// TRY IT

Interactive console

The same operations, rendered from the live OpenAPI document with full request and response schemas and a try-it panel. Paste a key to call the authenticated API, or use the keyless sandbox on the quickstart.

The interactive console loads as you scroll to it.

▸ reference generated from the OpenAPI spec · 20 operations · openapi.json