contract 2026-08-19
Endpoints

Auctions & history

Retrieve source-aware listing detail, galleries, price changes, and VIN history. A running openlane or ecarstrade auction has its bid refreshed from the auction house while the request is served — nothing needs to be passed to enable it.

GET

/api/auction/{site_slug}/{auction_id}

Canonical auction detail with private and bidder fields removed.

Requires the auctions scope.

Parameters

site_slugstring · in the URL pathrequired

Source name returned by /api/sites.

Example encar·Where the value comes from /api/search -> results[].site_name

auction_idinteger · in the URL pathrequired

Integer auction database id.

Example 38112900·Where the value comes from /api/search -> results[].auction_id

Example requests

Request
GET https://api.thecarapi.com/api/auction/encar/38112900
GET https://api.thecarapi.com/api/auction/openlane/11125938

Example response

json
{
  "success": true,
  "auction": {
    "auction_id": 11409652,
    "site_name": "openlane",
    "clean_make": "BMW",
    "clean_model": "320d",
    "model_display": "320d M Sport",
    "registration_year": 2020,
    "mileage": 45000,
    "current_price": 25900,
    "current_final": 33566,
    "public_price_eur": 33926,
    "auction_end_at": "2026-08-20T10:00:00Z",
    "images": [],
    "vault_gallery": {
      "images": [
        {
          "served_url": "/image-vault/ab/cd/openlane_11409652_00_deadbeef.avif",
          "remote_url": "https://cdn.example/photo_1.jpg",
          "image_status": "ready"
        }
      ],
      "count": 28,
      "pending": 0
    },
    "vehicle_details": {},
    "car_identification": {},
    "live_price": {
      "price": 25900,
      "currency": "EUR",
      "source": "openlane",
      "fetched_at": 1786659750
    }
  }
}

What you get back

vault_galleryobject

The image vault’s authoritative photo set, embedded verbatim: the exact body of /api/auction-images/{site}/{id} ({ images, count, pending }). Read it and a detail page is one request instead of two. Both routes build it from one cache entry, so they can never disagree; while pending is non-zero this response is served with max-age=10 so a polling client sees photos as they land. A client that ignores it behaves exactly as before.

vehicle_detailsobject

Cross-source normalized condition and paperwork: documents, inspection_reports, option_reports, service_history, technical_inspection, paperwork, condition, damages, equipment, warranty, specs, location, seller. Every key is omitted when the source has nothing for it, and the whole block is omitted while details_pending is true.

car_identificationobject

Raw source-specific spec map. Prefer vehicle_details where it covers what you need — its keys are stable across sources, these are not.

images / gallery_imagesobject[]

What the source record itself carried — untouched by this release. For the vault’s photo set with served URLs and dimensions, read vault_gallery above.

live_priceobject

Present only when the bid was refreshed from the auction house during this request: { price, currency, source, fetched_at }. price is the raw bid and equals current_price; fetched_at is a Unix timestamp in seconds — use it, not your own clock, to render “as of”. Its absence never means the price is stale — every listing is live-priced from the continuous feed regardless. The block only reports the additional at-request bid re-read, which most listings do not qualify for.

live_price_pendingboolean

True when the upstream refresh missed the request budget and is still running. Read the car once more after ~2s and then stop — the second read is served from cache. These responses carry Cache-Control: no-store and X-Live-Price: pending.

current_price / current_final / current_tax / current_tax_delivery / public_price_eurnumber

Recomputed from the refreshed bid when a live price happened, so a client that ignores live_price entirely still shows the right number. buy_now_price and buynow_final are never touched — a buy-now figure does not move with bidding.

auction_end_attimestamp | null

Absolute UTC instant the lot closes, or null for stock with no deadline. Drive countdowns from this — not from batch_end_date, whose timezone differs per auction house, nor auction_sec_left, which is a snapshot taken at the last refresh rather than seconds from now.

estimated_value_eurnumber | null

The auction house own valuation of the vehicle, in EUR — and NOT a price you can pay. No bid is settled against it and no car is sold at it. Only ecarstrade publishes one (its "eCT Estimation Price"); it is null for every other source and null for roughly 86% of eCarsTrade lots. Where it does appear it is often the only number on the lot, because a blind auction hides the bid: current_price, start_price and buy_now_price are all null while this carries the estimate. Label it as an estimate wherever you show it, and never sort or filter on it — it is absent on most cars, so any ordering that uses it silently drops them.

steeringenum

left or right, never null. japanauction is the only source that publishes a steering field; for every other source the side is read off the car registration market — a car registered in a country that drives on the left (GB, IE, JP, CY, MT, AU, NZ, ZA, IN, TH, HK, SG) is right, everything else left. Also present on every search result card and on /api/car-details.

