# Willhaben.at Scraper - Listings, Prices, Sellers & 40+ Fields (`abotapi/willhaben-at-scraper`) Actor

From $1/1K. Scrape Willhaben.at listings into clean JSON with 40+ structured fields, including prices, GPS coordinates, photos, dates, seller details and category-specific attributes. Search with filters or paste any Willhaben URL.

- **URL**: https://apify.com/abotapi/willhaben-at-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Real estate, Automation
- **Features:** Limited permissions
- **Stats:** 6 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Willhaben.at Scraper: Cars, Property, Marketplace & Job Listings

Willhaben.at Scraper turns Austria's largest online marketplace into clean, structured JSON. Pull used cars, apartments and houses, land, general marketplace goods, and job postings straight from willhaben's own data source, with roughly 40 base fields per listing (often many more once every category attribute is flattened in), GPS coordinates, a full address hierarchy, and every photo URL. Build a search from filters, or paste any willhaben search-page link, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

### Why This Scraper?

- **Two ways to find listings.** Build a search from filters (category, keyword, price range, area, sort), or paste any willhaben search-page link. URL mode accepts multiple links at once and walks every result page forward automatically.
- **Nine categories in one actor.** Marketplace goods, apartments and houses to rent or buy, land, used cars, caravans and motorhomes, and jobs, each returning its own category-specific fields.
- **Roughly 40 base fields per listing**, plus every category-specific attribute flattened into `attr_*` keys and the original attribute array preserved, so nothing the site exposes is dropped.
- **Full location detail on every record.** GPS latitude and longitude, the complete address hierarchy (state, district, postcode, municipality), and every photo URL.
- **Optional detail-page enrichment** adds contact name, dealer or organisation info, address detail and the complete attribute set; detail pages are fetched concurrently across listings rather than one at a time.
- **Automatic forward pagination** with page-level de-duplication and a mid-run checkpoint, so an interrupted run can pick up where it left off instead of re-collecting, and re-charging, listings it already pushed.
- **Built for schedules.** Incremental mode tracks a search over time and returns only new and changed listings on recurring runs, and a run that cannot reach willhaben at all ends with a clear status message explaining why, instead of a silent empty result.

### Use Cases

- **Car buyers and dealers:** track new listings for a specific make, model, year range or mileage band across Austria, or within one region.
- **Real estate investors and renters:** monitor apartments, houses and land in a chosen area for new listings and price changes.
- **Resellers and marketplace watchers:** search marketplace categories by keyword to spot newly listed items in a price range.
- **Job seekers and recruiters:** track new postings for a role or keyword, with company, salary and employment-type fields.
- **Market researchers and analysts:** build historical pricing and inventory datasets across categories for trend analysis.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| `id` | `"000000001"` |
| `url` | `https://www.willhaben.at/iad/gebrauchtwagen/d/auto/sample-000000001/` |
| `title` | `"Sample Listing Title"` |
| `description` | `"Full listing description text appears here."` |
| `price` / `priceDisplay` | `7800` / `"€ 7.800"` |
| `category` | `"gebrauchtwagen/auto/gebrauchtwagenboerse"` |
| `state` / `district` / `postcode` | `"Wien"` / `"Wien"` / `"1010"` |
| `locationName` | `"Sample Location"` |
| `latitude` / `longitude` | `48.2000` / `16.3700` |
| `publishedDate` / `changedDate` | `"2026-01-01T00:00:00.000Z"` |
| `isPrivate` / `advertiserLabel` | `true` / `"Privat"` |
| `imageCount` / `mainImage` | `12` / `https://cache.willhaben.at/mmo/0/000/000/000_0.jpg` |
| `images` | `["https://cache.willhaben.at/mmo/0/000/000/000_0.jpg", ...]` |
| `make` / `model` / `year` / `mileage` | car fields: `"SampleMake"` / `"SampleModel"` / `2019` / `120000` |
| `fuel` / `transmission` | `"Benzin"` / `"Automatik"` |
| `rooms` / `livingArea` / `floor` | property fields: `3.5` / `85` / `"2"` |
| `pricePerSqm` | `4200.0` (property listings) |
| `contactName` / `organisationName` | `"A. B."` / `"Sample Dealer"` (only when Fetch detail pages is on) |
| `seoUrl` | `"gebrauchtwagen/d/auto/sample-000000001/"` |
| `advertiserInfo` | the seller block the search API returns, passed through as-is |
| `rawAttributes` | the full, unflattened list of `{name, values}` attribute objects for this listing |

