Market intelligence
What a car is worth, and the price snapshots behind it. /api/market/valuation answers for a make and model worldwide and per country, from retail, auction, unsold and buy-now prices, with trends, time to sell and a price model’s estimate. The two snapshots cover a brand, model and year window — one for the Bulgarian retail market, one for our own auction inventory. Scope: market, granted per contract; none of the three is enabled on a new key by default. All three are read-only lookups into data built nightly or on the pipeline’s own schedule. On the two snapshots a 404 means "no snapshot exists for that window", the normal answer for a thin brand/model/year combination — not an error to retry — and neither snapshot route carries envelope metadata; read X-Request-ID from the header.
What a make and model is worth — retail, auction, unsold and buy-now prices, worldwide and per country.
Requires the market scope.
Parameters
brand / makestring · query stringrequiredBrand name, under either spelling.
Example BMW·Where the value comes from /api/brands -> brands[].name
modelstring · query stringrequiredModel name.
Example 320d·Where the value comes from /api/models?brand=bmw -> models[].name
yearinteger · query stringExact registration year for the exact block; the range block widens it by year_flex.
Example 2019·Where the value comes from The car being valued.
year_flexinteger · query stringYears either side of year for the range block. Default 1, maximum 10.
Example 1·Where the value comes from How wide a range you want.
year_from / year_tointeger · query stringRange bounds when year is not given.
Example 2018 / 2021·Where the value comes from Year bounds chosen by your user.
fuelstring · query stringPlug-in and mild hybrids count as Hybrid.
Example Diesel·Where the value comes from Petrol, Diesel, Hybrid, Electric, LPG, CNG or Hydrogen.
gearboxstring · query stringGearbox.
Example Automatic·Where the value comes from Automatic or Manual.
enginenumber · query stringDisplacement in litres.
Example 2.0·Where the value comes from The car being valued.
engine_flexnumber · query stringLitres either side of engine for the range block. Default 0.2.
Example 0.2·Where the value comes from How wide a range you want.
conditionenum · query stringnormal (default) excludes damaged lots, damaged keeps only them, any pools both. Classifieds are always normal.
Example normal·Where the value comes from normal, damaged or any.
mileageinteger · query stringAdds mileage_adjusted_eur — the median moved along the group’s own price/mileage slope, capped at ±35% — and sets the mileage of prediction.
Example 120000·Where the value comes from The car’s odometer in km.
countrystring · query stringNarrows exact, range, trend and time_to_sell. worldwide and countries always cover every country.
Example DE·Where the value comes from ISO 3166-1 alpha-2 country of the car.
kindsstring · query stringComma-separated subset of price kinds to return.
Example retail,auction·Where the value comes from retail, auction, buy_now, unsold.
trend_monthsinteger · query stringMonths of trend history including the current one. Default and maximum 12.
Example 12·Where the value comes from How much history to chart.
trend_weeksinteger · query stringWeeks of weekly trend history. Default 12, maximum 26.
Example 12·Where the value comes from How much history to chart.
Example requests
GET https://api.thecarapi.com/api/market/valuation?brand=BMW&model=320d&year=2019&fuel=Diesel&mileage=120000GET https://api.thecarapi.com/api/market/valuation?brand=Toyota&model=Corolla&year_from=2018&year_to=2021&country=DE&kinds=retail,auctionExample response
{
"success": true,
"valuation": {
"vehicle": {
"brand": "BMW",
"model": "320d",
"year": 2019,
"year_from": 2018,
"year_to": 2020,
"fuel": "Diesel",
"condition": "normal",
"mileage_km": 120000,
"country": null
},
"exact": {
"retail": {
"count": 640,
"median_eur": 21900,
"p25_eur": 19800,
"p75_eur": 24100,
"p10_eur": 17500,
"p90_eur": 26900,
"mean_mileage_km": 98000
},
"auction": {
"count": 212,
"median_eur": 16400,
"p25_eur": 14100,
"p75_eur": 18900,
"p10_eur": 12200,
"p90_eur": 21000,
"mean_mileage_km": 131000
}
},
"range": {},
"worldwide": {},
"countries": [
{
"country_code": "de",
"retail": {
"count": 410,
"median_eur": 21200
},
"auction": {
"count": 96,
"median_eur": 16100
},
"count": 506,
"time_to_sell": null
}
],
"ranking": {
"retail": {
"cheapest": [
{
"country_code": "pl",
"count": 88,
"median_eur": 18900
}
],
"most_expensive": [
{
"country_code": "ch",
"count": 41,
"median_eur": 27400
}
]
}
},
"trend": {},
"time_to_sell": null,
"price_bands": {},
"prediction": {
"estimates": {},
"accuracy": {}
},
"methodology": {},
"built": null
},
"contract_version": "2026-10-05"
}What you get back
exact / rangeobjectPer price kind, a summary of count, median_eur, p10/p25/p75/p90_eur and mean_mileage_km (plus mileage_adjusted_eur when mileage is sent), or null when no car matched. Kinds: retail (classified asking prices in Europe, Korea and China), auction (closing price of finished lots — the published result where the source has one, else the last bid seen), unsold (closing prices the car did not sell at) and buy_now (B2B buy-now prices). exact applies year and engine as given; range widens them.
worldwide / countries / rankingobjectworldwide is the same summary across every country; countries[] has count and median per kind for each country_code; ranking.<kind> lists the cheapest and most_expensive markets (countries with at least 5 observations).
trendobjectPer kind: weekly and change_pct (latest four weeks against the four before), monthly and monthly_change_pct (latest three months against the three before).
time_to_sellobject | nullOverall and per country, for retail and auction: Kaplan-Meier median days to sell (median_days, or over_days while over half are unsold), sold_within_pct within sold_within_days, sold and listings. Retail measures until a classified left the market, which is not proof of a sale. Null below 5 sales.
price_bandsobjectFor retail and auction, five bands around that kind’s median (under -15%, -15..-5%, ±5%, +5..+15%, over +15%) with the time to sell at that price, and for auctions closes and sold_pct.
predictionobjectA price model’s estimate, available even where few or no cars match: estimates.<kind> with estimate_eur, range_50_eur and range_80_eur (where an individual car’s price falls), basis (model, blended or comparables), per_year_older_pct, per_10k_km_pct, by_mileage and by_year. accuracy.<kind> reports held-out test error.
builtobject | nullWhen the nightly build that answered this ran.
Cars.bg Bulgarian retail market snapshot.
Requires the market scope.
Parameters
brand / makestring · query stringrequiredBrand name. Either spelling. Resolved through a fuzzy brand matcher, so display names work.
Example BMW·Where the value comes from /api/brands -> brands[].name
modelstring · query stringrequiredModel name. Resolved to one or more catalogue model ids.
Example 320d·Where the value comes from /api/models?brand=bmw -> models[].name
yearinteger · query stringrequiredThe centre year.
Example 2019·Where the value comes from Centre year chosen by your application.
flexinteger · query stringYear tolerance ± (0–10, default 1). A value outside that range is a 400.
Example 2·Where the value comes from Tolerance chosen by your application.
Example requests
GET https://api.thecarapi.com/api/cars-bg-market?brand=BMW&model=320d&year=2019GET https://api.thecarapi.com/api/cars-bg-market?make=Audi&model=A4&year=2020&flex=2Example response
{
"success": true,
"snapshot": {
"brand_id": "12",
"model_ids": [
"4411"
],
"year_center": 2019,
"year_flex": 2,
"year_from": 2017,
"year_to": 2021,
"listing_count": 84,
"avg_price_eur": 24310.5,
"median_price_eur": 23900,
"min_price_eur": 15500,
"max_price_eur": 41000,
"offers": [],
"raw_matches": [],
"computed_at": "2026-08-27T02:14:11",
"updated_at": "2026-08-27T02:14:11"
}
}What you get back
year_from / year_tointegerThe window year ± flex actually resolved to.
listing_countintegerHow many retail listings the aggregate is built from. Read this first — a median over four cars is not a market price.
avg_price_eur / median_price_eur / min_price_eur / max_price_eurnumberEUR aggregates as JSON numbers. Prefer the median; retail asking prices have a long right tail.
offersobject[]The listings behind the aggregate.
raw_matchesobject[]Everything the model matcher considered, including rows the aggregate excluded. Useful for auditing a surprising number.
computed_at / updated_attimestampWhen the snapshot was built. It is not recomputed per request.
Auction-market price snapshot for the same brand, model, and year window.
Requires the market scope.
Parameters
brand / makestring · query stringrequiredBrand name. Matched against the normalized clean_make.
Example BMW·Where the value comes from /api/brands -> brands[].name
modelstring · query stringrequiredModel name. Matched against clean_model.
Example 320d·Where the value comes from /api/models?brand=bmw -> models[].name
yearinteger · query stringrequiredThe centre year.
Example 2019·Where the value comes from Centre year chosen by your application.
flexinteger · query stringYear tolerance ± (0–10, default 1).
Example 2·Where the value comes from Tolerance chosen by your application.
scopeenum · query stringDefault all. Anything else is a 400. Each is a separately built snapshot, so active can exist while inactive does not.
Example active·Where the value comes from Choose all, active, or inactive.
Example requests
GET https://api.thecarapi.com/api/auction-market?brand=BMW&model=320d&year=2019&scope=activeGET https://api.thecarapi.com/api/auction-market?brand=Kia&model=EV6&year=2023&flex=1Example response
{
"success": true,
"snapshot": {
"clean_make": "BMW",
"clean_model": "320d",
"year_center": 2019,
"year_flex": 1,
"year_from": 2018,
"year_to": 2020,
"scope": "all",
"auction_count": 412,
"avg_price_eur": 18220.4,
"median_price_eur": 17800,
"p10_price_eur": 11900,
"p25_price_eur": 14750,
"p75_price_eur": 21400,
"p90_price_eur": 26100,
"min_price_eur": 6200,
"max_price_eur": 44900,
"first_seen_min": "2026-01-04T00:00:00",
"first_seen_max": "2026-08-22T00:00:00",
"auctions": [],
"computed_at": "2026-08-27T02:41:03",
"updated_at": "2026-08-27T02:41:03"
}
}What you get back
scopeenumEchoes the request: active restricts to lots still open, inactive to closed ones, all covers both.
auction_countintegerLots behind the aggregate. Sanity-check it before quoting a percentile.
p10_price_eur / p25_price_eur / p75_price_eur / p90_price_eurnumberPercentiles — the reason to prefer this over the Cars.bg snapshot when you want a spread rather than a point estimate. A wide p10–p90 band means the specification is not pinning the price down.
first_seen_min / first_seen_maxtimestampThe observation window the lots span. A snapshot whose window is eight months wide is not a spot price.
auctionsobject[]The individual lots the aggregate was computed from.