AutoScout24 [Only $1.25] Scraper · CH -> SWISS
Pricing
from $1.25 / 1,000 results
AutoScout24 [Only $1.25] Scraper · CH -> SWISS
[Only $1.25] Scrape vehicle listings from AutoScout24.ch (Switzerland) via the public REST API. Supports filter-based search and direct detail page URLs. ℹ️ For custom solutions or support, contact : muhameddidovic@gmail.com
Pricing
from $1.25 / 1,000 results
Rating
0.0
(0)
Developer
Muhamed Didovic
Maintained by CommunityActor stats
0
Bookmarked
39
Total users
5
Monthly active users
8 days ago
Last modified
Categories
Share
AutoScout24 Scraper · CH
Overview
This scraper extracts vehicle listings from AutoScout24 Switzerland
(autoscout24.ch) — a separate product operated by Auto Scout 24 Schweiz AG (part of
Swiss Marketplace Group). It is not the same backend as .de / .com and other
AutoScout24 EU markets — those are operated by a different company and need a different
scraper.
Looking for listings from any other AutoScout24 market (
.de/.com/.it/.es/.fr/.be/.nl/.at/.lu)? Use the sibling actor: AutoScout24 Scraper · DE · AT · IT · ES · FR · BE · NL · LU · .com.
The scraper talks to the public api.autoscout24.ch REST API:
| Endpoint | Method | Purpose |
|---|---|---|
POST /v1/listings/search?language={de|fr|it|en} | POST | Paginated vehicle search (max page size 20) |
GET /v1/listings/{id}?language={...} | GET | Full structured detail JSON for one listing |
No authentication needed. The API host is not behind Cloudflare (unlike the public
www.autoscout24.ch HTML pages), so a proxy is not strictly required.
Features
- Filter mode — paginate through the entire car catalog and pull matching listings.
- Detail-URL mode — paste one or more
https://www.autoscout24.ch/{lang}/d/{id}URLs. - Optional detail enrichment — fetches the JSON detail endpoint for each match (extra fields like cylinders, doors, drive type, full consumption breakdown).
- Seller profile and coordinates on every row — street, zip, city, typed phones, website and Google place id from the seller profile, plus the seller's latitude and longitude with their source.
- Dealers-only filter —
sellerTypes: ["professional"]sent to the search, so private listings never come back. - Completeness check — every search's reported count is compared with what was delivered; a short run ends as FAILED instead of passing for a small market.
- All four UI languages:
de,fr,it,en. - Five vehicle categories:
car,camper,truck,motorcycle,trailer.
Server-side filtering
The POST /v1/listings/search endpoint honours all the major filters server-side, using
the same shape the website's own SSR uses (extracted from initialSearchState in a
Wayback Machine snapshot):
| Filter | Field in request body | Notes |
|---|---|---|
| Make / model | makeModelVersions: [{ makeKey, modelKey }] | Nested — not top-level makeKey |
| Fuel | fuelTypes: [string] | Plural array; singular fuelType is silently ignored. The API rejects hybrid and plugin-hybrid (HTTP 400), so the form's Hybrid and Plug-in Hybrid options send hev-petrol + hev-diesel and phev-petrol + phev-diesel |
| Body | bodyTypes: [string] | Plural array |
| Condition | conditionTypes: [string] | Plural array: new, pre-registered, demonstration, used, oldtimer |
| Seller type | sellerTypes: [string] | professional (dealers) or private |
| Price (CHF) | priceFrom, priceTo | Int |
| Year | firstRegistrationYearFrom, firstRegistrationYearTo | Int |
| Mileage (km) | mileageFrom, mileageTo | Int |
| Power (HP) | horsePowerFrom, horsePowerTo | Int — not powerFrom/powerTo |
| Vehicle category | vehicleCategories: [string] | Plural array |
| Sort | sort: [{ type, order, variant: 'v1' }, …] | Server-side. Types: RELEVANCE, PRICE, MILEAGE, CREATED_DATE, FIRST_REGISTRATION_DATE, HORSE_POWER. Any sort but RELEVANCE and CREATED_DATE gets CREATED_DATE DESC as a second key (see below) |
Result: a query that matches 341 listings on the website also returns 341 from
the API (no over-fetching). A matchesClientSideFilters pass still runs as a
defense-in-depth safety net but is normally a no-op.
Stable paging
Listings that share a sort value (the same price, the same mileage) have no fixed order
among themselves, so between two page requests they can swap places: one comes twice,
another never. Measured 06.10.2026 on 980 used electric dealer cars at 25,000–29,999 CHF:
sorted by price alone, the 49 pages held 904 distinct ids. The actor adds CREATED_DATE DESC
as a second key to every sort but Relevance and Newest; with it, every sort returned all 976
dealer and all 105 private listings in that band.
Relevance has a different problem: on some result sets the server pages it with an offset error. The 105 private listings above came back as 120 rows with 99 distinct ids, on every pass, with page 3's last 10 listings repeated at the top of page 4 and again from page 4 to 5. A second sort key does not help. So when a Relevance search shows repeated listings and comes up short of its count, the actor reads it once more sorted newest first and adds only the listings the first pass missed (105 of 105 in that case). An id already delivered for the same search is never pushed twice.
Completeness check
The first page of every search reports totalElements. At the end of the run the actor
compares it with the distinct listings it delivered for that search, logs the comparison,
writes it to the COMPLETENESS record in the run's key-value store, and puts the summary in
the status message. When the shortfall exceeds the larger of 1% and 25 listings
(completenessTolerancePercent, completenessToleranceAbsolute), the run ends as FAILED.
A search stopped by maxItems is not judged, a search whose first page never loaded fails
outright, and delivering more than reported (listings published mid-run) never fails.
Turn the gate off with failOnIncomplete: false.
When Apify migrates a run to another server mid-run, the actor starts again on the new one.
It reads back the rows already in its dataset (each row's searchSegment names its search),
so no listing is pushed twice and every search keeps its delivered count. Each search's
reported count is kept in the COMPLETENESS_STATE record, so the restarted run is judged
against the same numbers.
Seller profile and coordinates
With includeSellerDetails (on by default) every row gets a seller object from
GET api.autoscout24.ch/v1/sellers/{id}, with the API's own field names and values, and a
sellerCoordinates object. Each seller is fetched once per run. The coordinates follow the
pin next to the address on the seller page:
sellerCoordinates.source | Where the coordinates come from |
|---|---|
seller_location | The dealer entered them on AutoScout24 (location in the profile; the pin links to ?query=lat,lng). |
google_place | The pin searches the dealer's Google place. A ChIJ… place id encodes Google's feature id; the actor asks Google for that place and keeps the answer only when the feature id matches. |
address_swisstopo | No usable link: the street, zip and city from the profile, geocoded by swisstopo's federal address search. |
address_google | The same address, geocoded by Google, when swisstopo has no match in that zip code. |
null | Nothing to geocode (private sellers publish no street) or no match. |
Sample of 511 dealer profiles on 06.10.2026: 15 had their own location, 401 were placed through their Google place, 94 by address, 1 not at all (two branches typed into one address field). Google-place coordinates sat a median 19 m from swisstopo's point for the same street address.
Still client-side only (server-side shape not yet confirmed)
- ZIP code + radius (
zipCode/radiusKm) — sent but silently ignored - Warranty / accident (
hasWarranty/hasAccidentHistory) — sent but ignored
If you set these, the actor pages through results and applies them client-side. Send an example URL that uses one of these filters and we can extend support.
Input
Provide either startUrls (detail URLs) or filter fields. URLs always win when
both are set.
Filter-mode example
{"filterLanguage": "de","filterVehicleCategory": "car","filterMake": "bmw","filterModel": "x1","filterPriceFromCHF": 20000,"filterPriceToCHF": 50000,"filterYearFrom": 2020,"filterFuelType": "hybrid","sortType": "RELEVANCE","sortOrder": "DESC","enrichDetails": false,"maxItems": 100,"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
Detail-URL example
Paste full detail URLs. The platform's input check refuses anything that is not a URL, so a bare listing id is rejected before the run starts; put it in a URL instead (https://www.autoscout24.ch/de/d/20464837).
{"filterLanguage": "de","startUrls": [{ "url": "https://www.autoscout24.ch/de/d/20703068" },{ "url": "https://www.autoscout24.ch/fr/d/20462882" },{ "url": "https://www.autoscout24.ch/it/d/20464837" }],"maxItems": 100}
The language comes from the URL (/de/, /fr/, /it/, /en/).
Input field reference
| Field | Type | Description |
|---|---|---|
startUrls | [{ url }] | Detail-page URLs (/de/d/{id}, /fr/d/{id}, …) or a search URL copied from the site. From a search URL the actor reads make/model, price, year, mileage, power, sort, seller type, and every ticked fuel type, body type and condition. |
filterLanguage | de|fr|it|en | Response locale & detail-page URL prefix. Default de. |
filterVehicleCategory | car|camper|truck|motorcycle|trailer | Works server-side. Default car. |
filterMake / filterModel | string | Make/model key (lowercase, hyphen-separated; e.g. bmw, mercedes-benz, x1). Applied client-side. |
filterConditionType | new|pre-registered|demonstration|used|oldtimer | Sent to the API as a search filter. |
filterFuelType | electric|petrol|diesel|hybrid|mhev-petrol|mhev-diesel|plugin-hybrid | Sent to the API as a search filter. hybrid covers petrol and diesel full hybrids, plugin-hybrid petrol and diesel plug-in hybrids. |
filterBodyType | suv|saloon|estate|coupe|cabriolet|small-car|pickup|minivan|bus | Client-side. |
filterSellerType | professional|private | Sent to the API as sellerTypes. professional = dealers only. Empty = any seller. |
filterPriceFromCHF / filterPriceToCHF | int | CHF, inclusive. Client-side. |
filterYearFrom / filterYearTo | int | First-registration year, inclusive. Client-side. |
filterMileageFrom / filterMileageTo | int | km, inclusive. Client-side. |
filterPowerFromHP / filterPowerToHP | int | HP, inclusive. Client-side. |
filterZipCode / filterRadiusKm | string + int | Sent to the API; not verified client-side (no geo lookup). |
sortType | RELEVANCE|PRICE|MILEAGE|CREATED_DATE|YEAR|POWER | Works server-side. Default RELEVANCE. YEAR and POWER are sent as FIRST_REGISTRATION_DATE and HORSE_POWER (the API rejects the short names). |
sortOrder | DESC|ASC | Default DESC. |
enrichDetails | bool | When true, fetch /v1/listings/{id} per result for extra fields. Doubles request count. |
includeSellerDetails | bool | Seller profile and seller coordinates on every row. Default true. |
failOnIncomplete | bool | End the run as FAILED when a search's shortfall exceeds the threshold. Default true. |
completenessTolerancePercent / completenessToleranceAbsolute | int | The threshold: the larger of this percentage of the reported count and this many listings. Defaults 1 and 25. |
maxItems | int | Hard cap on output rows. Default 1000. Non-paying users are capped at 60. |
maxConcurrency / minConcurrency / maxRequestRetries | int | Crawlee tuning knobs. |
proxy | object | Apify proxy config. Residential recommended only for very heavy runs. |
Output
Each pushed dataset row has this shape (selected fields shown):
{"id": 20458677,"url": "https://www.autoscout24.ch/de/d/20458677","language": "de","title": "M2 Coupé","price": 63890,"priceCurrency": "CHF","make": "BMW", "makeKey": "bmw", "makeId": 9,"model": "M2", "modelKey": "m2", "modelId": 999,"firstRegistrationDate": "2024-…","firstRegistrationYear": 2024,"mileage": 28900,"fuelType": "petrol","transmissionType": "automatic","bodyType": "coupe","conditionType": "used","powerHp": 460,"powerKw": 338,"consumptionCombined": 9.6,"drive": "rear","hadAccident": false,"accidentFree": true,"currentlyDamaged": null, // AutoScout24.ch does not publish this"roadworthy": null, // AutoScout24.ch does not publish this"hasWarranty": true,"warrantyType": "from-delivery","sellerId": 123456, "sellerName": "…", "sellerType": "professional","sellerCity": "Zürich", "sellerZipCode": "8001","seller": {"id": 64129, "name": "ADRAGNA AUTOMOBILE", "type": "professional","address": "Bernstrasse 64", "zipCode": "8952", "city": "Schlieren","phones": [{ "number": "+41764379004", "type": "sales" }, { "number": "+41763341416", "type": "business" }],"website": null,"googlePlaceId": "ChIJDbMwRQANkEcRgHKo2z6xmng"},"sellerDetailsFetched": true, // false when the profile request failed; `seller` then holds the search data"sellerCoordinates": { "latitude": 47.402593, "longitude": 8.4385758, "source": "google_place" },"imageCount": 17,"images": ["https://listing-images.autoscout24.ch/listing/…", "…"],"source": "list", // or "detail" when enrichDetails=true"raw": { "...": "full listing object as returned by the API" }}
accidentFree, currentlyDamaged and roadworthy use the same names and meaning as our mobile.de and AutoScout24 (DE, IT, ...) scrapers: true / false, or null when the listing does not say. AutoScout24.ch publishes accident history only, so accidentFree is hadAccident inverted and the other two are always null.
Architecture
src/main.ts— input validation, dual-mode resolution, crawler setup. Free-user clamp +ACTOR_MAX_PAID_DATASET_ITEMSrespected.src/routes.ts— two handlers:LIST— POSTs to/v1/listings/search, drops ids already delivered for the same search, appliesmatchesClientSideFilters, looks up each row's seller, pushes rows, enqueues next page untilmaxItemsor end.DETAILS— GETs/v1/listings/{id}, merges withbasicInfofrom the list pass.
src/lib/buildSearchQuery.ts— payload builder + client-side filter predicate.src/lib/sellers.ts— seller profiles and seller coordinates, cached per seller per run.src/lib/completeness.ts— the per-search completeness check.src/lib/utils.ts— URL classification (isDetailPageUrl,extractListingId,extractLanguageFromUrl, …).
Tests
npm test # 61 unit tests, fully offline (sub-second)npm run test:live # 2 integration tests against the real CDN (~1 s, requires internet)
The offline suite covers URL classification, language extraction, filter matching, payload construction, start-URL/bare-id normalisation, and image-URL construction.
The live suite checks that the image-CDN host we construct (listing-images.autoscout24.ch)
still resolves to real images. Run it after any AutoScout24-side change to catch
silent breakage early. Not invoked by default so flaky networks don't break local dev.
Out of scope
- Pasting search/list URLs (
/de/s/...) is not supported — the URL → API mapping isn't published and parsing the query-string syntax (mk-bmw,mo-320, …) is fragile. Use the filter fields instead. - The Swiss site has a separate iOS app (App Store ID
295835058, Android packagech.autoscout24.autoscout24). The actor does not currently use any iOS-app-specific endpoints — those would need a mitmproxy capture to discover.
Support
For custom solutions, schema changes, or to report a broken endpoint: muhameddidovic@gmail.com
🤖 For AI Agents & LLM Apps
Compact reference for AI agents calling this actor via the Apify MCP server or the Apify API (actor: memo23/autoscout24-ch-scraper).
Purpose: Extract vehicle listings from AutoScout24 Switzerland (autoscout24.ch) via its public REST API — by search filters or by detail URL/id. Swiss market only (.de / .com use the sibling actor).
Minimal input:
{"filterLanguage": "de","filterVehicleCategory": "car","maxItems": 50}
To scrape specific listings instead, pass startUrls with detail URLs (https://www.autoscout24.ch/de/d/20703068) or search URLs — URLs win when both are set.
Output: one dataset row per listing — id, url, language, title, price, priceCurrency, make/makeKey/makeId, model/modelKey/modelId, firstRegistrationDate, firstRegistrationYear, mileage, fuelType, transmissionType, bodyType, conditionType, powerHp, powerKw, consumptionCombined, drive, hadAccident, accidentFree, currentlyDamaged, roadworthy, hasWarranty, warrantyType, sellerId/sellerName/sellerType/sellerCity/sellerZipCode, seller (profile: address, zipCode, city, phones, website, googlePlaceId), sellerDetailsFetched, sellerCoordinates (latitude, longitude, source), imageCount, images, source, searchSegment (the search that produced the row: the pasted search URL or filters plus the filter query; null for a pasted listing URL), raw. accidentFree is hadAccident inverted (null when the seller did not say). currentlyDamaged and roadworthy are always null because AutoScout24.ch does not publish them; they are there so rows line up with the mobile.de and AutoScout24 (DE, IT, ...) actors.
Behaviors an agent should know:
maxItemsis required; non-paying users are capped at 60 (default 1000).startUrls(detail or search URLs) take priority over the filter fields.- Category and sort apply server-side; make/model/fuel/body/condition/price/year/mileage/power apply client-side while paginating.
enrichDetails: truefetches the detail endpoint per listing (cylinders, doors, drive) and doubles the request count.- No proxy required (the API host is not behind Cloudflare); residential recommended only for very large runs.
- Pay-per-event billing — see the Pricing tab on the actor page.
⚠️ Disclaimer
This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Auto Scout 24 Schweiz AG (part of Swiss Marketplace Group) or any of its subsidiaries. All trademarks mentioned are the property of their respective owners.
The scraper accesses only publicly available vehicle-listing data from AutoScout24.ch's public REST API — no authenticated endpoints, paid features, or content behind a login wall. Users are responsible for ensuring their use complies with AutoScout24.ch's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization.
SEO Keywords
autoscout24 scraper, scrape autoscout24, autoscout24.ch scraper, autoscout24 api, Apify autoscout24, swiss car scraper, car listings scraper, used car scraper, vehicle data api, auto marketplace scraper, car price scraper, car market analysis, used car pricing data, dealer inventory scraper, switzerland automotive data, vehicle listings export, car dealership data, automotive market intelligence