Every attribute willhaben exposes for a listing is also flattened into its own `attr_*` key (for example `attr_CAR_MODEL_MAKE`, `attr_NUMBER_OF_ROOMS`), so a field this table does not name is still in the record under its raw attribute name. Jobs listings (category `jobs`) are a different record shape entirely: `id`, `url`, `category` (`"jobs"`), `title`, `position`, `companyName`, `companyId`, `companyLogo`, `locations`, `locationName`, `salary`, `salaryTimeFrame`, `employmentTime`, `employmentModes`, `publishedDate`, `createdDate`, `modifiedDate`, `isExpired`, `topJob`, `overpay`, `brandNew`, `internalApplication`, and `raw` (the complete source entry); the Jobs category also ignores the price and area filters.

### How to Use

1. Pick a **mode**: `search` builds a query from the filters below, `url` scrapes any willhaben search-page link you paste.
2. In search mode, choose a **category** and fill in the filters you need (keyword, price range, area, sort). In URL mode, paste one or more search-page links; filter fields are ignored.
3. Turn on **Fetch detail pages** if you want contact and dealer info, and set **Max listings** (and optionally **Max pages**) to control run size and cost.
4. Click **Start**. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Search used cars, list-only:**

```json
{
  "mode": "search",
  "category": "gebrauchtwagen/auto/gebrauchtwagenboerse",
  "maxListings": 20,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AT" }
}
```

**Search the marketplace by keyword with a price range:**

```json
{
  "mode": "search",
  "category": "kaufen-und-verkaufen/marktplatz",
  "keyword": "iphone",
  "minPrice": 100,
  "maxPrice": 500,
  "sort": "price-asc",
  "maxListings": 200,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AT" }
}
```

**Rentals in Vienna with detail enrichment:**

```json
{
  "mode": "search",
  "category": "immobilien/mietwohnungen",
  "areaId": 900,
  "fetchDetails": true,
  "maxListings": 50,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AT" }
}
```

**Supported URL shapes (URL mode).** Category and keyword search pages (also with `page=`, filters such as `PRICE_FROM`, `PRICE_TO`, `CAR_MODEL/MAKE`, `areaId` and `sort=`, which are applied as pasted), real-estate region and district pages, older category slugs that willhaben redirects, motorcycle and other category pages, links without `www`, single classified ads (`/iad/<category>/d/...-<id>/` and `/iad/object?adId=<id>`), job searches (`/jobs/suche`, also paginated) and single job pages (`/jobs/job/<slug>/<id>`). A pasted `rows=` value never changes the page size; it only decides which page a pasted `page=` number points at. Single-ad links return one record; the detail enrichment option adds the contact and seller blocks as in search mode. A link that is not a willhaben page is skipped with a warning.

**Behaviour change:** selecting URL mode without editing the URL list now works through a list of 17 example links, one per shape above, instead of a single car search. The run stops at **Max listings** (default 20), and each link gets an equal share of that cap, so with the default only the first links in the list are reached; raise **Max listings** (for example to 100) to cover all 17 shapes. Replace the list with your own links to scrape only those. Search mode also accepts a plain **Region name** (for example `Wien` or `Tirol`) in place of an Area ID and plain category names such as `motorrad` or `mietwohnungen` in the custom category path.

**Paste willhaben search URLs (multi-URL, forward pagination):**

