---
title: "European classifieds"
description: "A retail price surface of roughly 9.8M live listings gathered from 681 origin portals — national classifieds sites, dealer groups and manufacturer stock pages — across 39 European countries: mobile.de, kleinanzeigen.de, autoscout24, leboncoin.fr, lacentrale.fr, autosupermarket.it, njuskalo.hr and hundreds more. Every listing keeps its origin portal in source_site, so the same feed answers a national question and a portal-level one. These are retail listings, not auction inventory: no bidding, no auction end date, no detail payload and no gallery, just a single remote thumbnail per listing. They live in their own tables with their own vocabulary (seller type, origin portal), which is why they get their own query surface instead of a site slug on /api/search. Ended and paid-placement (\"promoted\") rows are excluded from every response, and all three endpoints return 503 while the dataset has not been built yet. Scope: theparking — that is the scope name in the path, not the name of a source."
canonical: "https://thecarapi.com/docs/theparking"
contract_version: "2026-08-19"
api_base: "https://api.thecarapi.com"
source: "https://thecarapi.com/docs/theparking.md"
---

# European classifieds

A retail price surface of roughly 9.8M live listings gathered from 681 origin portals — national classifieds sites, dealer groups and manufacturer stock pages — across 39 European countries: mobile.de, kleinanzeigen.de, autoscout24, leboncoin.fr, lacentrale.fr, autosupermarket.it, njuskalo.hr and hundreds more. Every listing keeps its origin portal in source_site, so the same feed answers a national question and a portal-level one. These are retail listings, not auction inventory: no bidding, no auction end date, no detail payload and no gallery, just a single remote thumbnail per listing. They live in their own tables with their own vocabulary (seller type, origin portal), which is why they get their own query surface instead of a site slug on /api/search. Ended and paid-placement ("promoted") rows are excluded from every response, and all three endpoints return 503 while the dataset has not been built yet. Scope: theparking — that is the scope name in the path, not the name of a source.

## Calling it end to end

1. Call GET /api/theparking/facets once and cache it. It hands you every valid country code, brand, fuel, gearbox, seller type and origin portal, each with a live count, plus the price and year bounds for your sliders. It is precomputed by the nightly sweep and reads back in ~10 ms, so caching it costs you nothing in freshness. Never hard-code these values — brands are published verbatim by each portal.
2. When the user picks a brand, call GET /api/theparking/models?brand=BMW,Audi to fill the model select. Models come back most common first, capped at 400.
3. Query GET /api/theparking/listings with the chosen filters. Multi-value filters are comma-separated: they OR within a parameter and AND across parameters. Page with limit/offset or page/page_size — limit caps at 100. If you do not need a match count, pass include_total=false: it is the fastest way to page this feed.
4. Render each row directly. There is no detail endpoint and no gallery: the listing object you receive is the entire record, with one remote thumbnail in image_url and a deep link out in offer_url.
5. To use it as a market reference, query the same brand, model, and year window you are about to bid on, read the price_eur spread by seller_type, and diff it against the auction lot’s public_price_eur.

## GET /api/theparking/listings

Query retail classifieds aggregated from portals across Europe.

**Scope:** `theparking`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `country` | Query string | string | no | `de,at` | /api/theparking/facets -> countries[].value | Comma-separated country codes, case-insensitive. |
| `brand` | Query string | string | no | `BMW,Audi` | /api/theparking/facets -> brands[].value | Comma-separated brands, exact as published. |
| `model` | Query string | string | no | `3 Series` | /api/theparking/models?brand=BMW -> value | Comma-separated models. |
| `fuel` | Query string | string | no | `diesel` | /api/theparking/facets -> fuels[].value | Comma-separated normalized fuels. |
| `gearbox` | Query string | string | no | `automatic` | /api/theparking/facets -> gearboxes[].value | Comma-separated normalized gearboxes. |
| `seller` | Query string | string | no | `dealer` | /api/theparking/facets -> sellers[].value | Comma-separated seller types, such as dealer or private. |
| `source` | Query string | string | no | `mobile.de` | /api/theparking/facets -> sources[].value | Comma-separated origin portals, case-insensitive. Unlike site on the auction side, an unrecognised portal is not an error — this vocabulary is drawn from the data itself rather than fixed, so an unknown value simply matches nothing. |
| `source_exclude` | Query string | string | no | `mobile.de` | /api/theparking/facets -> sources[].value | Comma-separated origin portals to leave out, case-insensitive. With ~681 portals in the vocabulary this is the only practical way to say “everything but”. A listing with no recorded portal survives an exclusion. |
| `price_from / price_to` | Query string | integer | no | `5000 / 15000` | EUR budget chosen by your user. | EUR bounds. |
| `year_from / year_to` | Query string | integer | no | `2016 / 2022` | Year bounds chosen by your user. | Year bounds. |
| `kilometers_from / kilometers_to` | Query string | integer | no | `0 / 200000` | Mileage bounds chosen by your user. | Mileage bounds. |
| `q` | Query string | string | no | `touring` | Free text entered by your user. | Substring match on title and description. |
| `with_photo` | Query string | boolean | no | `true` | Your filter choice. | Only listings that carry a thumbnail. |
| `sort` | Query string | enum | no | `price_low` | Choose newest, price_low, price_high, year_new, year_old, or mileage_low. | Default newest. |
| `limit / offset` | Query string | integer | no | `50 / 0` | Pagination state in your application. | Or page_size / page. limit caps at 100. |
| `include_total` | Query string | boolean | no | `false` | Your pagination strategy. | Default true. Pass false to skip the match count entirely — total comes back null and the request is markedly faster. |

