# AutoScout24 Scraper - Used Car Listings in 8 Countries (`halobartku/autoscout24-scraper`) Actor

AutoScout24 used-car search results as flat rows from 8 country sites incl. Poland (DE/AT/FR/IT/ES/NL/BE/PL): make, model, price, mileage, registration, fuel, gearbox, power, seller. Incremental mode returns only new or changed listings. Built for daily price-monitoring schedules.

- **URL**: https://apify.com/halobartku/autoscout24-scraper.md
- **Developed by:** [B](https://apify.com/halobartku) (community)
- **Categories:** E-commerce, Automation, MCP servers
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 listing page harvesteds

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## AutoScout24 Scraper - Used Car Listings from 8 European Markets

Flat, normalised vehicle listings from AutoScout24 search results — one clean
JSON row per vehicle across 8 country sites (de/at/fr/it/es/nl/be/pl).
Built for **daily price-monitoring and inventory schedules**, not one-off
human searches. (`ch` is currently source-blocked — see Honest limits.)

**Independently validated:** a from-scratch comparator (zero shared code with
the actor) re-parses the same-minute raw HTML and re-derives every field —
latest fresh gate 2026-09-25 on `fr/audi`: 20 rows × 28 fields, **0
mismatches**, with `priceEur` re-derived from the human-visible formatted
price (not the raw field the actor reads) and listings located by the
comparator's own structure discovery. Earlier gates: 40/40 rows on de
(2026-09-06), v0.7 price-evaluation fields (0 mismatches).

### Why this one

| | typical store scraper | this actor |
|---|---|---|
| Markets | 5–8 domains | **8, incl. Poland** |
| Broken page / site change | emits garbage or partial rows | **fails loudly, zero charge** |
| Failed or empty run | you still pay compute events | **harvest events only fire on written pages** |
| Output | ad-hoc shapes per run | **stable schema, identical across all 8 markets** |

A 31k-run competitor in this niche holds a **1.75/5 rating** — broken runs
and inconsistent output are the norm. This actor is the boring, reliable
alternative: same schema every run, and **you are never charged for a page
that failed to parse**.

### What you get (per listing)

`id`, `url`, `make`, `model`, `variant`, `trim`, `offerType` (U=used / N=new /
A=all), `priceEur` + `priceFormatted`, `priceEvaluation` +
`priceEvaluationLabel` (AutoScout24's own deal rating:
1=very-good-price … 4=somewhat-expensive-price, null when unrated),
`priceDropFrom` (the on-site "Super Deal" previous price — the price-drop
signal), `currency`, `mileageKm`,
`firstRegistration` (MM/YYYY), `fuelType`, `gearbox`, `powerKw`,
`engineDisplacementCc`, `consumptionL100Km` + `co2Class` (WLTP, EU 2023 A–G
label; null when AS24 does not publish them), `equipment` (the listing's own
equipment line), `sellerType`
(Dealer/Private), `sellerName`, `sellerId`, `sellerPhone` (formatted dealer
phone), `sellerStreet`, `city`, `zip`, `countryCode`,
`imageCount`, cover `image`, source `page`, `scrapedAt` (ISO 8601 UTC).

34 fields total, all extracted from the search results page we already
fetch — no extra per-listing requests. `compact: true` trims to the 22
core fields (see Input).

#### Sample output (real run, de/bmw — rendered)

![Real run output: autoscout24.de / BMW, 1 page, compact mode — 20 rows, 13 of 22 compact fields shown](https://halobartku.github.io/agent-forge-site/assets/as24/output-sample.png)

Above: an unedited run from 2026-09-13 (dataset rendered as a table, 12 of 20 rows shown).
Raw JSON for one real row:

```json
{
  "id": "76be9f75-646e-4328-8448-58c364367224",
  "url": "https://www.autoscout24.de/angebote/bmw-5er-d-touring-m-optic-navi-klima-shz-diesel-blau-…",
  "make": "BMW",
  "model": "520",
  "variant": "520d",
  "trim": "d Touring M-Optic Navi Klima SHZ",
  "offerType": "A",
  "priceEur": 23990,
  "priceFormatted": "€ 23.990",
  "priceEvaluation": 1,
  "priceEvaluationLabel": "very-good-price",
  "currency": "EUR",
  "mileageKm": 84990,
  "firstRegistration": "03/2021",
  "fuelType": "Diesel",
  "gearbox": "Automatik",
  "powerKw": 140,
  "sellerType": "Dealer",
  "sellerName": "Narin´s Automobile e.K.",
  "sellerId": "15221647",
  "city": "Nürtingen",
  "zip": "72622",
  "countryCode": "DE",
  "image": "https://prod.pictures.autoscout24.net/listing-images/….jpg/250x188.webp",
  "page": 1,
  "scrapedAt": "2026-08-20T00:04:56+00:00"
}
```

### Input example

```json
{
  "domain": "de",
  "make": "bmw",
  "model": "3er",
  "priceFrom": 5000,
  "priceTo": 25000,
  "firstRegistrationFrom": 2018,
  "mileageToKm": 80000,
  "fuel": "D",
  "pages": 2,
  "incremental": true
}
```

- `domain` — country site: `de`, `at`, `ch`, `fr`, `it`, `es`, `nl`, `be`, `pl`
- `make` — URL slug (`bmw`, `audi`, `volkswagen`, …); empty = all makes
- `model` — URL slug (`3er`, `golf`, `a4`, …); requires `make`
- `priceFrom` / `priceTo` — EUR band filter (optional)
- `firstRegistrationFrom` / `firstRegistrationTo` — first-registration year
  window (optional, v0.8)
- `mileageFromKm` / `mileageToKm` — km band filter (optional, v0.8)
- `fuel` — fuel code: `B` petrol, `D` diesel, `E` electric, `2` hybrid-petrol,
  `3` hybrid-diesel, `L` LPG, `H` hydrogen (optional, v0.8)
- `pages` — 1–50 results pages; each page = 20 listings, ~1.5 s fetch interval
- `incremental` — deliver only listings not delivered before for this query;
  pages with nothing new are not charged (idle scheduled run = $0). Default
  `false` = full listing set every run.
- `compact` — deliver only the 22 core fields per row (id, url, make, model,
  variant, offerType, both price fields + AS24 price rating + price-drop
  flag, mileage, first registration, fuel, gearbox, power, seller type,
  city, country, page, scrapedAt). Same rows, same charges — ~40% smaller
  payloads for dashboards and LLM contexts. Incremental change fields
  (`changeType`, `previous*`) are always kept. Default `false` = full
  34-field schema.

#### Incremental mode also detects changes (v0.5)

With `incremental: true` the actor tracks price, mileage, offer type and
seller per listing between runs. A listing that changed is **re-delivered**
with a `changeType` field (`price`, `mileage`, `seller`, `offer-type`, or a
`+`-joined combination) plus the previous values (`previousPriceFormatted`,
`previousMileageKm`, `previousOfferType`, `previousSellerType`). A change is
delivered exactly once; the next run with no further change costs $0 again.
So each scheduled run returns exactly what a price-watch pipeline needs:
new listings **and** re-priced/edited listings.

### Use it from an AI agent (MCP)

This actor exposes an MCP server. Ask your agent:

```
Use the autoscout24-listings actor to fetch the first 2 pages of BMW
listings on autoscout24.de between €5,000 and €25,000, then summarise
the median price by model.
```

Or call it directly:

```bash
apify call halobartku~autoscout24-listings --input='{"domain":"de","make":"bmw","priceFrom":5000,"priceTo":25000,"pages":2}'
```

### For AI agents & LLM apps (compact reference)

- **Purpose:** flat AutoScout24 search-results rows for price monitoring,
  dealer-inventory watches and market analysis across 8 European markets.
- **Minimal input:** `{"domain":"de","make":"bmw","pages":2}` (all other
  fields optional).
- **Output fields per row:** `id`, `url`, `make`, `model`, `variant`, `trim`,
  `offerType`, `priceEur`, `priceFormatted`, `priceEvaluation`,
  `priceEvaluationLabel`, `currency`, `mileageKm`,
  `firstRegistration`, `fuelType`, `gearbox`, `powerKw`, `sellerType`,
  `sellerName`, `sellerId`, `city`, `zip`, `countryCode`, `image`, `page`,
  `scrapedAt`.
- **Agent behaviors you can rely on:**
  - billing is per 20-listing **results page written**, never per message or
    per listing;
  - a query the site answers with zero listings is a SUCCEEDED run with 0
    rows and **0 charges** — safe to probe;
  - a site layout change fails the run loudly (exit non-zero, no partial
    garbage rows) — never interpret empty output as "no cars";
  - `scrapedAt` + stable `id` make rows diff-able across scheduled runs for
    "new since yesterday" alerts;
  - in incremental mode a changed listing arrives with `changeType`
    (`price`, `mileage`, `seller`, `offer-type`, `+`-joined combos) and
    `previous*` fields — you get re-priced listings without diffing
    yourself, each change delivered exactly once;
  - schema is identical across all `domain` values — one parser, no
    per-market branching in your pipeline.

### Use it from Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("halobartku~autoscout24-listings").call(run_input={
    "domain": "de", "make": "bmw",
    "priceFrom": 5000, "priceTo": 25000, "pages": 2,
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(len(rows), "listings, median €",
      sorted(r["priceEur"] for r in rows)[len(rows)//2])
```

### Pricing (with the honest comparison)

**$0.03 per results page (20 listings) + $0.00005 per actor start.**
A failed parse raises before the charge event fires — a page that yields
nothing is never billed. In incremental mode an idle scheduled run
costs **$0**.

At depth, per-listing competitors can be cheaper — here is the real
break-even, computed from their live prices (checked 2026-09-11):

| listings pulled | this actor (page-based) | blackfalcondata $0.00049/item + $0.005 start | memo23 $0.0009/item + $0.005 start |
|---|---|---|---|
| 20 (1 page) | $0.03005 | $0.0148 | $0.023 |
| 100 (5 pages) | $0.15005 | $0.054 | $0.095 |
| 400 | $0.60005 | $0.201 | $0.365 |
| 1,000 (max/run) | $1.50005 | $0.495 | $0.905 |

Read it straight: **for one-off bulk pulls, the per-item leaders are
cheaper.** Where this actor wins:

- **Scheduled monitoring** — incremental idle runs are $0; per-item actors
  re-bill every listing on every run, forever.
- **Coverage** — 8 markets incl. `pl` in one schema; several leaders are
  single-market.
- **Change detection + free webhook/Telegram alerts** built in — with the
  per-item leaders you re-pull and re-bill to diff yourself.
- **Failed pages are never billed** — the charge fires only on written
  pages.

### Alternatives (and when to pick them)

- **blackfalcondata/autoscout24-scraper** (58k runs, 61 users/30d) — the
  volume leader; $0.00049/result is the cheapest bulk pull. Pick it for
  large one-off extracts. Expect re-billing on every monitoring run and no
  change detection.
- **memo23/autoscout24-scraper** (6k runs) — per-result pricing with
  `additional-data` events; mid-priced bulk.
- **ivanvs/autoscout-scraper** (32k runs) — $0.004/result tiered by plan;
  convenient if you already standardise on their output shape.
- Single-country actors (3x1t CH, fayoussef, others) — if you need exactly
  one market and nothing else.
- This actor — pick it for **recurring price/inventory watches across
  markets**, incremental $0 idle runs, change alerts, and a stable
  34-field schema.

### Troubleshooting

- **Run failed with exit non-zero, 0 charges** — a page failed to parse;
  most often a transient AS24 response or a layout change. Re-run (free).
  If it repeats on one `domain` but not others, report it — that's a
  parser gap, not your input.
- **0 rows, 0 charges, status SUCCEEDED** — the query genuinely matched
  nothing. This is a valid empty answer, not a failure. Widen price/year
  bands or drop `model`.
- **Incremental run suddenly returns everything again** — the filter set
  changed (state is keyed per exact filter combo), or the state entry
  (>20k ids) was rotated. Deduplicate on `id` downstream; the next run
  returns to new/changed only.
- **`previousPriceFormatted` present but price looks identical** —
  `priceEur` is the compared field; formatted strings can differ by
  formatting only. Diff on `priceEur`.
- **Webhook/Telegram silent** — check the run log; delivery is fail-soft
  and errors are logged, never fatal. $0 idle runs still POST the webhook
  (heartbeat by design).

### Scheduler recipe (the intended use)

1. Create one schedule per filter combo you track (e.g. `de/bmw/5–25k`,
   `at/audi/all`, `pl/volkswagen/under 40k`).
2. Set `incremental: true` — the actor remembers every delivered listing
   per query and delivers **only new or changed ones** on each run. A
   scheduled run where nothing changed costs **$0**: pages whose listings
   were all delivered before and unchanged are not charged. Your downstream
   never re-processes history.
3. Set `pages: 5` for a stable daily snapshot of the top 100 listings
   (full mode) or of the new/changed-listings window (incremental mode).

State lives in the actor's key-value store, keyed per query
(`domain+make+model+price/year/km/fuel set`), bounded to the 20,000 most recent ids —
no unbounded growth, and each schedule/filter combo keeps its own stream.

### Notifications (free — webhook & Telegram)

Close the loop without polling the dataset:

- **Webhook** (`webhookUrl`): every finished run POSTs a JSON summary —
  query, mode, delivered/new/changed counts, pages charged, and up to 5
  sample listings — **including $0 idle runs**, so your pipeline gets a
  heartbeat, not silence. Machine-readable, one endpoint, any receiver.
- **Telegram** (`telegramBotToken` + `telegramChatId`): a human-readable
  message ("3 new + 1 changed — de/bmw €5000-25000 • \[price] BMW 320d
  € 19.900 (was € 20.900)…") sent only when there is something to read.

Both are **free, forever — zero billing events**, and fail-soft: a dead
endpoint is logged and the run still succeeds with its dataset. Pair with
incremental mode + a schedule for a complete new/changed-listings alert
pipeline that costs $0 on quiet days.

### FAQ

**How many results can I get per run?**
`pages` accepts 1–50; each page is 20 listings, so up to 1,000 rows per run.
For more, run multiple filter combos (make / model / price / year / km / fuel) — each is a cheap
separate run.

**Which countries are covered?**
Eight AutoScout24 country sites deliver data: `de`, `at`, `fr`, `it`, `es`,
`nl`, `be`, `pl`, with the same normalised schema and `countryCode` on every
row. `ch` is accepted as input but autoscout24.ch currently blocks datacenter
traffic (HTTP 403), so a `ch` run fails with zero charge (see Honest limits).

**Does it support recurring monitoring?**
Yes — that is what it is built for. Two ways: schedule a run per filter
combo with `incremental: true` and get **only the new or changed listings**
each run — a price drop re-delivers the listing with `changeType: "price"`
and its previous price; a run with nothing new or changed costs $0. Or
leave incremental off and dedupe on the stable `id` + `scrapedAt`
(ISO 8601 UTC) fields yourself.

**What happens if AutoScout24 changes their page?**
The run fails loudly instead of emitting garbage rows — and **you are not
charged**: the `listing-page-harvested` event only fires after a page is
successfully parsed and written.

**What if my query matches zero listings?**
The run SUCCEEDS with 0 rows and 0 charges — a valid empty answer, not a
failure. Widen the make/price range and re-run (free).

**Can I use it through an MCP server?**
Yes. The actor exposes an MCP server, so AI assistants can call it
directly (see the MCP section above).

**Can I pull the data into Python or Node?**
Yes — the snippet above shows `apify-client` for Python; the same pattern
works in Node with `apify-client` from npm, or plain HTTP against the
Apify API.

**Is there an official AutoScout24 API?**
No — AutoScout24 does not offer a public listings API. This actor is the
practical alternative: structured JSON ready for a database, dashboard or
model, no HTML parsing on your side.

**Is it legal to scrape AutoScout24?**
The actor extracts publicly available listing data. Scraping public
information is generally considered legal, but review the target site's
terms and ensure your use case complies with applicable law, including
GDPR where relevant.

### Keywords

autoscout24 scraper, autoscout24 api, autoscout24 listings, car listings
scraper, used car data europe, vehicle inventory feed, car price monitoring,
dealer stock monitor, automotive market data, second hand car prices,
autoscout24 deutschland, autoscout24 daten, gebrauchtwagen scraper,
auto listings json, car market analysis europe, price watch schedule,
vehicle data for ai agents, european car market dataset, incremental scraper,
car search filters, filter by fuel diesel petrol electric, filter by mileage, filter by first registration year, bmw 3er monitor, new listings alert, new since yesterday, car monitor scheduler,
webhook notifications, telegram alerts, price drop alert, price change monitor,
price change alert, price drop monitor, car price tracker,
listing change detection

### Changelog

- **0.2.2 (2026-10-04) - listing text matches coverage.** Title, store
  description and the FAQ said 9 countries; `ch` has been source-blocked since
  \~2026-09 (re-checked 2026-10-04: HTTP 403). Now they say 8. No code change.

- **0.13 (2026-09-25) — BE fix + coverage truth pass.** `be` re-fixed after
  the source restructured to locale-prefixed paths (bare `/lst` 404s);
  independent comparator gate re-run fresh on `fr/audi` (20 rows × 28 fields,
  0 mismatches, incl. priceEur re-derived from the visible formatted price);
  all-market live sweep: 8/8 work, `ch` source-blocked (403 from both sandbox
  and platform egress) — README/schema corrected from "9 markets" to the
  truth. Two platform probe runs of the failing markets confirmed
  fail-loud-zero-charge.

- **0.12 (2026-09-13) — Sample-output screenshot.** The sample section now
  leads with a rendered table of a real run (de/bmw, 1 page, compact,
  2026-09-13) hosted on our site, above the raw-JSON row. README-only
  change; no code, schema or pricing changes.

- **0.10.1 (2026-09-11) — README truth fixes + honest pricing.** The field
  list and "Honest limits" still described the pre-0.9 output (denied
  sellerPhone/equipment/CO2 existed) — corrected to the real 34-field
  schema. Added pricing-at-depth break-even table vs live competitor
  prices (2026-09-11), Alternatives and Troubleshooting sections. No code
  changes; charges and schema unchanged.

- **0.10 (2026-09-07) — Compact mode.** New `compact: true` input: deliver
  only the 22 core fields per row — identity, price + AS24 price rating +
  price-drop flag, mileage, first registration, fuel, gearbox, power, seller
  type, city. Same rows, same charge events, identical incremental behaviour
  (change detection runs on the FULL row before compaction, so toggling
  `compact` never re-delivers the world or corrupts state). Built for
  dashboards and LLM contexts where the full 34-field schema is overhead.
  Independent gate: 20 live rows, 22/22 projected values byte-identical to
  the full rows, 0 mismatches (`smoke/test_v10_compact.py`, 10/10 PASS).

- **0.9 (2026-09-06) — Detail enrichment at zero cost.** 8 new fields per
  row, all extracted from the search page we already fetch — no extra
  requests, no new billing events, identical charge contract (per page with
  deliverable rows): `sellerPhone` (formatted dealer phone, 19/20 in
  sample), `sellerStreet`, `equipment` (the listing's own equipment line),
  `engineDisplacementCc`, `consumptionL100Km` + `co2Class` (WLTP, EU 2023
  A–G label; null when AS24 does not publish them), `imageCount`, and
  `priceDropFrom` (AS24 "Super Deal" previous price — the on-site
  price-drop signal). Verified against a from-scratch re-parse of the same
  payload: 20/20 rows exact, 0 mismatches.

- **0.8 (2026-09-06) — Search filters: model, year, mileage, fuel.** Narrow
  the harvest before paying for pages: `model` (URL path segment, e.g.
  bmw/3er), `firstRegistrationFrom`/`firstRegistrationTo` (year window),
  `mileageFromKm`/`mileageToKm` (km band) and `fuel` (7 codes: B/D/E/2/3/L/H).
  Every filter verified live against returned content on 2026-09-06 — a
  `fuel=D` query returns only Diesel listings, a 2018–2020 window only
  2018–2020 cars. Incremental state is keyed per full filter set, so
  overlapping queries never pollute each other's seen-listings. Same
  billing: one event per results page that carries deliverable rows.
  Zero-result filtered query = free, valid empty answer.

- **0.7 (2026-09-06) — AS24 price-rating labels.** Every row now carries
  `priceEvaluation` (1–4) and `priceEvaluationLabel`
  (very-good-price / good-price / fair-price / somewhat-expensive-price) —
  AutoScout24's own deal classification, the same signal buyers see on site.
  Rank deals in your pipeline without a separate evaluation step. Null when
  AS24 has not rated the listing. Zero new requests, zero new billing events;
  incremental fingerprints unchanged (rating drift ≠ listing change).

- **0.6** (2026-09-01) — **Free notifications**: optional `webhookUrl`
  (JSON run summary on every finished run, including $0 idle runs) and
  `telegramBotToken`+`telegramChatId` (human-readable message when there
  are new/changed listings). Zero billing events, fail-soft — a broken
  endpoint never fails a run. No changes to scraping, incremental state
  or pricing.

- **0.5** (2026-09-01) — **Change detection in incremental mode**: the actor
  now tracks price, mileage, offer type and seller per listing between runs.
  A changed listing is re-delivered with `changeType` (`price`, `mileage`,
  `seller`, `offer-type`, or `+`-joined) and `previous*` fields showing the
  old values. Changes are delivered exactly once; a run with nothing new or
  changed still costs $0. Backward compatible with 0.3 incremental state
  (ids without fingerprints are treated as unchanged until first seen in
  0.4). Full mode unchanged.

- **0.3** (2026-09-01) — **Incremental mode**: set `incremental: true` and
  the actor remembers delivered listings per query (key-value store,
  bounded to 20k ids) and delivers only new ones. Pages with no new
  listings are **not charged** — an idle scheduled run costs $0. Full
  mode unchanged (default `false`).

- **0.1.8** (2026-08-26) — README v3: restored the full store listing
  (FAQ, sample output, AI-agent reference, keywords) that a hotfix deploy
  had silently replaced with an early draft. No code changes.

- **0.1.7** (2026-08-24) — Empty-answer correctness gate: a query the site
  answers with zero listings now SUCCEEDS with 0 rows and 0 charges instead
  of failing red. Bad input is still rejected before any charge.

- **0.2 / 0.1.6** (2026-08-20) — Public store release. Pay-per-event pricing
  ($0.03 / results page, failures never charged). Poland (`pl`) added —
  8 markets total (ch source-blocked since ~09-2026). Loud-fail parser guardrails.

- **0.1** (2026-08-19) — Initial build: 8 markets, normalised 23-field
  schema, scheduler-oriented design, private QA runs.

### Honest limits

- **`ch` is currently source-blocked** (since ~2026-09): autoscout24.ch
  returns HTTP 403 to datacenter traffic on every path, from every egress we
  can test (sandbox and Apify platform both, verified 2026-09-25). A `ch` run
  fails loudly with zero charge — the no-garbage guarantee holds, but you get
  no data. All other 8 markets verified live 2026-09-25 (7 in one sweep +
  `be` after its restructure fix). If you need CH data, this is not your
  actor today.
- **`be` was restructured by the source** (bare `/lst/*` returns 404 since
  \~2026-09); v0.13 reads the locale-prefixed `/fr/lst` path. Filters,
  pagination and detail URLs verified live 2026-09-25. Promoted listings
  (`adTier`) may sit outside your filter window — the site injects them into
  results; we mirror the site, we do not filter them.
- Search-result depth: the 34 fields above, from the search page (v0.9
  added phone/street/equipment/CO2 at zero extra cost). Not included: the
  full free-text description and image galleries — that is a per-listing
  detail-page product, slower and costlier.
- Prices on non-EUR sites (e.g. `ch`) are normalised to a raw integer in the
  listing's local currency; `currency` is set per row accordingly.
- Server-rendered source; if AutoScout24 changes its page structure the actor
  fails loudly (zero charge) rather than emitting garbage.
- Politeness: ~1.5 s between page fetches. 50 pages ≈ 2 minutes.

### Made by an AI agent

This actor was designed, built and tested by an autonomous AI agent
(Jarvis Turbo, operator: Bartosz). \[AI-authored — no human wrote this code.]

# Actor input Schema

## `domain` (type: `string`):

AutoScout24 country: de, at, fr, it, es, nl, be, pl (ch currently source-blocked, runs fail free)

## `make` (type: `string`):

URL slug of the make, e.g. bmw, audi, volkswagen. Empty = all makes.

## `model` (type: `string`):

URL slug of the model, e.g. 3er, golf, a4. Requires make. Model filters ride the URL path (/lst/bmw/3er) — verified: bmw/3er = 11,122 listings, all 316/318/320/330 variants.

## `priceFrom` (type: `integer`):

Minimum price in EUR

## `priceTo` (type: `integer`):

Maximum price in EUR

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

Earliest first-registration year, e.g. 2018. Verified live: fregfrom=2018 narrows DE from 873,074 to 694,211 listings.

## `firstRegistrationTo` (type: `integer`):

Latest first-registration year. Pair with firstRegistrationFrom for a window (2018-2020 = 91,357 listings, every result within the window).

## `mileageFromKm` (type: `integer`):

Minimum mileage in km, e.g. 10000. Verified live: kmfrom=10000 alone narrows DE to 238,924 listings.

## `mileageToKm` (type: `integer`):

Maximum mileage in km. Pair with mileageFromKm for a band (10000-50000: every returned listing inside the band).

## `fuel` (type: `string`):

One of: B (petrol), D (diesel), E (electric), 2 (hybrid petrol), 3 (hybrid diesel), L (LPG), H (hydrogen). All 7 codes verified live by reading fuelType of returned listings (B=453,791 · D=259,072 · E=63,453 · 2=87,966 · 3=4,600 · L=2,872 · H=141).

## `pages` (type: `integer`):

Each page = 20 listings, charged as one event

## `incremental` (type: `boolean`):

Remember delivered listings per query (stored in the actor's key-value store) and deliver only NEW listings plus listings that CHANGED since the last run (price, mileage, offer type, seller) — changed rows carry a changeType field and their previous values. A page where nothing is new or changed is not charged — a scheduled run with nothing to report costs $0. Turn off to always get the full listing set.

## `webhookUrl` (type: `string`):

Optional. Every finished run POSTs a JSON run summary (counts, new/changed, sample listings) to this URL — including $0 idle runs, so your pipeline gets a heartbeat. Notifications are free and never billed; delivery failures are logged and never fail the run.

## `telegramBotToken` (type: `string`):

Optional. Bot token from @BotFather. Messages are sent only when there is something to read: new or changed listings in incremental mode, any listings otherwise. Pair with telegramChatId. Free, fail-soft.

## `telegramChatId` (type: `string`):

Optional. Chat/channel ID the Telegram notifications go to (get it from @userinfobot or a bot API call). Ignored when telegramBotToken is empty.

## `compact` (type: `boolean`):

Deliver only the 22 core fields (id, price, price rating, mileage, year, fuel, gearbox, power, seller type, city) instead of the full 34-field schema. Same rows, same charges — smaller payloads for dashboards and LLM contexts. Incremental change fields (changeType, previous\*) are always kept. Default false = full schema.

## Actor input object example

```json
{
  "domain": "de",
  "fuel": "",
  "pages": 2,
  "incremental": false,
  "compact": false
}
```

# Actor output Schema

## `items` (type: `string`):

All results, one dataset item per row. Fetch as JSON via this URL with an APIFY_TOKEN bearer header. A run-level summary is written to the run's terminal status message.

# 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 = {
    "domain": "de",
    "make": "",
    "model": "",
    "fuel": "",
    "webhookUrl": "",
    "telegramBotToken": "",
    "telegramChatId": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("halobartku/autoscout24-scraper").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 = {
    "domain": "de",
    "make": "",
    "model": "",
    "fuel": "",
    "webhookUrl": "",
    "telegramBotToken": "",
    "telegramChatId": "",
}

# Run the Actor and wait for it to finish
run = client.actor("halobartku/autoscout24-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "domain": "de",
  "make": "",
  "model": "",
  "fuel": "",
  "webhookUrl": "",
  "telegramBotToken": "",
  "telegramChatId": ""
}' |
apify call halobartku/autoscout24-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,halobartku/autoscout24-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/AkLpAAqTJ3VWEJPlF/builds/ejrdsgXGCBxKK0EfY/openapi.json
