Auctions & history
Retrieve source-aware listing detail, galleries, price changes, and VIN history. A running openlane or ecarstrade auction has its bid read from the auction house at the moment you request the detail — nothing needs to be passed to enable it. Every other listing is served at its cycle price, refreshed by the scheduled ingestion pipeline.
Canonical auction detail with private and bidder fields removed.
Requires the auctions scope.
Parameters
site_slugstring · in the URL pathrequiredSource name returned by /api/sites.
Example encar·Where the value comes from /api/search -> results[].site_name
auction_idinteger · in the URL pathrequiredInteger auction database id.
Example 38112900·Where the value comes from /api/search -> results[].auction_id
Example requests
GET https://api.thecarapi.com/api/auction/encar/38112900GET https://api.thecarapi.com/api/auction/openlane/11125938Example response
{
"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_galleryobjectThe 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_detailsobjectCross-source normalized condition and paperwork: documents, inspection_reports, option_reports, car_reports, service_history, technical_inspection, paperwork, condition, damages, equipment, warranty, specs, location, seller, profile. Every key is omitted when the source has nothing for it, and the whole block is omitted for a listing whose details have not been fetched yet (details_pending: true).
car_identificationobjectRaw 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_priceobjectPresent when a live bid overlay is available, including a recent cached bid within the TTL: { 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 is not an error: it means the car is served at its cycle price, which for most listings is the only price there is.
live_price_pendingbooleanTrue when the refreshed figure was not to hand in time for this response — your request is never held waiting on the auction house. 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. This is the only case worth reading again for, and the only case that sets the flag.
current_price / current_final / current_tax / current_tax_delivery / public_price_eurnumberJSON numbers, always. Recomputed from the 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_id_strstringThe auction id as a string — the one to persist and to address the car with. japan ids exceed 2^53, so JavaScript and any other client parsing JSON numbers as IEEE-754 doubles silently rounds the numeric auction_id and the result no longer addresses the row it came from. auction_id is unchanged and stays a number for existing consumers.
vehicle_id / manufacturer_slug / model_group_slug / fuel_typeinteger | stringThe same four catalog-shaped keys /api/listVehicles adds. vehicle_id is a duplicate of auction_id (same 2^53 caveat — prefer auction_id_str). manufacturer_slug and model_group_slug are the values the catalog routes are addressed by, so a detail response is enough to build a link to its manufacturer and model-group pages without a second lookup. fuel_type is present only when the row carries a fuel.
is_blindbooleanTrue when the auction house publishes no bid at all, by design — most eCarsTrade auctions. This is the one case where current_price: null is a final answer rather than missing data: there is no figure to fetch, now or later, and no live price will ever arrive. Read it before rendering "price unavailable, retrying…", which on a blind lot retries forever; show estimated_value_eur instead, labelled as an estimate.
details_pendingbooleanTrue while the full detail payload has not been fetched from the source yet. vehicle_details is omitted meanwhile and the response carries a short max-age so a polling client sees the details land. Not an error.
auction_end_attimestamp | nullAbsolute 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 | nullThe 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 may be null on eCarsTrade lots without a valuation. 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.
steeringenumleft or right, never null. japan 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 | nullCO2 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 | nullA 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 | nullNEDC 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.
vin_ocrobject | nullAuto1 only, and the reason an Auto1 car has a VIN at all: Auto1 publishes no chassis number but photographs the registration certificate, so the VIN is read off that paperwork. { vin, confidence, evidence } — confidence is high, stored or medium, and evidence identifies the document photo it was read from. A high or stored read also fills chassis_number when the row has none, so a client that only reads chassis_number needs no change. A medium read is labelled and never promoted to the car identity: show it as unconfirmed. Roughly 41% of Auto1 cars with a document photo end up with a published VIN — the ceiling is upstream, because Auto1 blurs the VIN field on ES, IT and PT certificates. Part of the vehicle_identity field grant.
vin_ocr_pendingbooleanTrue when a VIN read was queued by this request and was not finished in time to answer it — your request is never held waiting on OCR. Read the car once more after a second or two and then stop; the answer is cached for a day once found. Behaves exactly like live_price_pending, and like it, is never an error.
source_comparisonobject | nullKorean sources only. The same car is often listed on both Encar and KB ChaChaCha; listings are grouped on a shared VIN, or a corroborated plate with make and model, and one is published while the rest are hidden. This block is the evidence: offers (every listing for the car, each with site, auction_id, price_eur, offer_link and eligible), selected (the one published), spread_eur (highest minus lowest eligible price — what the same car costs on the cheaper source), image_source (the listing the gallery came from; photo sets from two listings are never interleaved) and match_method (vin or plate_make_model). Requires the korea_merge field grant.
canonical_offerobject | nullPresent on a Korean listing that was resolved as a duplicate: { site, auction_id } for the representative published in its place. Also behind the korea_merge field grant.
Every recorded price movement for one listing, oldest first.
Requires the auctions scope.
Parameters
site_slugstring · in the URL pathrequiredAuction source slug.
Example encar·Where the value comes from /api/search -> results[].site_name
auction_idinteger · in the URL pathrequiredAuction database id.
Example 38112900·Where the value comes from /api/search -> results[].auction_id
Example requests
GET https://api.thecarapi.com/api/auction/encar/38112900/price-historyGET https://api.thecarapi.com/api/auction/openlane/11125938/price-historyExample response
{
"success": true,
"site": "encar",
"auction_id": 38112900,
"source_auction_id": "38112900",
"history": [
{
"event_type": "initial",
"source_auction_id": "38112900",
"changed_fields": [
"current_price",
"public_price_eur"
],
"current_price": 21500,
"buy_now_price": null,
"start_price": null,
"final_price": null,
"current_final": null,
"buynow_final": null,
"public_price_eur": 21500,
"currency_code_id": "EUR",
"observed_at": "2026-07-10T08:00:00",
"created_at": "2026-07-10T08:00:05"
}
]
}What you get back
event_typeenumExactly three values: initial (the first prices we ever recorded for this lot), baseline (the state before the first observed change, written once so a series never starts mid-move), and change (a subsequent movement). There is no price_change value — a client matching on that has been discarding every event.
changed_fieldsstring[]Which price fields moved in this event. Always an array, empty on a baseline row.
current_price / buy_now_price / start_price / final_price / current_final / buynow_final / public_price_eurnumber | nullThe full price snapshot as of observed_at. JSON numbers or null.
currency_code_idstringCurrency of the source-side figures.
observed_attimestampWhen the change was observed. The field to plot against.
created_attimestampWhen the row was written. Within seconds of observed_at; not a second data point.
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 pathrequiredAuction source slug.
Example encar·Where the value comes from /api/search -> results[].site_name
auction_idinteger · in the URL pathrequiredAuction database id.
Example 38112900·Where the value comes from /api/search -> results[].auction_id
Example requests
GET https://api.thecarapi.com/api/auction-images/encar/38112900GET https://api.thecarapi.com/api/auction-images/openlane/11125938Example response
{
"success": true,
"count": 1,
"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
countintegerHow many photos this response carries.
pendingintegerHow many photos the auction still owes. Show placeholders for these instead of a silently short gallery.
urlstringThe best URL available right now — served_url once vaulted, remote_url until then.
served_urlstringA path, not an absolute URL. It begins /image-vault/… and is relative to the API base, so join it: https://api.thecarapi.com + served_url. Load this directly once the photo is stored.
remote_urlstringAlways absolute — the source’s own URL. Load this through an image proxy until the photo is vaulted.
image_statusenumready, pending, downloading, failed, blocked, or not_downloaded — but on this endpoint it is always ready, because only stored photos are listed at all. The other values appear on the image annotations attached to search cards, where a photo can still be in flight. failed and blocked mean the photo exhausted its retries and is not counted in pending, so a gallery can settle with pending: 0 and still be shorter than picture_count.
image_sourceenumdownloaded (in the vault) or proxied (still served from the source).
index / is_primaryinteger / booleanGallery order and which photo is the thumbnail.
source_sectionstringSection the source filed the photo under, e.g. exterior, interior, damage.
width / heightintegerPixel dimensions of the stored photo.
servedUrl / thumbnailUrl / image_indexstring | integercamelCase and legacy twins of served_url, thumbnail and index, kept for older clients and always equal to their counterparts. Prefer the snake_case names — the twins may be retired.
local_status / local_rel_pathstringlocal_status is the same value as image_status; local_rel_path is the vault path served_url is built from.
Look up a full VIN across current and archived auction records.
Requires the auctions scope.
Parameters
vinstring · in the URL pathrequiredValid 17-character VIN. Matching ignores punctuation and case in the stored VIN, so a source that wrote it with separators still matches.
Example WBA8E9G50GNU12345·Where the value comes from /api/auction/{site}/{id} -> auction.chassis_number
Example requests
GET https://api.thecarapi.com/api/vin/WBA8E9G50GNU12345/historyGET https://api.thecarapi.com/api/vin/KNAB3811ALT123456/historyExample response
{
"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,
"source_auction_id": "38112900"
}
]
}What you get back
site_name / auction_id / source_auction_idstring | integerWhich source and lot. source_auction_id is the Auto1 batch id where one exists, otherwise the auction id as text.
chassis_numberstringThe VIN as the source published it (punctuation and case as stored).
clean_make / clean_model / model_displaystringNormalized identity.
date_first_registration / mileagestring | integerRegistration date and the mileage recorded on that appearance.
current_price / buy_now_price / start_price / final_price / current_final / buynow_final / public_price_eurnumber | nullThe whole price block. JSON numbers or null.
currency_code_idstringCurrency of the source-side figures.
first_seen_at / last_seen_at / last_available_at / created_at / updated_at / archived_attimestamp | nullLifecycle. archived_at is null on a live row.
is_active / offer_linkboolean | stringWhether the lot is still open, and its listing URL.
archivedbooleanTrue when this appearance comes from the archive rather than live inventory.