```json
{
  "mode": "url",
  "urls": [
    "https://www.willhaben.at/iad/immobilien/eigentumswohnung/wien",
    "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?YEAR_MODEL_FROM=2018"
  ],
  "maxPages": 5,
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AT" }
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/willhaben-at-scraper").call(run_input={
    "mode": "search",
    "category": "gebrauchtwagen/auto/gebrauchtwagenboerse",
    "maxListings": 20,
    "proxy": {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AT"},
})
for listing in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(listing["title"], listing["priceDisplay"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/willhaben-at-scraper').call({
  mode: 'search',
  category: 'gebrauchtwagen/auto/gebrauchtwagenboerse',
  maxListings: 20,
  proxy: { useApifyProxy: true, apifyProxyGroups: ['RESIDENTIAL'], apifyProxyCountry: 'AT' },
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Result caps and Max pages

**Max listings** caps the total number of records returned across every search or URL in one run (`0` for unlimited). **Max pages** optionally caps how many result pages are walked per search or URL (about 25 listings per page for most categories, 90 for Jobs); leave it empty to walk every result page the site serves.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one specific interrupted run: paste its run or dataset ID and this run skips every listing that run already collected, matched on the same `id` field, so you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for running the same search again and again, for example daily. The first run for a given search returns everything as `NEW`. Later runs return `NEW` and `UPDATED` (with `changedFields`) by default; `UNCHANGED` listings are suppressed, neither returned nor billed, unless `emitUnchanged` is on. `EXPIRED` is only produced after a run that fully scans the tracked search (not capped by `maxListings`, not a Resume run), and only when `emitExpired` is on. `REAPPEARED` only appears for a listing that an earlier complete-scan run with `emitExpired` on had recorded as gone. State is kept separately per search/category/URL/filter combination unless you set `stateKey` to name or deliberately share it. Every field is compared by default (no fields are excluded automatically); use `ignoreFieldsForChanges` to exclude fields you consider noise, for example `changedDate` or `mainImage`. Ignored fields are still returned but never trigger `UPDATED` or appear in `changedFields`. With incremental mode off, output is exactly as before.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` builds a query from the filters below; `url` scrapes pasted search-page links. |
| `category` | string | `kaufen-und-verkaufen/marktplatz` | Section of willhaben.at to scrape (Search mode). Includes Jobs, which returns job-specific fields and ignores the price and area filters. |
| `categoryPath` | string | none | Advanced: override Category with any willhaben SEO path. Leave empty to use Category above. |
| `keyword` | string | none | Free-text keyword within the category. Leave empty to list everything in the category. |
| `areaId` | integer | none | willhaben location ID to restrict results to a region or district, for example `900` for Vienna. Not applied to the Jobs category. |
| `region` | string | none | Plain region name such as `Wien`, `Tirol` or `Niederösterreich`, used when Area ID is empty. An unknown name stops the run with a clear message. |
| `minPrice` | integer | none | Minimum price filter, in euros. Not applied to the Jobs category. |
| `maxPrice` | integer | none | Maximum price filter, in euros. |
| `sort` | string | `default` | One of `default`, `newest`, `price-asc`, `price-desc`. The price-based sorts do not apply to the Jobs category. |
| `customParameters` | array | none (prefill: empty list) | Extra raw willhaben query parameters as `KEY=VALUE` strings, one per line, for example `YEAR_MODEL_FROM=2018`. |
| `urls` | array | 17 example links, one per supported URL shape (same as the prefill) | willhaben URLs to scrape (URL mode). Multiple URLs are supported; filter fields above are ignored in this mode. With the default list, Max listings (default 20) is shared equally between links, so only the first links are reached unless you raise it. |
| `startUrl` | string | empty | Single willhaben.at link; shortcut that switches to URL mode and scrapes only that link (replaces `urls`). |
| `fetchDetails` | boolean | `false` | Open each listing's detail page to add contact name, dealer or organisation info, address detail and the full attribute set. Costs an extra fetch per listing. |
| `maxListings` | integer | `20` | Maximum number of listings to scrape across all searches. Set `0` for unlimited. |
| `maxItems` | integer | empty | Alias of `maxListings`; used when `maxListings` is left at its default of `20`. |
| `maxPages` | integer | none (omitted or `0` means walk every result page) | Optional bound on result pages walked per search or URL. |
| `resumeFromRunId` | string | none | Paste a previous run ID (or dataset ID) from this actor to skip listings already collected there. For recurring monitoring of the same search, use `incrementalMode` instead. |
| `incrementalMode` | boolean | `false` | Turn on for recurring monitoring of the same search. See the Resume and recurring updates section above for exactly what each run returns. |
| `stateKey` | string | none | Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to derive a key automatically from the search/category/URL/filter settings. |
| `emitUnchanged` | boolean | `false` | Incremental mode only: also return, and bill, listings unchanged since the last run, marked `UNCHANGED`. |
| `emitExpired` | boolean | `false` | Incremental mode only: also return, and bill, listings from a previous run no longer found, marked `EXPIRED`. Only produced after a run that fully scanned the tracked search, not one capped by `maxListings` and not a Resume run. |
| `ignoreFieldsForChanges` | array | none | Incremental mode only: field names (or dotted nested paths such as `advertiserInfo.label`) that must not make a listing count as `UPDATED`. They stay in the output and are left out of `changedFields`. Changing the list does not cause a one-time wave of `UPDATED` rows. |
| `proxy` | object | none (prefill: Apify Proxy, Residential group, country AT) | Connection settings. Residential, country AT is recommended and is the prefill; many datacenter exits are refused. See Plan Requirement below. |
| `mcpConnectors` | array | none | Optional: send a summary of each listing to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | none | Notion connector only: page under which item pages are created. |
| `maxNotifyListings` | integer | `50` | Cap on items written to each connector per run. Does not affect the dataset. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live listing.