### Example requests

```http
GET https://api.thecarapi.com/api/theparking/listings?country=de,at&brand=BMW&price_to=15000
```

```http
GET https://api.thecarapi.com/api/theparking/listings?seller=dealer&source=mobile.de&sort=price_low&with_photo=true
```

```http
GET https://api.thecarapi.com/api/theparking/listings?country=de&source_exclude=mobile.de,kleinanzeigen.de&include_total=false
```

### Example response

```json
{
  "success": true,
  "listings": [
    {
      "reference_id": "tp-91744022",
      "title": "BMW 320d Touring",
      "brand": "BMW",
      "model": "3 Series",
      "engine": "320d",
      "year": 2016,
      "price_eur": 12500.5,
      "mileage_km": 180000,
      "fuel_norm": "diesel",
      "gearbox_norm": "automatic",
      "colour": "black",
      "doors": "5",
      "country": "Germany",
      "country_code": "de",
      "region": "Bayern",
      "seller_type": "dealer",
      "source_site": "mobile.de",
      "photo_count": 12,
      "offer_url": "https://www.theparking.eu/...",
      "image_url": "https://img.leparking.fr/...",
      "published": "2026-07-30",
      "first_seen_at": "2026-07-30T04:11:02",
      "last_seen_at": "2026-08-05T04:09:55"
    }
  ],
  "total": 4412,
  "total_capped": false,
  "total_unavailable": false,
  "limit": 50,
  "offset": 0,
  "total_pages": 89
}
```

### Response fields

| Field | Type | Meaning |
| --- | --- | --- |
| `reference_id` | string | Stable identity for the listing, prefixed tp-. Use it to de-duplicate across polls. |
| `title` | string | Listing headline as published by the origin portal. |
| `brand / model / engine` | string | Brand and model as published, plus the engine or trim string when the portal supplies one. |
| `year` | integer | Registration year. |
| `price_eur` | number | Retail asking price in EUR. This is an asking price, not a transaction price. |
| `mileage_km` | integer | Odometer reading in kilometres. |
| `fuel_norm` | string | Normalized fuel, e.g. diesel, petrol, electric, hybrid. Matches the fuel filter vocabulary. |
| `gearbox_norm` | string | Normalized gearbox, e.g. automatic, manual. Matches the gearbox filter vocabulary. |
| `colour` | string | Exterior colour as published. |
| `doors` | string | Door count as published by the portal. |
| `country / country_code` | string | Country the car is listed in — display name and lower-case code. country_code is what the country filter accepts. |
| `region` | string | Sub-national region or state when the portal publishes one. |
| `seller_type` | string | dealer or private. The single most useful axis for separating trade from retail asking prices. |
| `source_site` | string | Origin portal the listing came from, e.g. mobile.de. Matches the source / source_exclude filter vocabulary, ~681 values wide. This is the attribution that makes a country figure readable — half of all German inventory is one portal. |
| `photo_count` | integer | How many photos the origin listing carries. Only one thumbnail is exposed here. |
| `offer_url` | string | Deep link out to the listing, routed through the aggregator that indexes it — the URL is a theparking.eu one, not a direct link on the origin portal. Read source_site, not this URL, to know which portal the listing belongs to. |
| `image_url` | string | Single remote thumbnail on the aggregator’s own CDN. Not copied into our image vault — treat it as a hotlink that disappears with the listing. |
| `published` | date | Date the origin portal published the listing. |
| `first_seen_at / last_seen_at` | timestamp | When our sweep first and most recently observed the listing. Use last_seen_at to judge staleness. |
| `total / limit / offset / total_pages` | integer | Standard pagination envelope for the matching set. total is exact for ordinary filters, and null when total_unavailable is true. |
| `total_capped` | boolean | True when counting stopped at 100,000. Read total as “100,000+” and page by offset rather than trusting total_pages. An unfiltered total is precomputed and never capped. |
| `total_unavailable` | boolean | True when total is null because the count did not finish inside its budget — typically a q= search or a rare source. The listings themselves are complete and correct; only the count is missing, and the verdict is cached for 5 minutes. |

