# Willhaben.at Vehicles Scraper (`artsiom_k/willhaben-vehicles-scraper`) Actor

Austria used-vehicle scraper for Willhaben.at — cars, motorcycles, commercial vehicles, caravans & motorhomes — with province filters and built-in delta mode.

- **URL**: https://apify.com/artsiom\_k/willhaben-vehicles-scraper.md
- **Developed by:** [Artsiom Kunitsyn](https://apify.com/artsiom_k) (community)
- **Categories:** E-commerce, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 results

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

## willhaben-vehicles-scraper

Scrapes used-vehicle listings from [Willhaben.at](https://www.willhaben.at) — cars, motorcycles &
quads, commercial vehicles/pickups, caravans & motorhomes — via Willhaben's own internal JSON
search API, not HTML scraping. Sibling actor to `willhaben-scraper` (real estate), reusing the same
API family and delta-mode design against a different Willhaben vertical.

### Contents

- [Key features](#key-features)
- [Output](#output)
- [Input](#input)
- [Input examples](#input-examples)
- [Incremental (delta) mode](#incremental-delta-mode)
- [You might also like](#you-might-also-like)
- [FAQ](#faq)

### 🔑 Key features

- **No URL wrangling.** Pick a category and an Austrian province from a dropdown.
- **Delta mode built in.** Every run classifies each listing as `new`, `changed` (price moved),
  `unchanged`, or `delisted` against a persisted baseline — same design as `willhaben-scraper`.
- **Rich, structured vehicle data.** Make/model/trim, mileage, year, fuel type, transmission,
  engine power, condition, equipment list, and dealer-vs-private seller — all read directly from
  the search response, no per-listing detail fetch needed.
- **Real publish dates.** Unlike the real-estate actor (Willhaben exposes no trustworthy publish
  date there), vehicle listings carry a genuine `published_at` timestamp.
- **Privacy-aware by construction.** `dealer_name` is populated for dealer listings only (company
  name) and left `null` for private sellers.

### 📋 Output

One dataset item per listing (see [`.actor/dataset_schema.json`](.actor/dataset_schema.json) for
the full field list) plus `change_type`.

**Example record (car, private seller):**

```json
{
  "source": "willhaben",
  "external_id": "1813527645",
  "url": "https://www.willhaben.at/iad/gebrauchtwagen/d/auto/audi-e-tron-gt-...-1813527645/",
  "vehicle_type": "auto",
  "make": "Audi",
  "model": "e-tron GT",
  "model_specification": "e-tron GT 93,4kWh quattro Head-Up | Laserlicht",
  "body_type": "Limousine",
  "price_eur": 45990.0,
  "mileage_km": 156210.0,
  "year": 2021,
  "fuel_type": "Elektro",
  "transmission": "Automatik",
  "condition": "Gebrauchtwagen",
  "engine_power_kw": 350.0,
  "no_of_seats": 5,
  "equipment": ["LED-Scheinwerfer", "Bluetooth", "Apple CarPlay", "..."],
  "is_dealer": false,
  "dealer_name": null,
  "state": "Oberösterreich",
  "city": "Wels",
  "postal_code": "4600",
  "published_at": "2026-08-18T20:10:00+00:00",
  "change_type": "new"
}
```

**Known gaps, by design (see `docs/actors/willhaben-vehicles-scraper.md` for the full story):**

- `equipment` is human-readable feature names only for `auto` listings; other categories fall back
  to Willhaben's raw internal numeric codes (no text lookup exists for those on the site itself).
- `no_of_seats` applies to cars/commercial vehicles; `no_of_berths` (sleeping places) applies to
  caravans/motorhomes — a vehicle only ever has one or the other populated.
- `warranty` is only populated when Willhaben exposes a resolved text label for that listing.

### ⚙️ Input

See [`.actor/input_schema.json`](.actor/input_schema.json) for the full JSON schema. Key
parameters:

| Parameter | Type | Default | Description |
|---|---|---|---|
| `category` | String (required) | `auto` | One of `auto` (cars), `motorrad` (motorcycles & quads), `nutzfahrzeuge` (commercial vehicles/pickups), `wohnwagen_wohnmobile` (caravans & motorhomes). |
| `region` | String | `all` | One of Austria's 9 provinces, or `all` for the whole country. Changing this between runs starts a fresh incremental-mode baseline for the category. |
| `mode` | String | `auto` | `auto`/`full`/`incremental` — see [Incremental mode](#incremental-delta-mode). |
| `maxItems` | Integer | `50` | Stop after pushing this many dataset items. Clear it (`null`) for a full run — a capped run never updates the incremental baseline. |
| `maxPages` | Integer | *(none)* | Safety cap on search pages fetched. |
| `rows` | Integer | `200` | Listings per search page (1–200). |
| `delaySeconds` | Number | `3.5` | Politeness delay between consecutive search-page requests. |
| `proxyConfiguration` | Object | `{"useApifyProxy": false}` | Off by default, carried over from `willhaben-scraper`'s confirmed real-estate finding — **not yet independently reconfirmed for vehicles on Apify's infra** (see the build doc). |
| `impersonate` | String | *(none)* | Optional curl\_cffi TLS-impersonation target, used only if Willhaben starts blocking plain requests. |

### 🧪 Input examples

**Quick preview** — Wien cars, default 50-item cap:

```json
{ "category": "auto", "region": "wien" }
```

**Scheduled tracking run** — full, uncapped run:

```json
{ "category": "auto", "region": "wien", "mode": "incremental", "maxItems": null }
```

### 🔄 Incremental (delta) mode

Every run classifies each listing as `new`, `changed` (price moved), `unchanged`, or `delisted`,
using a state baseline persisted in a named Apify Key-Value Store scoped to `category` + `region`.
Same design as `willhaben-scraper` — full details in
[`docs/incremental-mode.md`](../../docs/incremental-mode.md).

### 🔗 You might also like

- **[Willhaben.at Real Estate Scraper](https://apify.com/artsiom_k/willhaben-scraper)** — the
  sibling actor for Willhaben's real-estate listings (apartments, houses, land, commercial — rent
  or sale). Same delta-mode design, same province filters — useful if you're tracking a seller or
  household's full footprint on Willhaben, not just vehicles.

### ❓ FAQ

**Is it legal to scrape Willhaben.at?** It's legal to collect publicly available listing data such
as prices, mileage, and locations. Results may contain limited personal data (dealer company names
only — see [Key features](#key-features)); scrape it only with a legitimate purpose under GDPR.

**Why is `equipment` sometimes just numbers?** See [Output](#output) — Willhaben only exposes
human-readable equipment names for the `auto` category; other categories fall back to raw codes.

### Search keywords

willhaben scraper, willhaben.at scraper, austria used car scraper, gebrauchtwagen scraper,
austrian vehicle data, car scraper austria, motorcycle scraper austria, willhaben auto api,
used vehicle price tracking, austria vehicle data feed

# Actor input Schema

## `category` (type: `string`):

Which Willhaben vehicle search category to scrape.

## `region` (type: `string`):

Restrict the search to one Austrian province, or scrape the whole country. Note: changing this between runs starts a fresh incremental-mode baseline for this category.

## `rows` (type: `integer`):

How many listings Willhaben returns per search page.

## `maxPages` (type: `integer`):

Safety cap on the number of search pages fetched. Leave empty to paginate to completion. Note: a capped run cannot detect delisted listings and does not update the incremental-mode baseline (see docs/incremental-mode.md).

## `maxItems` (type: `integer`):

Stop after pushing this many dataset items. Defaults to 50 — a fast, cheap preview, and what keeps an unconfigured run within Apify's automated 5-minute QA check. Raise it or clear it (set to null) for a full run.

## `delaySeconds` (type: `number`):

Politeness delay between consecutive search-page requests.

## `impersonate` (type: `string`):

Optional curl\_cffi browser TLS-impersonation target (e.g. "chrome124"), used if Willhaben starts blocking plain requests. Leave empty for plain requests.

## `mode` (type: `string`):

"auto" (recommended): full scan on the first run for a given category/area, incremental (new/changed only) afterwards. "full": always push every listing and refresh the baseline — schedule this periodically to catch delistings. "incremental": always push only new/changed listings. See docs/incremental-mode.md.

## `proxyConfiguration` (type: `object`):

Apify Proxy configuration. Starting default carried over from willhaben-scraper's confirmed finding (Willhaben returns HTTP 403 to Apify's shared datacenter proxy pool, direct connection works fine) — not yet independently reconfirmed for this vertical. Leave off unless you start seeing blocks from your own IP reputation; if so, try residential rather than datacenter groups.

## Actor input object example

```json
{
  "category": "auto",
  "region": "all",
  "rows": 200,
  "maxItems": 50,
  "delaySeconds": 3.5,
  "mode": "auto",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("artsiom_k/willhaben-vehicles-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("artsiom_k/willhaben-vehicles-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 '{}' |
apify call artsiom_k/willhaben-vehicles-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,artsiom_k/willhaben-vehicles-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/YYykZZmyrgivHqaC8/builds/PqQ5WCalGuW5v8cbf/openapi.json