```json
{
  "id": "000000001",
  "uuid": "00000000-0000-0000-0000-000000000000",
  "url": "https://www.willhaben.at/iad/gebrauchtwagen/d/auto/sample-000000001/",
  "title": "Sample Listing Title",
  "description": "Full listing description text appears here.",
  "price": 7800,
  "priceDisplay": "€ 7.800",
  "category": "gebrauchtwagen/auto/gebrauchtwagenboerse",
  "make": "SampleMake",
  "model": "SampleModel",
  "mileage": 120000,
  "year": 2019,
  "fuel": "Benzin",
  "transmission": "Automatik",
  "condition": "Gebrauchtwagen",
  "country": "Österreich",
  "state": "Wien",
  "district": "Wien",
  "postcode": "1010",
  "locationName": "Sample Location",
  "latitude": 48.2000,
  "longitude": 16.3700,
  "publishedDate": "2026-01-01T00:00:00.000Z",
  "changedDate": "2026-01-01T00:00:00.000Z",
  "isPrivate": true,
  "advertiserLabel": "Privat",
  "imageCount": 2,
  "mainImage": "https://cache.willhaben.at/mmo/0/000/000/000_0.jpg",
  "images": [
    "https://cache.willhaben.at/mmo/0/000/000/000_0.jpg",
    "https://cache.willhaben.at/mmo/0/000/000/000_1.jpg"
  ]
}
```

### Plan Requirement

willhaben.at blocks connections per IP address rather than per connection type. In our testing datacenter connections are refused on many exits (some pages, such as sorted price filters, the motorcycle category and share links, never loaded from datacenter ranges), while Austrian residential connections loaded the pages reliably, so Residential with country AT is recommended and is the prefill. Use an Apify plan that includes the Residential proxy group, and leave the Proxy input at its default prefill (Residential, country AT) or set it yourself. The actor rotates between Austrian and German residential exits automatically, and automatically retries with a different connection when one is refused. If your plan has no residential access, it continues on a backup residential connection and says so in the log. If no page can be loaded at all, the run ends as failed with a message explaining why, rather than quietly returning an empty dataset that looks like a clean success.

### FAQ

#### How much does it cost?

You pay per record returned, with detail-page enrichment billed as a separate surcharge (event `detail-enrichment`, one unit per delivered record whose detail page was fetched, charged as each record is saved) only when Fetch detail pages is on. When the run time limit is close, the actor stops taking new pages and saves what it has already collected. The **Pricing** tab on the actor page shows current rates. Use **Max listings** (and optionally **Max pages**) to cap the cost of any run.

#### Is it legal to scrape willhaben.at?

This actor collects only publicly available listing data. You are responsible for how you use it: follow willhaben's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Contact details and photos may carry their own rights beyond simple facts like price or location.

