# AutoScout24 Price Market — True Population Price Quantiles (`jpmarketdata/autoscout24-price-market-checker`) Actor

Population price statistics for any car make/model on AutoScout24 in one call: the exact stock count and the true p10/p25/median/p75/p90 of the whole listing population, read from the site's own exact hit counts at price ceilings. Listing dumps stop at 200 pages and cannot reach these numbers.

- **URL**: https://apify.com/jpmarketdata/autoscout24-price-market-checker.md
- **Developed by:** [h ichi](https://apify.com/jpmarketdata) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 model market summaries

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## AutoScout24 Price Market — True Population Price Quantiles

**What is the median asking price of a BMW 3 Series in Germany?** There are 11,013 of them listed on AutoScout24 right now. No listing scraper on this Store can answer that question — and the reason is not effort, it is arithmetic.

**AutoScout24 stops paginating at 200 pages.** Measured 2026-08-02: `numberOfPages` returns 200 for a 4,981-car result set, 200 for an 11,013-car result set, and 200 for the 62,236-car BMW population. At ~20 cars a page that is a hard ceiling of about 4,000 listings. A dump-style Actor cannot reach the middle of an 11,013-car market **in principle**, however many pages it pays for — and the 13 AutoScout24 scrapers already on the Store are all dump-style.

`priceto=X` is a different door. It is a *search filter*, and the site reports the exact hit count for any filter, so `priceto=8600` answers "2,352 cars cost €8,600 or less" — a **population CDF**, exactly. Twelve of those requests give the real p10 / p25 / median / p75 / p90 with zero sampling error, in about 40 seconds.

That is what this Actor sells: **one compact market record per make/model**, with

- **`price`** — p10 / p25 / median / p75 / p90 of the **whole listing population**, read out of the site's own exact counts and interpolated inside population bands. Never a page sample. In EUR and USD
- **`totalFound`** — the exact live stock count, and **`pageCapReached`** telling you whether this is a market a listing dump could have reached at all
- **`ladder`** and **`bands`** — the raw CDF, so you can rebuild any quantile or histogram yourself
- **`sampleStats`** — dealer vs private split, median mileage, median registration year, fuel and gearbox mix, drawn **newest-first** (an ordering independent of price)
- Optionally every sampled listing (price, mileage, first registration, fuel, gearbox, power, dealer or private)

Eight country front-ends: **Germany, Austria, France, Italy, Spain, Netherlands, Belgium** and the English-language international site. No login, no API key, **no proxy**, nothing stored between runs.

### Pricing — from $0.02 per make/model, no subscription

| Event | Price | When |
|---|---|---|
| Make/model market summary | **$0.02** | Per make/model analyzed |
| Individual listing | **$0.002** | Only if you enable **Include individual listings** |

A default run (1 model, summary only) costs **$0.02** and makes **14 requests in about 40 seconds** — roughly 2.4 MB on the wire, since every AutoScout24 page is ~170 KB gzipped (~720 KB decompressed). Two models fit comfortably in one call; beyond that the run's time budget starts trimming the ladder and says so in the record. **A make/model that returns nothing is never charged.**

### Input

| Field | Example | Notes |
|---|---|---|
| `models` | `["bmw/3er"]` | `make` or `make/model` slugs, or a full AutoScout24 search URL. Each costs $0.02 |
| `country` | `"de"` | `de`, `at`, `fr`, `it`, `es`, `nl`, `be`, `com`. Sets the market, the domain and the currency |
| `ladderSteps` | `9` | Price ceilings to get exact counts for. One request each |
| `refineSteps` | `3` | Extra requests spent bisecting whichever band a quantile is stuck in, widest first |
| `mileageMax` | `100000` | Optional. Conditions the **whole** analysis on cars at or below this mileage |
| `firstRegistrationFrom` | `2018` | Optional. Conditions the whole analysis on cars first registered from this year |
| `maxListings` | `20` | Listings kept for the mix sample. Does **not** affect the price quantiles |
| `includeIndividualItems` | `false` | Also emit each sampled listing |
| `convertToUsd` | `true` | Adds USD stats at the current exchange rate |

`mileageMax` and `firstRegistrationFrom` condition the entire record — the stock count, the ladder and the quantiles all become "given this filter". `{"models": ["bmw/3er"], "mileageMax": 100000, "firstRegistrationFrom": 2018}` is the price distribution of a 2018-or-newer 3 Series under 100,000 km: the shape a trade-in quote or a residual-value model actually needs, and one no listing dump can produce.

**Model slugs are localised** — `bmw/3er` on de/at, `bmw/serie-3` on fr, `bmw/3-series` on it/es/nl/be/com. You do not have to know which: AutoScout24 308-redirects to its own slug and the Actor follows it *while re-attaching your filters*, which the redirect itself strips. `resolvedName` on the record tells you what the site decided you meant (`"BMW Série 3"` when `bmw/3er` is sent to France).

### The one thing to understand: counts are exact, the mix is a sample

Every block carries its own `basis` and the record never lets the two blur:

| Block | `basis` | Why |
|---|---|---|
| `price`, `ladder`, `bands`, `totalFound` | **`count-ladder`** | Each rung is a *filter*, and AutoScout24 prints the exact hit count for whatever filter you send. `priceto=17500` returning 4,410 means 4,410 cars cost €17,500 or less, full stop. The quantiles are exact at every band edge and linearly interpolated inside the band they land in |
| `sampleStats` | **`sample`** | Dealer share, mileage and age come from the ~20 listings actually fetched, with `n` stated. They are drawn `sort=age&desc=1` — newest first — because that ordering is independent of price. A price-sorted page would tell you what the *cheap end* of the market looks like, which is a different question |

If the ladder ever comes back non-monotonic, or flat (the signature of a filter the site did not apply), the Actor **does not** publish it as a population figure: it falls back to statistics over the listings it fetched, flips `priceBasis` to `"sample"`, and adds a caveat saying so. You never have to guess which kind of number you are holding.

### Output example (`type: "market_summary"`, real measured output)

BMW 3er on autoscout24.de, 2026-08-02, 14 requests, 40 seconds:

```json
{
  "type": "market_summary",
  "model": "bmw/3er",
  "make": "bmw",
  "modelSlug": "3er",
  "country": "de",
  "countryName": "Germany",
  "currency": "EUR",
  "totalFound": 11013,
  "numberOfPages": 200,
  "pageCapReached": true,
  "resolvedCategory": "ma13gr100038",
  "resolvedName": "BMW 3er",
  "price": {
    "p10": 4584, "p25": 10182, "median": 23030, "p75": 35857, "p90": 46228,
    "min": 1, "max": 1000000, "count": 11013
  },
  "priceBasis": "count-ladder",
  "ladder": [
    { "priceTo":   500, "cumulativeCount":     6, "cumulativeShare": 0.0005 },
    { "priceTo":   950, "cumulativeCount":    51, "cumulativeShare": 0.0046 },
    { "priceTo":  1800, "cumulativeCount":   221, "cumulativeShare": 0.0201 },
    { "priceTo":  3500, "cumulativeCount":   786, "cumulativeShare": 0.0714 },
    { "priceTo":  4800, "cumulativeCount":  1164, "cumulativeShare": 0.1057 },
    { "priceTo":  6700, "cumulativeCount":  1761, "cumulativeShare": 0.1599 },
    { "priceTo": 12500, "cumulativeCount":  3414, "cumulativeShare": 0.3100 },
    { "priceTo": 17500, "cumulativeCount":  4410, "cumulativeShare": 0.4004 },
    { "priceTo": 24500, "cumulativeCount":  5798, "cumulativeShare": 0.5265 },
    { "priceTo": 34000, "cumulativeCount":  7964, "cumulativeShare": 0.7231 },
    { "priceTo": 46500, "cumulativeCount":  9955, "cumulativeShare": 0.9039 },
    { "priceTo": 88500, "cumulativeCount": 10997, "cumulativeShare": 0.9985 }
  ],
  "bands": [
    { "from": null,  "to": 500,   "count": 6 },
    { "from": 500,   "to": 950,   "count": 45 },
    { "from": 950,   "to": 1800,  "count": 170 },
    { "from": 1800,  "to": 3500,  "count": 565 },
    { "from": 3500,  "to": 4800,  "count": 378 },
    { "from": 4800,  "to": 6700,  "count": 597 },
    { "from": 6700,  "to": 12500, "count": 1653 },
    { "from": 12500, "to": 17500, "count": 996 },
    { "from": 17500, "to": 24500, "count": 1388 },
    { "from": 24500, "to": 34000, "count": 2166 },
    { "from": 34000, "to": 46500, "count": 1991 },
    { "from": 46500, "to": 88500, "count": 1042 },
    { "from": 88500, "to": null,  "count": 16 }
  ],
  "sampleStats": {
    "basis": "sample",
    "sampleDesign": "newest-listings-first (sort=age), price-independent",
    "n": 17,
    "dealerCount": 8, "privateSellerCount": 9,
    "dealerShare": 0.4706, "privateShare": 0.5294,
    "medianMileageKm": 190000,
    "medianFirstRegistrationYear": 2011,
    "medianAgeYears": 15,
    "medianPrice": 11900,
    "fuelTop": [["Diesel", 10], ["Benzin", 7]],
    "transmissionTop": [["Schaltgetriebe", 9], ["Automatik", 8]],
    "excludedNonOrganic": 3
  },
  "ocsResultsReported": 212,
  "filters": {
    "mileageMaxKm": null, "firstRegistrationFrom": null,
    "damagedListings": "excluded", "vehicleState": "new+used"
  },
  "requestsMade": 14,
  "priceUsd": { "p10": 5277, "p25": 11721, "median": 26511, "p75": 41277, "p90": 53215 },
  "exchangeRateEurUsd": 1.15115,
  "sourceUrl": "https://www.autoscout24.de/lst/bmw/3er?atype=C&cy=D&damaged_listing=exclude&desc=0&powertype=kw&sort=price&ustate=N%2CU",
  "checkedAt": "2026-08-02T15:42:01Z"
}
```

Read the ladder and the size of the problem becomes obvious. Half the German 3 Series market is under €24,500 — but 5,798 cars sit below that line, and the page cap can only ever show you about 4,000. **The median is on the far side of a wall that paging cannot climb.** The same call against France returns 1,371 cars and a €26,523 median: the same car, a market 8× smaller and 15% dearer.

### What this Actor does not do

- **No personal data, ever.** AutoScout24's listing JSON *does* carry `seller.contactName` (a named individual), `seller.phones[]` and `location.street` — for private sellers too. Those fields are dropped **at the parser**, not filtered at the end, and a guard asserts no emitted record contains them. A dealer keeps its company name; a private seller keeps nothing beyond the country its car is in. This is a market-structure tool, not a lead scraper — which is exactly where the existing AutoScout24 Actors, several of which advertise "Dealer Data" and "Phone Numbers", are pointed
- **No `/lst?`.** AutoScout24's robots.txt disallows the make-less all-inventory search, so a make is always required and there is no free-text keyword mode. It also disallows both GraphQL endpoints and the lightweight count-only API, so this Actor pays for a full HTML page per count rather than take the cheap route it is not allowed to take
- **No detail-page fetching.** `/angebote/` is disallowed. Listing URLs are emitted so you can open them; they are never requested
- **No `cat=` in any URL** (`Disallow: /*?*cat=*`). The site resolves the category itself and echoes it back, which is where `resolvedCategory` comes from
- **No browser, no proxy, no stored dataset.** Plain HTTP, 256 MB, every run fetches live

### Notes on the data

- **Asking prices, not transaction prices.** Used cars turn over slowly and get repriced often; treat these as the live ask distribution
- **`min` and `max` are outliers by construction.** A measured BMW 3er is listed at €1 (flagged `priceLabel: toolow-price`) and another at €1,000,000. The ladder deliberately starts at €500 and its top rung is anchored near the 99.8th percentile rather than the maximum, so the quantiles are unaffected — but do not read `min`/`max` as market signal
- **Promoted slots are excluded from the sample.** `searchResultType: "Nfm"` entries look exactly like plain hits — `isOcs: false`, `Main` section — and only that one field gives them away. `excludedNonOrganic` reports how many were dropped. The site's default `sort=standard` is tier-weighted too: a measured page was 20 dealers out of 20, which is why every request here sends an explicit sort
- **A page renders 17-20 listings, not 20.** The count varies with how many placements the page carried, so `n` is always measured
- **No listing dates.** AutoScout24's result pages carry no publication timestamp, so no turnover or days-on-market metric is possible. `firstRegistration` gives the fleet's age distribution instead
- **Run it without a proxy.** Verified 2026-08-02 with the internal `dc-ip-probe`, using the **filtered** URL form (`&priceto=…`), not just the bare one: a plain Apify datacenter IP gets 200 with and without the datacenter proxy, at every price ceiling tested
- **Counts drift between runs.** AutoScout24's inventory changes minute to minute; the same query measured 11,008 / 11,012 / 11,013 within an hour. The ladder is internally consistent because every rung of one run is fetched within ~30 seconds

# Actor input Schema

## `models` (type: `array`):

One or more 'make' or 'make/model' slugs as they appear in an AutoScout24 URL, e.g. 'bmw/3er' from https://www.autoscout24.de/lst/bmw/3er. A full search URL works too. Make-only ('bmw') returns the whole make's population. Each entry costs $0.02. Note: AutoScout24 has no make-less search — its robots.txt disallows it — so a make is always required.

## `country` (type: `string`):

Which AutoScout24 country site to measure. The stock and the price distribution are that country's market, and prices are always in that domain's currency (all eight are EUR). Model slugs are localised: 'bmw/3er' on de/at, 'bmw/serie-3' on fr, 'bmw/3-series' on it/es/nl/be/com — the Actor follows the site's own redirect, so any of them works, but it tells you when a slug resolves to something different than you asked for.

## `ladderSteps` (type: `integer`):

How many price ceilings to ask the exact hit count for. Each rung is one request and one exact population count; more rungs means narrower bands and sharper quantiles. 9 covers a used-car market from a few hundred euros to six figures.

## `refineSteps` (type: `integer`):

Extra requests spent bisecting whichever band a wanted quantile is stuck in, widest first. Three of these typically cut the median's interpolation span by an order of magnitude. Set to 0 for the cheapest possible run.

## `mileageMax` (type: `integer`):

Optional. Conditions the whole analysis on cars at or below this mileage — the stock count, the ladder and the quantiles all become 'given mileage <= this'. Leave empty for the unconditional market.

## `firstRegistrationFrom` (type: `integer`):

Optional. Conditions the whole analysis on cars first registered in this year or later. Combine with 'Max mileage' for the price distribution of, say, a 2018+ car under 100,000 km — the shape a trade-in or residual-value quote actually needs.

## `maxListings` (type: `integer`):

How many listings to keep for the dealer/private split and the mileage, year, fuel and gearbox mix. They are drawn newest-first, which is independent of price. The price quantiles are exact population figures and are not affected by this. Each extra page of about 17-20 listings costs one request; this also caps how many are returned (and billed) when 'Include individual listings' is on (+$0.002 each).

## `includeIndividualItems` (type: `boolean`):

Off by default: a run costs a flat $0.02 per model summary. Enable to also get every sampled listing (price, mileage, first registration, fuel, gearbox, power, dealer or private) at +$0.002 per listing. Seller names, phone numbers and street addresses are never collected.

## `convertToUsd` (type: `boolean`):

Adds USD statistics next to EUR using the current exchange rate (open.er-api.com).

## Actor input object example

```json
{
  "models": [
    "bmw/3er"
  ],
  "country": "de",
  "ladderSteps": 9,
  "refineSteps": 3,
  "maxListings": 20,
  "includeIndividualItems": false,
  "convertToUsd": true
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "models": [
        "bmw/3er"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jpmarketdata/autoscout24-price-market-checker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "models": ["bmw/3er"] }

# Run the Actor and wait for it to finish
run = client.actor("jpmarketdata/autoscout24-price-market-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "models": [
    "bmw/3er"
  ]
}' |
apify call jpmarketdata/autoscout24-price-market-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=jpmarketdata/autoscout24-price-market-checker",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7oYNuBykq3nSO9691/builds/ArTU8XBeYfQt2UYa4/openapi.json
