Status
Changelog
Every contract version and what moved in it. The version this deployment serves is on every response as contract_version, currently 2026-09-03.
2026-09-03improvement2026-09-19
Current source coverage and API documentation
- Documented eight current sources, including KB ChaChaCha and the current Japanese importer under site=japan. Japan uses stored auction details, not /api/car-details.
- Added archive search, archive detail and coverage statistics, production-year facets and filters, trim facets and filtering, and per-key source and field permissions.
- Use /api/classifieds/* and the classifieds scope; /api/theparking/* and theparking grants remain compatible legacy aliases.
- Documented cached report-vault files, KB diagnosis and inspection status, Japanese offer evidence and unverified result prices.
- Contract version is 2026-09-03. This documentation review is dated 2026-09-19; it does not imply a new server contract or a deployment.
2026-08-29fix2026-08-29
Documentation audit — three things this reference stated were wrong
- Documentation only. No code changed, no field moved, no parameter changed its meaning, and contract_version stays 2026-08-19. This reference was audited line by line against the running service and three things it stated were wrong. If you coded against them, they are worth checking.
- price-history event_type never had the value we printed. The example showed "event_type": "price_change". That value does not exist. The field takes exactly three values — initial, baseline and change — and always has. A client matching on price_change has been discarding every event. The full per-event field set, the oldest-first ordering and the 5,000-event cap are now documented too.
- /api/search/auction-ids returns bare numeric ids, not site:id pairs. The example showed ["encar:38112900", "auto1:AB12CD"]. The endpoint returns auction_id values as plain JSON numbers, with no source attached and therefore no guarantee of uniqueness across sources. It is a filter, not an addressing scheme; use /api/search when you need cards you can address. Its three possible outcomes are now spelled out too — an empty list on a 503 means "unknown", not "zero".
- Pagination depth was described more restrictively than it behaves. /api/listVehicles has no offset cap either, and the 5,000 cap applies to the catalog, top-offer and theparking-listing routes. The depth policy now says which is which, and which envelopes carry page / page_size / max_page at all.
- Also newly documented, previously absent: the japanauction auction sheet (type auction_sheet in inspection_reports); vehicle_id, manufacturer_slug, model_group_slug and fuel_type on both detail responses; which endpoints omit the envelope metadata, and which are not enabled on a new key; full field tables for the market snapshots, the calculator breakdown, VIN history, /api/contract and theparking facets; that served_url is a path to be joined to the base URL; that theparking’s brand/model/fuel/gearbox/seller filters are case-sensitive; that the calculator spells the United Kingdom UK where the vehicle facets spell it GB; and a Recipes page with six complete flows.
2026-08-28fix2026-08-28
Accuracy fixes to brand matching and result counting — contract unchanged
- The schema contract is unchanged: no field was added, removed or retyped, no parameter changed its name or accepted values, and no endpoint changed its URL, method or authentication. contract_version stays 2026-08-19. A client that parses responses generically needs no code changes — what moves is the numbers.
- A brand filter now matches the brand, not the spelling. Sources do not agree on how to write a brand — openlane publishes "Volkswagen" where the catalogue and every other source say "VW" — and brand was matched on the published name alone, so a branded search silently dropped every source that spelt it differently. It is now matched on the brand identity or its name, so ?brand=vw, ?brand=VW and ?brand=175 all return the same inventory. Expect branded searches to return more rows and branded counts to rise; re-baseline any snapshot tests pinned to the old totals.
- One entry per model in the facet catalogue. On /api/models and /api/facets, a model written with different casing by different sources was listed twice, each row carrying part of the count. Entries are now merged on the model identity rather than its printed label, so each model appears once under one stable spelling with the count the search actually returns. Counts for affected models go up. Any de-duplication you were doing yourself is now unnecessary, though harmless.
- A filtered search total now matches the rows it returns. The row count and the rows themselves were not applying the same rule about lots that had closed, so a filtered search could report a few hundred matches and run out of rows before the last page. total, total_pages and the rows now agree; total gets slightly smaller and correct for filtered searches. Unfiltered searches were never affected, and include_ended=true / is_active=false still include closed lots.
2026-08-27improvement2026-08-27
Cross-filtered facets, per-option counts, capped card galleries and estimated_value_eur
- Live now, and served under contract version 2026-08-19: no field changed type, no parameter changed its name or accepted values, and no endpoint changed its URL, method or authentication, so the contract itself did not move. The one non-additive item is the first, which drops four keys from a single endpoint body; everything else is additive.
- Non-additive: GET /load-models no longer merges contract metadata into the brand map. contract_version, request_id, server_time and data_updated_at are removed from that body, because its payload keys are brand names and those four were being read as brands. Read the correlation id from the X-Request-ID header instead — it was always there. Filtering the four keys out remains harmless and correct.
- New auction-detail field estimated_value_eur on /api/auction/{site}/{id} and /api/car-details: the auction house own valuation in EUR, and not a price you can pay. Only ecarstrade publishes one, and it is null for every other source and for roughly 86% of eCarsTrade lots. Label it as an estimate, and do not sort or filter on it.
- Facet counts are now cross-filtered. /api/brands, /api/models, /api/sites and the value lists /api/years, /api/fuels, /api/gearboxes, /api/countries (individually and via /api/facets) respect the other filters on the request, each dimension ignoring only its own — so price_to=5000&fields=brands returns each brand count under EUR 5,000, and value lists omit options with no matches left. Facet endpoints accept the search filter set for this reason; is_active and include_ended are not among them, because facets always describe lots whose auction is still open. With no filters present the responses are byte-for-byte what they were before.
- Per-option counts on years, fuels, gearboxes and countries: year_counts, fuel_counts, gearbox_counts and country_counts, each an array of {value, count} in the same order as its sibling value list. They obey the same cross-filtered semantics. Brands, models and sites already returned counts. Clients reading only the plain value arrays are unaffected.
- Listing endpoints cap embedded galleries at 8 photos. /api/search and /api/listVehicles embed at most the first 8 display-ordered entries per row, primary first. Cards need only a thumbnail; anything treating images[] on a search row as the full gallery must read vault_gallery on the detail endpoint or /api/auction-images/{site}/{id} instead. Detail endpoints are unchanged and still return complete galleries plus the pending count.
- live_price_pending now occurs on cold opens. The first read of a running OpenLane or eCarsTrade auction returns immediately with live_price_pending: true, X-Live-Price: pending and Cache-Control: no-store rather than blocking for the bid; the documented single retry after ~2s collects the refreshed figure from cache. Integrations already following that recipe are unaffected.
- /image-vault/* paths answer with a 302 to a pre-signed object URL instead of streaming bytes through the API. Mainstream HTTP clients and browsers follow it transparently, and the redirected object is immutable-cached, so the hop is paid once per photo.
2026-08-19feature2026-08-19
Contract version 2026-08-19 — combined facets, embedded galleries, gzip and conditional requests
- Every change in this release is additive. No field changed type, no field was removed, no default changed, and no endpoint changed its response shape — an integration written against 2026-08-18 runs unmodified.
- New GET /api/facets returns brands, years, fuels, countries, gearboxes and sites in one response, with fields= selecting the subset you render. Each field is its own endpoint’s payload merged verbatim, so a value means exactly what it means there. Scope search, and it bills one quota unit instead of six. models stays at /api/models — it is brand-scoped.
- A facet dimension that cannot be served is reported under errors while every other dimension is returned normally, so one slow field cannot blank a filter sidebar. The per-dimension endpoints are unchanged and stay supported.
- GET /api/search accepts is_active as the documented spelling of the ended-lot switch: is_active=true (the default) hides closed lots, is_active=false shows them in addition to live ones. It is not a filter on the is_active response field, and it does not return only ended lots. include_ended is the same switch inverted and is unchanged; if both are sent, include_ended wins.
- GET /api/auction/{site}/{id} now embeds vault_gallery — the exact body of /api/auction-images/{site}/{id} (images, count, pending) — so a detail page is one request instead of two. images and gallery_images are untouched. Both routes read one cache entry and can never disagree, and while pending is non-zero both are served with max-age=10 so a polling client sees photos as they land.
- Send Accept-Encoding: gzip and any response over 2 KB comes back gzipped — on the order of an eighth of the bytes on a full search page. Most HTTP clients negotiate this transparently; a few (notably PHP Guzzle in some configurations) need it enabled explicitly.
- /api/search now carries an ETag, as the facet and catalog routes already did. Echo it back in If-None-Match and an unchanged page answers 304 Not Modified with no body. ETags are weak (W/"...") across the API by design, so one tag stays valid whether or not the body came back compressed — compare them as opaque strings.
- GET /api/contract declares the new surface: schemas.auction_detail.optional lists vault_gallery, schemas.brands_models_facets.optional lists errors, and version reads 2026-08-19.
2026-08-18breaking2026-08-18
Contract version 2026-08-18 — price fields are JSON numbers
- Price fields are now always JSON numbers. They were serialized as strings ("25600.00") on a normal read but as numbers (25600.0) whenever a live price had been refreshed — the same field, two types, depending on something a client could neither control nor predict. No change is needed if you parse with Number(...), float(...) or into a dynamic type; a statically typed client declaring them as strings has to retype them as numbers.
- Affected fields: start_price, current_price, current_final, current_tax, current_tax_delivery, buy_now_price, buynow_final, buynow_tax, buynow_tax_delivery, final_price, public_price_eur, current_bgn and buynow_bgn.
- /api/car-details now returns the price fields. It previously returned none of them, so the two detail endpoints disagreed about what a car costs. They arrive under the same names at the top level of the response, because that body is source-shaped and has no auction wrapper. Purely additive.
- New field live_price_pending: true means the upstream refresh missed the request budget and is still running — read once more after ~2s and stop. Those responses carry Cache-Control: no-store and X-Live-Price: pending, so a follow-up read is not answered from cache.
- GET /api/contract declares the new fields: schemas.auction_detail.optional now lists the price fields, live_price and live_price_pending.
2026-08-17feature2026-08-17
Contract version 2026-08-17 — live prices on running auctions
- A running openlane or ecarstrade auction now has its bid refreshed from the auction house while the request is served. There is no separate endpoint and no parameter to switch on: ask for the car the way you already do, and if it qualifies you get the current figure.
- It applies to GET /api/auction/{site}/{id} and to GET|POST /api/car-details. Search, top-offers, market intelligence and every price-based sort or filter stay on cycle prices, so a detail page can legitimately show a higher current_price than the search card it was opened from — that is a bid that moved after the last cycle, not an inconsistency.
- A refreshed response carries a live_price block ({price, currency, source, fetched_at}) and has current_price, current_final, current_tax, current_tax_delivery and public_price_eur recomputed, so a client that ignores live_price still shows the right number. buy_now_price and buynow_final are never touched.
- GET /api/contract gains a live_prices block ({enabled, sites, ttl_seconds}). Read it rather than hardcoding which sources support live prices, and use it to tell "this deployment cannot refresh prices" from "this car has none right now".
- A refreshed price is reused for ttl_seconds (default 120) and Cache-Control: max-age drops to match. Polling faster than the TTL returns the same figure.
- The classifieds listings feed gained source_exclude and include_total, and now reports total_capped (counting stopped at 100,000) and total_unavailable (the count did not finish its budget) alongside total. /api/theparking/facets gained limit and value_counts.
2026-08-14feature2026-08-14
Contract version 2026-08-14 — Japanese auctions, vehicle_details, account-scoped fields removed
- Added the Japanese auction houses (USS, ARAI, AUCNET, BAYAUC, CAA) as a seventh source under the slug japanauction: a valid site value for search, auction detail, price history, images and car-details. For japanauction and auto1, /api/car-details takes the offer id/UUID rather than the numeric auction id.
- Added the normalized vehicle_details block to /api/auction/{site}/{id} and /api/car-details — condition, documents, inspection reports, service history, equipment, warranty, specs and location, in one shape across sources. Omitted while details_pending.
- /api/countries now returns JP. country=europe is an exclusion, not a list: it currently excludes KR (Encar) and JP (Japanese auctions), and rows with no recorded country are treated as European.
- Account-scoped commercial data is now stripped at the response boundary: auction fees, bid history, transport and every delivery*/selfpickup* key, plus our buyer-account identity on each platform and internal processing metadata. Email addresses are redacted from free-text values. Published asking prices, VAT scheme, estimated repair cost and seller location are unaffected.
2026-08feature2026-08-06
European retail classifieds feed
- Added /api/theparking/listings, /api/theparking/facets, and /api/theparking/models under the new theparking scope.
- Retail classifieds aggregated from 681 origin portals across 39 European countries, with seller type and origin portal on every row.
- Ended and paid-placement listings are excluded from every response.
2026-07feature2026-07-15
eCarsTrade added as an auction source
- eCarsTrade is now a valid site value for search, auction detail, car-details, and the calculator fee model.
- Galleries are exposed under the ImageUrls key of car_identification.
- Published /api/top-offers with the market_reference each verdict was made against.
2026-06-10feature2026-06-10
Contract version 2026-06-10
- Published contract version 2026-06-10 with pagination caps (limit 100, offset 5000).
- Documented public-read site bypass for GET inventory, facets, catalog, SEO, images, health.
- Documented search cards, auction details, galleries, catalog responses, and pagination limits.
2026-05improvement2026-05-20
Image vault & gallery API scale
- Image vault past 50M+ ready WebP assets with CDN served_url on gallery endpoint.
- Listings annotate ready vault URLs when available.
- Encar async/sync image prefetch endpoints for on-demand boost.
2026-03feature2026-03-01
Multi-source public inventory
- Unified public feed across Auto1, OpenLane, Schadeautos, Copart DE.
- Normalized clean_make / clean_model / fuel_group / gearbox_group / public_price_eur.
- Archive pipeline for inactive lots (full row + archived_at).