#### Can I get only new or changed listings on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only `NEW` and `UPDATED` listings for that search by default, and unchanged ones are not billed.

#### What is the difference between Resume and Incremental mode?

**Resume from run ID** continues one specific interrupted run: paste its run or dataset ID and this run skips every listing that run already collected. **Incremental mode** is for running the same search again and again, for example daily, and getting only what changed; it keeps its own state per search/category/URL/filter combination (or per State key, if you set one) instead of needing a run ID pasted in every time.

#### Do REAPPEARED and EXPIRED listings always show up in incremental mode?

No. `EXPIRED` is only produced after a run that fully scans the tracked search with **Emit expired** on; a run capped by Max listings, or a Resume run, cannot tell "gone" apart from "not reached yet", so expiry detection is skipped for that run and the previous state is kept as-is. `REAPPEARED` only appears for a listing that an earlier run had already marked `EXPIRED` under those conditions. If you have never turned on Emit expired, your incremental runs will only ever show `NEW` and `UPDATED` (plus `UNCHANGED` if you turn that on).

#### Why did my run fail instead of returning an empty dataset?

A run fails outright only in specific cases: a Resume from run ID pointing to a run or dataset ID the actor cannot read, or Incremental mode combined with Resume against a search that already has saved incremental state (remove one, or use a different State key). Otherwise, a run that finds nothing still succeeds, returning 0 items with a status message explaining why, most often a missing or rejected connection, so "nothing found" is never confused with "the run crashed".

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push a summary of each listing into Notion, Linear or Airtable.

### 🔗 Want more car data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏷️ <a href="https://apify.com/abotapi/avito-ru-scraper"><b>Avito.ru Scraper</b></a><br>Scrape structured listings from Avito.ru by region, category, filters, or direct URL...</td><td>🏷️ <a href="https://apify.com/abotapi/gumtree-uk-scraper"><b>Gumtree.com Scraper</b></a><br>Scrape Gumtree.com classifieds across motors, property, jobs, services, pets and goods...</td></tr>
<tr><td>🏷️ <a href="https://apify.com/abotapi/leboncoin-fr-scraper"><b>Leboncoin FR</b></a><br>Pull public leboncoin.fr listings across cars, property, jobs, electronics, furniture...</td><td>🏷️ <a href="https://apify.com/abotapi/subito-it-scraper"><b>Subito.it Scraper</b></a><br>Scrape Subito.it listings across cars, real estate, marketplace, jobs, and more. Search...</td></tr>
<tr><td>🚗 <a href="https://apify.com/abotapi/autotrader"><b>AutoTrader UK Scraper</b></a><br>Pull structured vehicle listings from autotrader.co.uk at scale. Search by filters or use...</td><td>🚗 <a href="https://apify.com/abotapi/autotrader-com-scraper"><b>Autotrader US</b></a><br>Scrape US car listings from Autotrader by make, model, ZIP, price, year, body style...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/willhaben-at-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

How to find listings: build a search from filters, or paste search-page URLs.

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

Listing category (Search mode). Pick a section of willhaben.at to scrape.

## `categoryPath` (type: `string`):

Optional. Override Category with any willhaben SEO path, e.g. 'immobilien/anlageobjekte' or 'gebrauchtwagen/motorrad'. Leave empty to use the Category above. Copy the path that follows '/iad/' from a willhaben URL.

## `keyword` (type: `string`):

Free-text keyword to search within the category, e.g. 'iphone', 'bmw', 'altbau'. Leave empty to list everything in the category.

## `areaId` (type: `integer`):

Optional willhaben location ID to restrict results to a region/district. Example: 900 = Vienna (Wien). Find the ID in the URL after picking a location on willhaben.at. Leave empty for all of Austria. Not applied to the Jobs category.

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

Optional plain region name instead of an Area ID, e.g. 'Wien', 'Salzburg', 'Tirol', 'Niederösterreich'. Ignored when Area ID is set. An unknown name stops the run with a clear message.

## `minPrice` (type: `integer`):

