contract 2026-09-03
Endpoints

Market intelligence

Precomputed price snapshots for a brand, model, and year window — one for the Bulgarian retail market, one for our own auction inventory. Scope: market. Neither is enabled on a new key by default; ask for them. Both are read-only lookups into snapshots built by a pipeline on its own schedule. A 404 therefore means "no snapshot exists for that window", which is the normal answer for a thin brand/model/year combination — not an error to retry. Neither route carries envelope metadata; read X-Request-ID from the header.

GET

/api/cars-bg-market

Cars.bg Bulgarian retail market snapshot.

Requires the market scope.

Parameters

brand / makestring · query stringrequired

Brand 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 stringrequired

Model name. Resolved to one or more catalogue model ids.

Example 320d·Where the value comes from /api/models?brand=bmw -> models[].name

yearinteger · query stringrequired

The centre year.

Example 2019·Where the value comes from Centre year chosen by your application.

flexinteger · query string

Year 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

Request
GET https://api.thecarapi.com/api/cars-bg-market?brand=BMW&model=320d&year=2019
GET https://api.thecarapi.com/api/cars-bg-market?make=Audi&model=A4&year=2020&flex=2

Example response

json
{
  "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_tointeger

The window year ± flex actually resolved to.

listing_countinteger

How 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_eurnumber

EUR 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_attimestamp

When the snapshot was built. It is not recomputed per request.

GET

/api/auction-market

Auction-market price snapshot for the same brand, model, and year window.

Requires the market scope.

Parameters

brand / makestring · query stringrequired

Brand name. Matched against the normalized clean_make.

Example BMW·Where the value comes from /api/brands -> brands[].name

modelstring · query stringrequired

Model name. Matched against clean_model.

Example 320d·Where the value comes from /api/models?brand=bmw -> models[].name

yearinteger · query stringrequired

The centre year.

Example 2019·Where the value comes from Centre year chosen by your application.

flexinteger · query string

Year tolerance ± (0–10, default 1).

Example 2·Where the value comes from Tolerance chosen by your application.

scopeenum · query string

Default 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

Request
GET https://api.thecarapi.com/api/auction-market?brand=BMW&model=320d&year=2019&scope=active
GET https://api.thecarapi.com/api/auction-market?brand=Kia&model=EV6&year=2023&flex=1

Example response

json
{
  "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

scopeenum

Echoes the request: active restricts to lots still open, inactive to closed ones, all covers both.

auction_countinteger

Lots behind the aggregate. Sanity-check it before quoting a percentile.

p10_price_eur / p25_price_eur / p75_price_eur / p90_price_eurnumber

Percentiles — 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_maxtimestamp

The 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.