**Notes.** Every multi-value filter is OR within the parameter and AND across parameters — country=de,at&brand=BMW,Audi means (DE or AT) and (BMW or Audi). A non-numeric value on a numeric parameter is a 400, not a dropped filter. Counting is bounded rather than exhaustive on a feed this size: read total together with total_capped and total_unavailable, or skip it with include_total=false. There is no detail endpoint for these rows: what you see here is the whole record. Returns 503 while the dataset has not been built yet.

## GET /api/theparking/facets

Counted filter vocabulary for the classifieds dataset.

**Scope:** `theparking`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `limit` | Query string | integer | no | `0` | How many values your picker can hold. | Values returned per dimension, default 50. 0 returns all of them — the only way to get the full 681-portal sources list. |

### Example requests

```http
GET https://api.thecarapi.com/api/theparking/facets
```

```http
GET https://api.thecarapi.com/api/theparking/facets?limit=0
```

```bash
curl -H "X-API-Key: $API_KEY" "https://api.thecarapi.com/api/theparking/facets"
```

### Example response

```json
{
  "success": true,
  "countries": [
    {
      "value": "de",
      "count": 184220
    },
    {
      "value": "fr",
      "count": 151077
    }
  ],
  "brands": [
    {
      "value": "BMW",
      "count": 41288
    }
  ],
  "fuels": [
    {
      "value": "diesel",
      "count": 302914
    }
  ],
  "gearboxes": [
    {
      "value": "automatic",
      "count": 188402
    }
  ],
  "sellers": [
    {
      "value": "dealer",
      "count": 402881
    }
  ],
  "sources": [
    {
      "value": "mobile.de",
      "count": 1109971
    },
    {
      "value": "leboncoin.fr",
      "count": 658810
    }
  ],
  "value_counts": {
    "countries": 39,
    "brands": 118,
    "fuels": 7,
    "gearboxes": 3,
    "sellers": 2,
    "sources": 681
  },
  "totals": {
    "listings": 9808689,
    "total_capped": false,
    "price_min": 300,
    "price_max": 480000,
    "year_min": 1970,
    "year_max": 2026
  }
}
```

### Response fields

| Field | Type | Meaning |
| --- | --- | --- |
| `countries` | {value, count}[] | Every country code present in the dataset with its live listing count. This is the list to render a country picker from — it shows how wide the European coverage actually is. |
| `brands` | {value, count}[] | Brands exactly as published, with counts. Feed a value straight back into the brand filter or into /api/theparking/models. |
| `fuels` | {value, count}[] | Normalized fuel vocabulary with counts. These are the only values the fuel filter accepts. |
| `gearboxes` | {value, count}[] | Normalized gearbox vocabulary with counts. |
| `sellers` | {value, count}[] | Seller types with counts, typically dealer and private. |
| `sources` | {value, count}[] | Origin portals with counts, most common first — the list of classifieds sites, dealer groups and manufacturer stock pages currently aggregated, and the vocabulary the source / source_exclude filters accept. There are ~681 of them, so the default trim of 50 is a leaderboard rather than a picker; pass limit=0 for the whole set. |
| `value_counts` | object | How many distinct values each dimension really has, before the per-dimension trim. Check value_counts.sources against the length of sources to know whether you are looking at all of them. |
| `totals` | object | Dataset-level summary: active listing count plus price and year bounds (price_min, price_max, year_min, year_max). Use it to seed slider ranges. totals.total follows the same capping rule as the listings feed and carries its own totals.total_capped. |

**Notes.** This is the discovery endpoint for every other classifieds filter — always build selects from here rather than hard-coding values. The vocabulary is precomputed by the nightly sweep and read back in ~10 ms, so it is warm as soon as the data changes and no request pays to compute it; a dimension not yet precomputed is counted on demand and cached for an hour. Returns 503 while the dataset has not been built yet.

## GET /api/theparking/models

List models available for one or more brands.

**Scope:** `theparking`

### Parameters

| Parameter | Send in | Type | Required | Example | Where the value comes from | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `brand` | Query string | string | yes | `BMW,Audi` | /api/theparking/facets -> brands[].value | Comma-separated brand list. Required. |

### Example requests

```http
GET https://api.thecarapi.com/api/theparking/models?brand=BMW
```

```http
GET https://api.thecarapi.com/api/theparking/models?brand=BMW,Audi
```

### Example response

```json
{
  "success": true,
  "models": [
    {
      "value": "3 Series",
      "count": 8841
    },
    {
      "value": "5 Series",
      "count": 6002
    }
  ]
}
```

### Response fields

| Field | Type | Meaning |
| --- | --- | --- |
| `models` | {value, count}[] | Model names available for the requested brands, most common first, capped at 400. value goes straight back into the model filter on /api/theparking/listings. |

**Notes.** brand is required — calling without it returns 400. Because brands are published verbatim by each portal, always take the brand value from /api/theparking/facets rather than typing it. Returns 503 while the dataset has not been built yet.