Minimum price filter. Leave empty for no minimum. Not applied to the Jobs category.

## `maxPrice` (type: `integer`):

Maximum price filter. Leave empty for no maximum.

## `sort` (type: `string`):

Result ordering (Search mode).

## `customParameters` (type: `array`):

Optional raw willhaben query parameters as KEY=VALUE strings, e.g. 'YEAR_MODEL_FROM=2018', 'MILEAGE_TO=100000', 'CAR_MODEL/MAKE=139'. Lets you replicate any filter the site offers. One per line.

## `urls` (type: `array`):

willhaben.at URLs to scrape (URL mode): search pages, category pages, paginated or filtered links, single classified ads (/d/ pages or /iad/object?adId=), job searches and single job pages. Multi-URL supported. Filter fields above are ignored in this mode. Pagination starts at the page in the URL and walks forward.

## `startUrl` (type: `string`):

Shortcut for one willhaben.at link: scrapes only that link in URL mode and replaces Search URLs. Leave empty to use the other fields.

## `fetchDetails` (type: `boolean`):

Open each listing's detail page to add contact name, dealer/organisation info, address detail and the full attribute set. Slower and costs an extra fetch per listing. Leave off for fast list-only scraping (the list already carries ~40 fields).

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

Maximum number of listings to scrape across all searches. Set 0 for unlimited.

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

Alias of Max listings. Used when Max listings is left at its default of 20.

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

Optional bound on result pages walked per search/URL (~25 listings/page). Leave empty to walk every result page.

## `proxy` (type: `object`):

willhaben.at only accepts traffic from Austrian (or nearby EU) residential connections. Datacenter proxies are rejected. Use Apify Residential with country AT for reliable results. The actor rotates AT and DE residential exits automatically.

## `resumeFromRunId` (type: `string`):

