Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: TheCarApi — Public Filter facets API
version: '2026-08-19'
summary: Multi-source vehicle auction inventory API
description: 'Normalized vehicle auction inventory from Auto1, OpenLane, Schadeautos, Copart Germany,
eCarsTrade, Encar and the Japanese auction houses, plus a European retail classifieds network.
A vehicle is addressed by the pair site_name + auction_id_str, for example encar/38112900.
Primary envelopes carry contract_version, request_id, server_time and data_updated_at; a handful of routes omit those.
Price fields are JSON numbers, never strings.
Full documentation: https://thecarapi.com/docs'
contact:
name: TheCarApi API support
email: api@thecarapi.com
url: https://thecarapi.com/contact
termsOfService: https://thecarapi.com/terms
servers:
- url: https://api.thecarapi.com
description: Production
security:
- apiKeyHeader: []
- bearerAuth: []
tags:
- name: Filter facets
description: Discover live filter values and counts. Every facet endpoint accepts the search filters, so a facet describes whatever slice of inventory you are looking at rather than the whole of it — each dimension ignoring only its own filter, so a user can switch brands without the option disappearing. Counts and the totals /api/search reports for the same filters cannot disagree. Facet responses cache for about 600 seconds, and a filter sidebar can read every flat dimension in one call with /api/facets.
externalDocs:
url: https://thecarapi.com/docs/facets
paths:
/api/facets:
get:
operationId: get_api_facets
summary: Every flat facet dimension in one request instead of six
description: 'Every flat facet dimension in one request instead of six.
Every filter accepted by the individual facet endpoints applies here, per field, exactly as its own endpoint would apply it — including the cross-filtering, so ?price_to=5000&fields=brands returns each brand count under EUR 5,000 and value lists omit options with no matches left. is_active and include_ended are not accepted: facets always describe lots whose auction is still open. The per-dimension endpoints below are unchanged and stay fully supported — this is an additional way to read the same data. Scope search, the same scope that already grants them, and the fan-out bills one quota unit rather than six.
Response fields — brands / years / fuels / countries / gearboxes / sites (array): Each field is its own endpoint’s payload merged verbatim — countries still ships country_details alongside it, and no field gains or loses counts by being requested here. errors (object): Present only when a dimension could not be served: { field: reason }. Every other field is still returned, so one slow dimension cannot blank a filter sidebar.'
tags:
- Filter facets
security:
- apiKeyHeader: []
- bearerAuth: []
x-scope: search
x-examples:
- GET https://api.thecarapi.com/api/facets?fields=brands,fuels,gearboxes&country=DE
- GET https://api.thecarapi.com/api/facets?fields=brands,years,sites&damaged=true
parameters:
- name: fields
in: query
required: false
description: 'Comma-separated subset of brands, years, fuels, countries, gearboxes, sites. Defaults to all of them. models is not available here — it is brand-scoped and stays at /api/models. Value comes from: The dimensions your sidebar actually renders.'
schema:
type: string
example: brands,fuels,gearboxes
- name: brand
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: model
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: year_from
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: year_to
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: fuel
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: gearbox
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: country
in: query
required: false
description: 'ISO country code or europe. Accepted and ignored by /api/sites and /load-models. Value comes from: /api/countries -> country_details[].code'
schema:
type: string
example: DE
- name: site
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: site_exclude
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: kilometers_from
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: kilometers_to
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: horsepower_from
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: horsepower_to
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: price_from
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: price_to
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: vehicle_type
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: body_style
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: steering
in: query
required: false
description: 'Restrict to left- or right-hand-drive vehicles. Slower than an unfiltered facet call, because it is computed live rather than read from the precomputed facet table. Value comes from: left | lhd | left-hand | left_hand, or right | rhd | right-hand | right_hand — any other value is a 400.'
schema:
type: string
example: left
- name: damaged
in: query
required: false
description: 'Restrict to damaged or broken vehicles. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: buy_now
in: query
required: false
description: 'Restrict to Buy-Now inventory. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: ending_soon
in: query
required: false
description: 'Restrict to lots ending within about two hours. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
example:
success: true
brands:
- id: 12
name: BMW
slug: bmw
count: 3266
fuels:
- Diesel
- Petrol
gearboxes:
- Automatic
- Manual
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
/api/brands:
get:
operationId: get_api_brands
summary: List brands with round-trippable slugs and live inventory counts
description: 'List brands with round-trippable slugs and live inventory counts.
The slug round-trips: send it back as ?brand= to /api/search or /api/models. Each brand appears once, keyed on its identity rather than on the spelling a source happened to use, and its count is the whole count for that brand — never a per-spelling fraction of it. The list is safe to render straight into a dropdown without de-duplicating.'
tags:
- Filter facets
security:
- apiKeyHeader: []
- bearerAuth: []
x-scope: search
x-examples:
- GET https://api.thecarapi.com/api/brands?search=bm&ordering=-count&limit=20
- GET https://api.thecarapi.com/api/brands?country=DE&damaged=true
parameters:
- name: brand
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: model
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: year_from
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: year_to
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: fuel
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: gearbox
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: country
in: query
required: false
description: 'ISO country code or europe. Accepted and ignored by /api/sites and /load-models. Value comes from: /api/countries -> country_details[].code'
schema:
type: string
example: DE
- name: site
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: site_exclude
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: kilometers_from
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: kilometers_to
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: horsepower_from
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: horsepower_to
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: price_from
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: price_to
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: vehicle_type
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: body_style
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: steering
in: query
required: false
description: 'Restrict to left- or right-hand-drive vehicles. Slower than an unfiltered facet call, because it is computed live rather than read from the precomputed facet table. Value comes from: left | lhd | left-hand | left_hand, or right | rhd | right-hand | right_hand — any other value is a 400.'
schema:
type: string
example: left
- name: damaged
in: query
required: false
description: 'Restrict to damaged or broken vehicles. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: buy_now
in: query
required: false
description: 'Restrict to Buy-Now inventory. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: ending_soon
in: query
required: false
description: 'Restrict to lots ending within about two hours. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: search
in: query
required: false
description: 'Substring filter on brand name. Value comes from: Free text entered by your user.'
schema:
type: string
example: bm
- name: ordering
in: query
required: false
description: 'name, -name, count, or -count. An unrecognised value falls back to name rather than erroring. Value comes from: Choose name, -name, count, or -count.'
schema:
type: string
example: -count
- name: limit
in: query
required: false
description: 'Maximum number of values. No default — the full brand list is returned unless you cap it, which is what a filter sidebar wants. Value comes from: Result count chosen by your application.'
schema:
type: integer
example: '20'
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
example:
success: true
brands:
- id: 12
name: BMW
slug: bmw
count: 1543
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
/api/models:
get:
operationId: get_api_models
summary: List models for one brand with live inventory counts
description: 'List models for one brand with live inventory counts.
brand is required — calling without it returns 400. Each model appears once, merged on the model identity rather than its printed label, so a model spelled "GOLF" by one source and "Golf" by another is one row carrying the whole count.'
tags:
- Filter facets
security:
- apiKeyHeader: []
- bearerAuth: []
x-scope: search
x-examples:
- GET https://api.thecarapi.com/api/models?brand=bmw&ordering=-count
- GET https://api.thecarapi.com/api/models?brand=12&search=x&country=DE
parameters:
- name: brand
in: query
required: true
description: 'Brand name, slug, or brand_id. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: brand
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: model
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: year_from
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: year_to
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: fuel
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: gearbox
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: country
in: query
required: false
description: 'ISO country code or europe. Accepted and ignored by /api/sites and /load-models. Value comes from: /api/countries -> country_details[].code'
schema:
type: string
example: DE
- name: site
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: site_exclude
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: kilometers_from
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: kilometers_to
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: horsepower_from
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: horsepower_to
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: price_from
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: price_to
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: vehicle_type
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: body_style
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: steering
in: query
required: false
description: 'Restrict to left- or right-hand-drive vehicles. Slower than an unfiltered facet call, because it is computed live rather than read from the precomputed facet table. Value comes from: left | lhd | left-hand | left_hand, or right | rhd | right-hand | right_hand — any other value is a 400.'
schema:
type: string
example: left
- name: damaged
in: query
required: false
description: 'Restrict to damaged or broken vehicles. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: buy_now
in: query
required: false
description: 'Restrict to Buy-Now inventory. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: ending_soon
in: query
required: false
description: 'Restrict to lots ending within about two hours. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: search
in: query
required: false
description: 'Substring filter on model name. Value comes from: Free text entered by your user.'
schema:
type: string
example: x
- name: ordering
in: query
required: false
description: 'name, -name, count, or -count. An unrecognised value falls back to name rather than erroring. Value comes from: Choose name, -name, count, or -count.'
schema:
type: string
example: -count
- name: limit
in: query
required: false
description: 'Maximum number of values. No default — the full model list for the brand is returned unless you cap it. Value comes from: Result count chosen by your application.'
schema:
type: integer
example: '20'
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
example:
success: true
models:
- name: 320d
slug: 320d
count: 210
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
/api/years:
get:
operationId: get_api_years
summary: List registration years available in current inventory
description: 'List registration years available in current inventory.
Response fields — years (integer[]): Registration years present in the filtered inventory, newest first. year_counts ({value, count}[]): Mirrors years in the same order. Each count is the offers for that registration year under the request other filters.'
tags:
- Filter facets
security:
- apiKeyHeader: []
- bearerAuth: []
x-scope: search
x-examples:
- GET https://api.thecarapi.com/api/years?country=DE
- GET https://api.thecarapi.com/api/years?damaged=true&buy_now=true
parameters:
- name: brand
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: model
in: query
required: false
description: 'Name, slug, or brand_id, exactly as on /api/search. Value comes from: /api/brands -> brands[].slug (or id/name)'
schema:
type: string
example: bmw
- name: year_from
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: year_to
in: query
required: false
description: 'Registration-year range. Accepted and ignored by /api/years itself, which is the dimension they would narrow. Value comes from: /api/years -> years[]'
schema:
type: integer
example: '2018'
- name: fuel
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: gearbox
in: query
required: false
description: 'Canonical group, matched whole. Value comes from: /api/fuels -> fuels[], /api/gearboxes -> gearboxes[]'
schema:
type: string
example: Diesel
- name: country
in: query
required: false
description: 'ISO country code or europe. Accepted and ignored by /api/sites and /load-models. Value comes from: /api/countries -> country_details[].code'
schema:
type: string
example: DE
- name: site
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: site_exclude
in: query
required: false
description: 'Comma-separated source slugs to include or leave out. Value comes from: /api/sites -> sites[].name'
schema:
type: string
example: openlane
- name: kilometers_from
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: kilometers_to
in: query
required: false
description: 'Mileage range. Value comes from: Mileage bounds chosen by your user.'
schema:
type: integer
example: '0'
- name: horsepower_from
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: horsepower_to
in: query
required: false
description: 'Power range in HP. Value comes from: Power bounds chosen by your user.'
schema:
type: integer
example: '150'
- name: price_from
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: price_to
in: query
required: false
description: 'Public EUR price range. price_to=5000 on /api/brands returns each brand count under EUR 5,000. Value comes from: EUR budget chosen by your user.'
schema:
type: integer
example: '0'
- name: vehicle_type
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: body_style
in: query
required: false
description: 'Vehicle-type and body-style filters. Value comes from: As on /api/search.'
schema:
type: string
example: cars
- name: steering
in: query
required: false
description: 'Restrict to left- or right-hand-drive vehicles. Slower than an unfiltered facet call, because it is computed live rather than read from the precomputed facet table. Value comes from: left | lhd | left-hand | left_hand, or right | rhd | right-hand | right_hand — any other value is a 400.'
schema:
type: string
example: left
- name: damaged
in: query
required: false
description: 'Restrict to damaged or broken vehicles. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: buy_now
in: query
required: false
description: 'Restrict to Buy-Now inventory. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
- name: ending_soon
in: query
required: false
description: 'Restrict to lots ending within about two hours. Value comes from: Your filter choice.'
schema:
type: boolean
example: 'true'
responses:
'200':
description: Success.
content:
application/json:
schema:
type: object
example:
success: true
years:
- 2024
- 2023
- 2022
- 2021
year_counts:
- value: 2024
count: 512
- value: 2023
count: 4127
- value: 2022
count: 3890
- value: 2021
count: 3544
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/
# --- truncated at 32 KB (70 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/thecarapi/refs/heads/main/openapi/thecarapi-filter-facets-api-openapi.yml