co2integer | null

CO2 in g/km as the auction house measured and published it. Null on roughly half of lots, which publish none. It carries no standard tag and mixes both EU test cycles by registration year — see the CO2 & emissions page.

co2_estimatedinteger | null

A derived CO2 figure for lots that publish none, inferred from other lots of the same specification. NEVER a measured value, and null wherever the specification does not decide it. It is a separate field from co2 on purpose, so an estimate can never be mistaken for a measurement.

co2_estimated_standardenum | null

NEDC or WLTP — which EU test cycle co2_estimated is expressed on. Always read the estimate together with this: the same physical car reads 12-25% higher under WLTP, so a figure without its standard is not comparable.

GET

/api/auction/{site_slug}/{auction_id}/price-history

Chronological source-price and public-price snapshots for a listing.

Requires the auctions scope.

Parameters

site_slugstring · in the URL pathrequired

Auction source slug.

Example encar·Where the value comes from /api/search -> results[].site_name

auction_idinteger · in the URL pathrequired

Auction database id.

Example 38112900·Where the value comes from /api/search -> results[].auction_id

Example requests

Request
GET https://api.thecarapi.com/api/auction/encar/38112900/price-history
GET https://api.thecarapi.com/api/auction/openlane/11125938/price-history

Example response

json
{
  "success": true,
  "site": "encar",
  "auction_id": 38112900,
  "source_auction_id": "38112900",
  "history": [
    {
      "event_type": "price_change",
      "changed_fields": [
        "current_price"
      ],
      "current_price": 21500,
      "public_price_eur": 21500,
      "observed_at": "2026-07-10T08:00:00"
    }
  ]
}
GET

/api/auction-images/{site_slug}/{auction_id}

Ordered gallery metadata backed by the image vault. Usually unnecessary — the same body rides on the auction detail response as vault_gallery.

Requires the auctions scope.

Parameters

site_slugstring · in the URL pathrequired

Auction source slug.

Example encar·Where the value comes from /api/search -> results[].site_name

auction_idinteger · in the URL pathrequired

Auction database id.

Example 38112900·Where the value comes from /api/search -> results[].auction_id

Example requests

Request
GET https://api.thecarapi.com/api/auction-images/encar/38112900
GET https://api.thecarapi.com/api/auction-images/openlane/11125938

Example response

json
{
  "success": true,
  "pending": 12,
  "images": [
    {
      "url": "https://cdn.example/photo_1.jpg",
      "remote_url": "https://cdn.example/photo_1.jpg",
      "served_url": "/image-vault/ab/cd/ecarstrade_7399555_00_deadbeef.avif",
      "thumbnail": "/image-vault/ab/cd/ecarstrade_7399555_00_deadbeef.avif",
      "image_status": "ready",
      "image_source": "downloaded",
      "index": 0,
      "is_primary": true,
      "source_section": "exterior",
      "width": 1024,
      "height": 768
    }
  ]
}

What you get back

pendinginteger

How many photos the auction still owes. Show placeholders for these instead of a silently short gallery. A non-zero value also promotes the outstanding rows to user-requested priority.

urlstring

The best URL available right now — served_url once vaulted, remote_url until then.

served_urlstring

Same-origin vault path once the photo is stored. Load this directly.

remote_urlstring

Original source URL. Load this through an image proxy.

image_statusenum

ready, pending, downloading, failed, or not_downloaded.

image_sourceenum

downloaded (in the vault) or proxied (still served from the source).

index / is_primaryinteger / boolean

Gallery order and which photo is the thumbnail.

source_sectionstring

Section the source filed the photo under, e.g. exterior, interior, damage.

width / heightinteger

Pixel dimensions of the stored photo.

GET

/api/vin/{vin}/history

Look up a full VIN across current and archived auction records.

Requires the auctions scope.

Parameters

vinstring · in the URL pathrequired

Valid 17-character VIN. Exact match only.

Example WBA8E9G50GNU12345·Where the value comes from /api/auction/{site}/{id} -> auction.chassis_number

Example requests

Request
GET https://api.thecarapi.com/api/vin/WBA8E9G50GNU12345/history
GET https://api.thecarapi.com/api/vin/KNAB3811ALT123456/history

Example response

json
{
  "success": true,
  "vin": "WBA8E9G50GNU12345",
  "match_count": 2,
  "auctions": [
    {
      "site_name": "encar",
      "auction_id": 38112900,
      "clean_make": "BMW",
      "clean_model": "320d",
      "mileage": 45000,
      "public_price_eur": 21500,
      "first_seen_at": "2026-06-01T00:00:00",
      "last_seen_at": "2026-07-10T00:00:00",
      "archived": false
    }
  ]
}