Optional: paste a previous run ID (or dataset ID) from this actor. Listings already collected there are skipped, so this run only returns new ones. Leave empty for a normal run. For recurring daily/weekly monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching listings as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED listings. Turn on "Emit unchanged" or "Emit expired" only when you also want those listings returned (and billed). State is kept separately for each search/category/URL/filter combination; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from run ID above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search/category/URL/filter settings -- different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. This returns -- and bills -- extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search -- not when Max listings capped it or when Resume was used. This returns -- and bills -- extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Incremental mode only. Field names that must NOT make a listing count as UPDATED, for example changedDate or mainImage. Use a dotted path for nested fields (advertiserInfo.label); it applies to every element of a list. Ignored fields are still returned in the output and never appear in changedFields. Leave empty to compare every field.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "category": "kaufen-und-verkaufen/marktplatz",
  "sort": "default",
  "customParameters": [],
  "urls": [
    "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
    "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone",
    "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&page=2",
    "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?rows=5&page=3",
    "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?CAR_MODEL/MAKE=1008&sort=1&PRICE_TO=10000",
    "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&PRICE_FROM=100",
    "https://www.willhaben.at/iad/immobilien/mietwohnungen/wien",
    "https://www.willhaben.at/iad/immobilien/eigentumswohnung/wien/wien-1010-innere-stadt",
    "https://www.willhaben.at/iad/immobilien/haus-kaufen/niederoesterreich?rows=10&areaId=3",
    "https://www.willhaben.at/iad/immobilien/mietwohnungen?PRICE_FROM=500&PRICE_TO=900&sort=1",
    "https://www.willhaben.at/iad/gebrauchtwagen/d/auto/bmw-x3-30e-phev-xdrive-aut-1015504999/",
    "https://www.willhaben.at/iad/object?adId=1015504999",
    "https://willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
    "https://www.willhaben.at/jobs/suche?keyword=koch",
    "https://www.willhaben.at/jobs/suche?keyword=koch&page=2",
    "https://www.willhaben.at/jobs/job/koch-koechin/13279237",
    "https://www.willhaben.at/iad/gebrauchtwagen/motorrad"
  ],
  "fetchDetails": false,
  "maxListings": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AT"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": [],
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `jobs` (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 = {
    "mode": "search",
    "customParameters": [],
    "urls": [
        "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
        "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone",
        "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&page=2",
        "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?rows=5&page=3",
        "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?CAR_MODEL/MAKE=1008&sort=1&PRICE_TO=10000",
        "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&PRICE_FROM=100",
        "https://www.willhaben.at/iad/immobilien/mietwohnungen/wien",
        "https://www.willhaben.at/iad/immobilien/eigentumswohnung/wien/wien-1010-innere-stadt",
        "https://www.willhaben.at/iad/immobilien/haus-kaufen/niederoesterreich?rows=10&areaId=3",
        "https://www.willhaben.at/iad/immobilien/mietwohnungen?PRICE_FROM=500&PRICE_TO=900&sort=1",
        "https://www.willhaben.at/iad/gebrauchtwagen/d/auto/bmw-x3-30e-phev-xdrive-aut-1015504999/",
        "https://www.willhaben.at/iad/object?adId=1015504999",
        "https://willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
        "https://www.willhaben.at/jobs/suche?keyword=koch",
        "https://www.willhaben.at/jobs/suche?keyword=koch&page=2",
        "https://www.willhaben.at/jobs/job/koch-koechin/13279237",
        "https://www.willhaben.at/iad/gebrauchtwagen/motorrad"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "AT"
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/willhaben-at-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 = {
    "mode": "search",
    "customParameters": [],
    "urls": [
        "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
        "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone",
        "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&page=2",
        "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?rows=5&page=3",
        "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?CAR_MODEL/MAKE=1008&sort=1&PRICE_TO=10000",
        "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&PRICE_FROM=100",
        "https://www.willhaben.at/iad/immobilien/mietwohnungen/wien",
        "https://www.willhaben.at/iad/immobilien/eigentumswohnung/wien/wien-1010-innere-stadt",
        "https://www.willhaben.at/iad/immobilien/haus-kaufen/niederoesterreich?rows=10&areaId=3",
        "https://www.willhaben.at/iad/immobilien/mietwohnungen?PRICE_FROM=500&PRICE_TO=900&sort=1",
        "https://www.willhaben.at/iad/gebrauchtwagen/d/auto/bmw-x3-30e-phev-xdrive-aut-1015504999/",
        "https://www.willhaben.at/iad/object?adId=1015504999",
        "https://willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
        "https://www.willhaben.at/jobs/suche?keyword=koch",
        "https://www.willhaben.at/jobs/suche?keyword=koch&page=2",
        "https://www.willhaben.at/jobs/job/koch-koechin/13279237",
        "https://www.willhaben.at/iad/gebrauchtwagen/motorrad",
    ],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "AT",
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/willhaben-at-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 '{
  "mode": "search",
  "customParameters": [],
  "urls": [
    "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
    "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone",
    "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&page=2",
    "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?rows=5&page=3",
    "https://www.willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse?CAR_MODEL/MAKE=1008&sort=1&PRICE_TO=10000",
    "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=iphone&PRICE_FROM=100",
    "https://www.willhaben.at/iad/immobilien/mietwohnungen/wien",
    "https://www.willhaben.at/iad/immobilien/eigentumswohnung/wien/wien-1010-innere-stadt",
    "https://www.willhaben.at/iad/immobilien/haus-kaufen/niederoesterreich?rows=10&areaId=3",
    "https://www.willhaben.at/iad/immobilien/mietwohnungen?PRICE_FROM=500&PRICE_TO=900&sort=1",
    "https://www.willhaben.at/iad/gebrauchtwagen/d/auto/bmw-x3-30e-phev-xdrive-aut-1015504999/",
    "https://www.willhaben.at/iad/object?adId=1015504999",
    "https://willhaben.at/iad/gebrauchtwagen/auto/gebrauchtwagenboerse",
    "https://www.willhaben.at/jobs/suche?keyword=koch",
    "https://www.willhaben.at/jobs/suche?keyword=koch&page=2",
    "https://www.willhaben.at/jobs/job/koch-koechin/13279237",
    "https://www.willhaben.at/iad/gebrauchtwagen/motorrad"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "AT"
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/willhaben-at-scraper --silent --output-dataset

```

## MCP server setup

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