# Willhaben.at $0.9💰 Marktplatz · Immobilien · Auto ·Job Scraper (`memo23/willhaben-scraper`) Actor

💰$0.9 per 1000 results\&unlimited extraction. Extract structured data from willhaben.at across Marktplatz, Immobilien, Auto & Motor, and Jobs. Fields: title, price/rent, area, rooms, address, images, contact, listing IDs; search and detail trace URLs. Jobs: role, company, location, emails

- **URL**: https://apify.com/memo23/willhaben-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Real estate, Jobs, Automation
- **Features:** Limited permissions
- **Stats:** 124 total users, 20 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.90 / 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/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

### Overview

Extract structured **listings** from **[willhaben.at](https://www.willhaben.at/)** (Austria). The site is organised into four main areas—**Marktplatz**, **Immobilien**, **Auto & Motor**, and **Jobs**—as shown in the main navigation. Paste a search link and the actor returns every listing in that search, each with its full details. You get **one dataset row per listing**, with a **normalised flat schema** plus **Willhaben attribute keys** (and optional widget-derived fields) for spreadsheets.

Use it to **monitor ads**, **export price and location data**, or **feed analytics**; **Jobs** runs use a **dedicated job object shape** (see **Output Structure**).

***

### Features

- **Four supported verticals** (aligned with Willhaben’s top navigation):
  - **Marktplatz** — classifieds under **`/iad/kaufen-und-verkaufen/…`** (marketplace).
  - **Immobilien** — real estate under **`/iad/immobilien/…`**.
  - **Auto & Motor** — vehicles under **`/iad/auto/…`** (and related iad auto paths).
  - **Jobs** — job search under **`/jobs/…`**.

- **Search + full details**:
  - **Search**: each **www** link returns the listings of that search, with the filters you set on the site.
  - **Details**: every listing (or job) found is opened for its full details, up to **`maxItems`** **per `startUrls` entry** (each www link has its own budget).

- **Pagination**:
  - Every result page of a search is followed until **`maxItems`** is reached. Filters in your link, including keys like `ESTATE_SIZE/LIVING_AREA_FROM`, are kept exactly as written.
  - **Jobs** searches page through their results the same way.

- **Flattened export** (**`flattenOutput`: true**):
  - One **flat** object per listing: mapped fields **plus** all **`listingDocument.attributesFlat`** keys at the **top level** (collisions → **`attr_<name>`**). The sample below reflects this mode.

***

### How to Use

1. **Set Up**: Apify account and this actor (or run locally with `apify run` / `npm run start:dev`).
2. **Provide Input**: Add one or more **Willhaben** URLs under **`startUrls`** (`https://www.willhaben.at/…`).
3. **Configure**: Set **`maxItems`** (cap on **detail** requests queued **per start URL**), **`flattenOutput`**, concurrency, retries, and **proxy** (residential proxies recommended; the default works out of the box).
4. **Run & Export**: Download **JSON** / **CSV** from the dataset. Failed requests are retried automatically.

#### Usage Limitations

**Free / non-paying Apify users** may be subject to platform limits on dataset items or charges. **Paid users** typically get higher limits; adjust **`maxItems`** to control how many **detail** pages are fetched **per start URL** (each link in **`startUrls`** can queue up to **`maxItems`** details). Residential proxies are recommended; the default works out of the box.

***

### Input Configuration

Example input:

```json
{
    "startUrls": [
        {
            "url": "https://www.willhaben.at/jobs/suche?employment_type=109&location=Salzburg&region=14096"
        },
        {
            "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?sfId=0db5c6aa-6f06-4760-a077-5d2d88453916&rows=30&areaId=5&page=1&PRICE_FROM=150&PRICE_TO=1300"
        },
        {
            "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz/kinderfeste-kinderfeiern-4282/a/zustand-neu-22?sfId=60dce79c-cd0d-421b-ab8a-d278d9dea396&rows=30&isNavigation=true&PRICE_FROM=0&PRICE_TO=1"
        }
    ],
    "maxItems": 30,
    "flattenOutput": true,
    "maxConcurrency": 50,
    "minConcurrency": 1,
    "maxRequestRetries": 100,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "AT"
    }
}
```

#### Input Fields Explanation

- **startUrls** (`startUrls`): Array of objects with **`url`** pointing to a **www.willhaben.at** listing hub—**Jobs** (`/jobs/…`), **Immobilien**, **Marktplatz**, or **Auto** under **`/iad/…`**. Each link returns the listings of that search.
- **maxItems** (`maxItems`): Maximum number of listings whose full details are fetched, **separately for each** object in **`startUrls`** (keyed by that row’s **`url`**). Two start URLs with **`maxItems: 30`** can yield up to **60** detail scrapes. Default **30** (see actor schema).
- **flattenOutput** (`flattenOutput`): When **true**, each row is a **single flat object** (mapped fields + attribute flat index at the top level for **iad** verticals). **Jobs**: adds **`willhabenKind: jobs`** and **`source: willhaben_jobs_detail`** around the job object. When **false**, **iad** rows include nested **`listingDocument`** and **`actorMeta`**; **Jobs** rows are the job object + **`apify_*`** only. Default **false** in schema; the **Immobilien** sample below uses **flattened** shape; **`data-jobs.json`** shows flattened **Jobs**.
- **maxConcurrency** (`maxConcurrency`): Maximum number of search and detail pages read in parallel. **`minConcurrency`** in the schema is ignored by this actor.
- **maxRequestRetries** (`maxRequestRetries`): How many times a failed search or detail page is retried before it is skipped.
- **proxy** (`proxy`): Residential proxies recommended. The default works out of the box; set this only to use your own.

***

### Output Structure

The dataset contains **one row per listing** (or **one row per job**) after the detail step.

**iad / Auto / Marktplatz / Immobilien** (not Jobs):

- **`source: willhaben_detail`**
- **`willhabenKind`**: **`real_estate`**, **`marketplace`**, or **`car`**
- With **`flattenOutput: true`**, see the **first sample** below (`data.json`).
- With **`flattenOutput: false`**, the same logical data appears under nested **`listingDocument`** and **`actorMeta`**.

**Jobs**:

- Native-style **job object** fields (**`id`**, **`title`**, **`company`**, **`employmentModes`**, **`jobLocations`**, …) plus **`apify_scrapedAt`** and **`apify_extracted_emails`**.
- With **`flattenOutput: true`**, rows also include **`willhabenKind: jobs`** and **`source: willhaben_jobs_detail`** (see **`data-jobs.json`** sample below).
- With **`flattenOutput: false`**, rows are **only** the job fields plus the two **`apify_*`** fields (no **`listingDocument`**).

Filter **non-Jobs** rows with **`source === 'willhaben_detail'`**. Filter **Jobs** with **`willhabenKind === 'jobs'`** (and optionally **`source === 'willhaben_jobs_detail'`** when flattened).

***

#### Sample: `willhaben_detail` (first object in `data.json`)

The JSON below is based on the **first record** of a real export (**`willhabenKind: real_estate`**, **`flattenOutput: true`**). Long strings, links and image arrays are **shortened** for the README; the on-disk file contains the **full** values. **`_readme_note`** is documentation-only and does not appear in live output.

```json
{
    "source": "willhaben_detail",
    "willhabenKind": "real_estate",
    "listingId": null,
    "url": null,
    "platform": "willhaben",
    "scrapedAt": "2026-04-07T06:43:48.489Z",
    "publishedAt": null,
    "updatedAt": null,
    "objectType": null,
    "transactionType": "rent",
    "title": "Erdgeschosswohnung mit Garten",
    "description": null,
    "price": null,
    "priceText": null,
    "currency": "EUR",
    "pricePerM2": null,
    "rentGross": 1300,
    "rentNet": null,
    "rentPerM2": null,
    "operatingCosts": null,
    "heatingCosts": null,
    "parkingCosts": null,
    "additionalCostsTotal": null,
    "livingAreaM2": null,
    "usableAreaM2": null,
    "totalAreaM2": null,
    "plotAreaM2": null,
    "roomCount": null,
    "bedroomCount": null,
    "bathroomCount": null,
    "floor": null,
    "street": null,
    "postalCode": null,
    "city": null,
    "district": null,
    "region": null,
    "country": null,
    "latitude": null,
    "longitude": null,
    "hasSeaView": false,
    "distanceToSea": null,
    "distanceToCenter": null,
    "hasMountainView": false,
    "yearBuilt": null,
    "condition": null,
    "energyCertificateAvailable": false,
    "energyClass": null,
    "heatingType": null,
    "hasBalcony": false,
    "hasTerrace": false,
    "hasLoggia": false,
    "hasGarden": false,
    "hasYard": false,
    "hasPool": false,
    "hasGarage": false,
    "hasCarport": false,
    "hasParkingSpace": false,
    "hasStorageRoom": false,
    "hasElevator": false,
    "hasBasement": false,
    "hasAirConditioning": false,
    "hasBuiltInKitchen": false,
    "isBarrierFree": false,
    "sellerName": null,
    "sellerType": null,
    "phone": null,
    "email": null,
    "imageUrls": [],
    "videoUrls": [],
    "locationText": null,
    "rawHtmlSnippet": null,
    "apify_actor": "willhaben-cheerio",
    "apify_scrapedAt": "2026-04-07T06:43:48.489Z",
    "detailUrl": "https://www.willhaben.at/iad/immobilien/d/mietwohnungen/…-1774459287/",
    "originalInputUrl": "[redacted input URL]",
    "listPage": 1,
    "listingDocumentId": "1355309481",
    "listingDocumentUuid": "95079344-3e17-4b08-8be2-b34e9d305250",
    "LOCATION": "Aigelsbrunn",
    "POSTCODE": "5204",
    "STATE": "Salzburg",
    "BODY_DYN": "Zur Vermietung gelangt eine moderne und gepflegte Erdgeschosswohnung …",
    "ORG_UUID": "0bf5c502-ee07-4290-aa9d-c82a3667e9e5",
    "ESTATE_SIZE/LIVING_AREA": "77",
    "DISTRICT": "Salzburg-Umgebung",
    "HEADING": "Erdgeschosswohnung mit Garten",
    "LOCATION_QUALITY": "1.0",
    "PUBLISHED": "1775498880000",
    "COUNTRY": "Österreich",
    "LOCATION_ID": "113901",
    "PROPERTY_TYPE": "Erdgeschoßwohnung",
    "NUMBER_OF_ROOMS": "3",
    "ADTYPE_ID": "2",
    "PROPERTY_TYPE_ID": "105",
    "ADID": "1355309481",
    "ORGID": "24712601",
    "SEO_URL": "immobilien/d/mietwohnungen/salzburg/salzburg-umgebung/erdgeschosswohnung-mit-garten-1355309481/",
    "ALL_IMAGE_URLS": "1/135/530/9481_317321052.jpg;1/135/530/9481_731380688.jpg;…",
    "PUBLISHED_String": "2026-04-06T20:08:00Z",
    "ESTATE_PREFERENCE": "15, 24, 250, 27, 28",
    "categorytreeids": "7276",
    "RENT/PER_MONTH_LETTINGS": "1300.0",
    "PRODUCT_ID": "200",
    "MMO": "1/135/530/9481_317321052.jpg",
    "ROOMS": "3X3",
    "AD_UUID": "95079344-3e17-4b08-8be2-b34e9d305250",
    "ADDRESS": "Haidach 14",
    "COORDINATES": "47.99015,13.24095",
    "PRICE": "1300",
    "PRICE_FOR_DISPLAY": "€ 1.300",
    "ESTATE_SIZE": "77",
    "ISPRIVATE": "1",
    "PROPERTY_TYPE_FLAT": "true",
    "DISPLAY/Gesamtmiete": "€ 1.300",
    "DISPLAY/Wohnfläche": "77 m²",
    "DISPLAY/Zimmer": "3",
    "WIDGET_TEXT/Objektstandort": "Haidach 14,\n5204 Aigelsbrunn, Salzburg-Umgebung, Salzburg",
    "WIDGET/Objektinformation/Objekttyp": "Erdgeschoßwohnung",
    "WIDGET/Objektinformation/Verfügbar": "nach Vereinbarung",
    "WIDGET/Objektinformation/Bautyp": "Altbau",
    "WIDGET/Objektinformation/Heizung": "Pellets",
    "WIDGET/Objektinformation/Zustand": "Renoviert",
    "DESCRIPTION_FROM_WIDGET": "Zur Vermietung gelangt eine moderne …",
    "WIDGET_TEXT/Lage": "Die Wohnung befindet sich in Haidach …",
    "WIDGET_TEXT/untitled": "Privatperson",
    "ALL_IMAGE_REFERENCE_URLS": "https://cache.willhaben.at/mmo/1/135/530/9481_317321052.jpg;…",
    "IMAGE_REFERENCE_URL": [
        "https://cache.willhaben.at/mmo/1/135/530/9481_317321052.jpg",
        "https://cache.willhaben.at/mmo/1/135/530/9481_731380688.jpg"
    ],
    "_readme_note": "Omitted: remaining IMAGE_REFERENCE_URL entries and the full description text."
}
```

***

#### Output fields (`willhaben_detail`, flattened) — field-by-field

##### Row identity and crawl metadata

- **`source`** — Always **`willhaben_detail`** for rows produced from the list → detail pipeline.
- **`willhabenKind`** — Vertical classifier: **`real_estate`**, **`marketplace`**, **`car`**, or **`jobs`** (Jobs rows use a different top-level schema; see **Output Structure**).
- **`listingId`** — Listing id in the **mapped** schema when the mapper fills it; may be **`null`** in flattened runs if the id appears only under **`ADID`** / **`listingDocumentId`**.
- **`url`** — Public www URL for the listing when the mapper sets it; often **`null`** when the listing has no public page link.
- **`platform`** — Always **`willhaben`**.
- **`scrapedAt`** — ISO timestamp when the row was assembled (mapper / push time).

##### Mapped schema — publication and transaction

- **`publishedAt`** — Normalised “published” time string when the mapper fills it from attributes; else **`null`** (raw publish values may still appear under **`PUBLISHED`** / **`PUBLISHED_String`**).
- **`updatedAt`** — Last-updated field when mapped; else **`null`**.
- **`objectType`** — High-level object type when mapped (e.g. property type); else **`null`**.
- **`transactionType`** — **`buy`** or **`rent`** inferred from the iad path (e.g. **Mietwohnung** → **`rent`**).

##### Mapped schema — title, description, money

- **`title`** — Listing heading from the mapper (often aligns with **`HEADING`**).
- **`description`** — Long description in the mapped schema when extracted; else **`null`** (teaser may be in **`BODY_DYN`** or **`DESCRIPTION_FROM_WIDGET`**).
- **`price`** — Numeric price when mapped as a single number; else **`null`**.
- **`priceText`** — Human-readable price string when mapped; else **`null`**.
- **`currency`** — Currency code when set (e.g. **`EUR`**).
- **`pricePerM2`** — Price per square metre when mapped; else **`null`**.
- **`rentGross`** — Gross rent per month (or period per mapper rules) when applicable; e.g. **1300**.
- **`rentNet`** — Net rent when mapped; else **`null`**.
- **`rentPerM2`** — Rent per m² when mapped; else **`null`**.
- **`operatingCosts`** — Nebenkosten / operating costs when mapped; else **`null`**.
- **`heatingCosts`** — Heating costs when mapped; else **`null`**.
- **`parkingCosts`** — Parking costs when mapped; else **`null`**.
- **`additionalCostsTotal`** — Aggregated extra costs when mapped; else **`null`**.

##### Mapped schema — areas and rooms

- **`livingAreaM2`** — Living area in m² when mapped; else **`null`** (see **`ESTATE_SIZE`** / **`ESTATE_SIZE/LIVING_AREA`**).
- **`usableAreaM2`** — Usable area when mapped; else **`null`**.
- **`totalAreaM2`** — Total area when mapped; else **`null`**.
- **`plotAreaM2`** — Plot / land area when mapped; else **`null`**.
- **`roomCount`** — Room count when mapped; else **`null`** (see **`NUMBER_OF_ROOMS`** / **`ROOMS`**).
- **`bedroomCount`** — Bedrooms when mapped; else **`null`**.
- **`bathroomCount`** — Bathrooms when mapped; else **`null`**.
- **`floor`** — Floor / storey when mapped; else **`null`**.

##### Mapped schema — address and geo

- **`street`** — Street line when mapped; else **`null`** (see **`ADDRESS`**).
- **`postalCode`** — PLZ when mapped; else **`null`** (see **`POSTCODE`**).
- **`city`** — City / Ort when mapped; else **`null`** (see **`LOCATION`**).
- **`district`** — Bezirk when mapped; else **`null`** (see **`DISTRICT`**).
- **`region`** — Bundesland / region when mapped; else **`null`** (see **`STATE`**).
- **`country`** — Country when mapped; else **`null`** (see **`COUNTRY`**).
- **`latitude`** — Latitude when mapped; else **`null`** (may parse from **`COORDINATES`**).
- **`longitude`** — Longitude when mapped; else **`null`**.

##### Mapped schema — features (booleans / enums)

- **`hasSeaView`**, **`hasMountainView`**, **`hasBalcony`**, **`hasTerrace`**, **`hasLoggia`**, **`hasGarden`**, **`hasYard`**, **`hasPool`**, **`hasGarage`**, **`hasCarport`**, **`hasParkingSpace`**, **`hasStorageRoom`**, **`hasElevator`**, **`hasBasement`**, **`hasAirConditioning`**, **`hasBuiltInKitchen`**, **`isBarrierFree`** — Feature flags from the mapper; **`false`** when not set from data.
- **`distanceToSea`**, **`distanceToCenter`** — Distance fields when mapped; else **`null`**.
- **`yearBuilt`**, **`condition`**, **`energyCertificateAvailable`**, **`energyClass`**, **`heatingType`** — Building / energy fields when mapped; else **`null`** / **`false`**.

##### Mapped schema — seller and media

- **`sellerName`** — Advertiser / org display name when mapped; else **`null`**.
- **`sellerType`** — Seller type when mapped (e.g. private vs dealer); else **`null`**.
- **`phone`**, **`email`** — Contact fields when mapped; else **`null`**.
- **`imageUrls`** — Array of main image URLs in the mapped schema (may be empty if images only appear under **`IMAGE_REFERENCE_URL`** / **`ALL_IMAGE_REFERENCE_URLS`**).
- **`videoUrls`** — Video URLs when mapped; else **`[]`**.
- **`locationText`** — Free-text location line when mapped; else **`null`**.
- **`rawHtmlSnippet`** — Reserved; usually **`null`**.

##### Apify and traceability

- **`apify_actor`** — Actor name (**`willhaben-cheerio`**).
- **`apify_scrapedAt`** — ISO timestamp when the dataset row was written.
- **`detailUrl`** — The listing's public page on willhaben.at. When the listing has no page link of its own, a willhaben.at link by ad id that opens the same page.
- **`originalInputUrl`** — The **www** start URL from input that led to this crawl branch.
- **`listPage`** — **1-based** list page index on which this listing appeared.
- **`listingDocumentId`** — Numeric **ad id** (string) in the merged listing document.
- **`listingDocumentUuid`** — **UUID** of the advert in the merged listing document.

##### Willhaben attributes (flattened from `listingDocument.attributesFlat`)

- **`LOCATION`** — Location label from search / advert attributes (e.g. locality name).
- **`POSTCODE`** — Postal code (**PLZ**).
- **`STATE`** — Austrian **Bundesland** (or state label).
- **`BODY_DYN`** — Short teaser text shown on the listing.
- **`ORG_UUID`** — Organisation UUID associated with the advertiser.
- **`ESTATE_SIZE/LIVING_AREA`** — Living area as string (m²), from slashed attribute name.
- **`DISTRICT`** — District (**Bezirk**) label.
- **`HEADING`** — Listing headline from Willhaben attributes.
- **`LOCATION_QUALITY`** — Willhaben's location precision score for the listing, when present.
- **`PUBLISHED`** — Publish time in **epoch milliseconds** as string.
- **`COUNTRY`** — Country label (e.g. **Österreich**).
- **`LOCATION_ID`** — Willhaben location id.
- **`PROPERTY_TYPE`** — Property type label (e.g. **Erdgeschoßwohnung**).
- **`NUMBER_OF_ROOMS`** — Room count as string.
- **`ADTYPE_ID`** — Ad type identifier.
- **`PROPERTY_TYPE_ID`** — Property type identifier.
- **`ADID`** — Advert id (string); same listing as **`listingDocumentId`** when aligned.
- **`ORGID`** — Organisation id.
- **`SEO_URL`** — SEO path segment for the listing on www.
- **`ALL_IMAGE_URLS`** — Semicolon-separated **relative** image paths.
- **`PUBLISHED_String`** — ISO-style publish date and time, as shown on the listing.
- **`ESTATE_PREFERENCE`** — Comma-separated preference / filter codes when present.
- **`categorytreeids`** — Category tree ids for navigation / classification.
- **`RENT/PER_MONTH_LETTINGS`** — Monthly rent as string from slashed attribute name.
- **`PRODUCT_ID`** — Product vertical id (e.g. real estate product code).
- **`MMO`** — Primary **MMO** image path (multi-media object key).
- **`ROOMS`** — Encoded rooms bucket (e.g. **`3X3`**) from Willhaben.
- **`AD_UUID`** — Advert UUID (matches **`listingDocumentUuid`** when aligned).
- **`ADDRESS`** — Street address line.
- **`COORDINATES`** — **`lat,lon`** string when present.
- **`PRICE`** — Raw price string from attributes.
- **`PRICE_FOR_DISPLAY`** — Localised display price (e.g. **€ 1.300**).
- **`ESTATE_SIZE`** — Size string (often living area in m²).
- **`ISPRIVATE`** — **`"1"`** / **`"0"`** style flag for private vs commercial when provided.
- **`PROPERTY_TYPE_FLAT`** — String boolean for flat / apartment classification when present.

##### Sections of the listing page

- **`DISPLAY/Gesamtmiete`**, **`DISPLAY/Wohnfläche`**, **`DISPLAY/Zimmer`** — The headline figures shown under the listing title (labels depend on locale and vertical).
- **`WIDGET_TEXT/Objektstandort`** — The listing's location text (Objektstandort section).
- **`WIDGET/Objektinformation/Objekttyp`**, **`Verfügbar`**, **`Bautyp`**, **`Heizung`**, **`Zustand`** — Each line of the listing's **Objektinformation** section, as label and value.
- **`DESCRIPTION_FROM_WIDGET`** — The full description (Beschreibung section) when present.
- **`WIDGET_TEXT/Lage`** — The location description (Lage section).
- **`WIDGET_TEXT/untitled`** — A text section without a heading (e.g. **Privatperson**).
- **`ALL_IMAGE_REFERENCE_URLS`** — Semicolon-separated **absolute** image URLs.
- **`IMAGE_REFERENCE_URL`** — Array of absolute image URLs (same images as above, as a list).

***

#### Sample: Jobs (first object in `data-jobs.json`)

The JSON below is the **first record** of a real **Jobs** export (**`flattenOutput: true`**). **`_readme_note`** is documentation-only and does not appear in live output.

```json
{
    "willhabenKind": "jobs",
    "source": "willhaben_jobs_detail",
    "id": 13158523,
    "title": "Zimmerer Hilfskraft (m/w/d)",
    "slugTitle": "zimmerer-hilfskraft-m-w-d",
    "description": "Zimmerer Hilfskraft (m/w/d)\nBruttogehalt: € 2.720 monatlich",
    "employmentTime": "ab sofort",
    "position": "Mitarbeiter:in",
    "firstPublishDate": "2026-04-07T01:50:00",
    "lastModifiedDate": "2026-04-07T01:50:00",
    "expiryDate": null,
    "lastReorderDate": "2026-04-07T01:50:00",
    "overpay": false,
    "forceExternalApplicationForm": true,
    "salary": 2720,
    "salaryTimeFrame": "monatlich",
    "isExpired": false,
    "employmentModes": ["Teilzeit", "Vollzeit"],
    "jobLocations": [
        {
            "name": "Sankt Johann im Pongau",
            "federalState": "Sankt Johann im Pongau",
            "country": "Österreich"
        }
    ],
    "languageSkills": [],
    "company": {
        "id": 79439,
        "title": "Maschinenring Personal u Service eGen",
        "slugTitle": "maschinenring-personal-u-service-egen",
        "type": "Firma",
        "uidNumber": null,
        "url": "https://www.willhaben.at/jobs/firma/personaldienstleister/79439",
        "logoUrl": "https://www.willhaben.at/jobs/api/v1/images/public/6355402?resolution=480",
        "industry": "Personaldienstleistungen",
        "address": null,
        "foundingYear": null,
        "employeeCountFrom": null,
        "employeeCountTo": null,
        "activeAdverts": null
    },
    "contact": {},
    "applyUrl": null,
    "apify_scrapedAt": "2026-04-07T06:42:52.366Z",
    "apify_extracted_emails": []
}
```

***

#### Output fields (Jobs) — field-by-field

##### Row labels (flattened Jobs runs only)

- **`willhabenKind`** — Always **`jobs`** when this wrapper is present (**`flattenOutput: true`**).
- **`source`** — **`willhaben_jobs_detail`**: a Jobs row (iad listings use **`willhaben_detail`**).

##### Job identity and copy

- **`id`** — Numeric **Willhaben job advert id** (the number in the job's link).
- **`title`** — Job title / headline.
- **`slugTitle`** — URL-style slug derived from **`title`** (lowercase, hyphenated, diacritics normalised).
- **`description`** — Short teaser plus optional lines such as **Bruttogehalt** from the job details. **Not** the full long description, which Willhaben shows only to signed-in users.

##### Employment and dates

- **`employmentTime`** — Start / availability label (e.g. **ab sofort**) from the job's details strip when present.
- **`position`** — Position level label (e.g. **Mitarbeiter:in**, **Lehre**) from the same details strip.
- **`firstPublishDate`** — First publish timestamp string as shown in the search results, when available.
- **`lastModifiedDate`** — Last modified string from the job's “Zuletzt geändert” line and publish time when available.
- **`expiryDate`** — Advert end date when shown; often **`null`**.
- **`lastReorderDate`** — Reorder / bump date aligned with publish when available; else mirrors **`firstPublishDate`**.

##### Salary and application flags

- **`overpay`** — **`true`** when the salary text indicates **Überzahlung** / willingness to pay above scale.
- **`forceExternalApplicationForm`** — **`true`** when the job is applied for outside Willhaben.
- **`salary`** — Parsed gross amount as a number (e.g. **2720**), when a **€** amount is found in the **Bruttogehalt** / salary text.
- **`salaryTimeFrame`** — **`monatlich`**, **`stündlich`**, etc., inferred from that text when possible.
- **`isExpired`** — **`false`** by default for scraped active rows; **`true`** only when the listing says so.

##### Modes, locations, languages

- **`employmentModes`** — Array of employment types (e.g. **Teilzeit**, **Vollzeit**, **Freiberuflich**) from the job's details strip or its search result.
- **`jobLocations`** — Array of place objects for the job’s advertised locations:
  - **`name`** — Place label (city, region, or the free-text place shown on the job).
  - **`federalState`** — Bundesland or same as **`name`** when only a coarse label exists (heuristic).
  - **`country`** — **`Österreich`**, **`Deutschland`**, etc., inferred from known regions (e.g. **Bayern** → Germany).
- **`languageSkills`** — Array of language requirements when listed; often **`[]`**.

##### Company (nested object)

- **`company.id`** — Willhaben **company** id (from the company links on the job, when present).
- **`company.title`** — Legal or display company name.
- **`company.slugTitle`** — Slug for the company profile URL.
- **`company.type`** — e.g. **Firma** or **Personaldienstleister** when set; default **Firma** in the mapper when unknown.
- **`company.uidNumber`** — Austrian **UID** when present; else **`null`**.
- **`company.url`** — Link to the **www** company profile when the job links to one.
- **`company.logoUrl`** — Company logo URL on Willhaben’s **www** jobs CDN when present (often with a fixed resolution query).
- **`company.industry`** — Industry / sector label from the company information section when present.
- **`company.address`** — Structured address (**street**, **zipCode**, **city**, **country**) when shown; else **`null`**.
- **`company.foundingYear`**, **`employeeCountFrom`**, **`employeeCountTo`**, **`activeAdverts`** — Firmographics when present; often **`null`**.

##### Contact and apply

- **`contact`** — Object for contact person fields. Often **`{}`** when the job lists no contact name or email; if an email is found in **`description`**, **`contact.email`** may be set to the first match.
- **`applyUrl`** — External application URL when the job links one; often **`null`** (many jobs only link to login-gated content).

##### Apify-only

- **`apify_scrapedAt`** — ISO timestamp when this dataset row was written.
- **`apify_extracted_emails`** — Deduplicated email addresses regex-matched from **`title`**, **`description`**, and **`contact`** fields (order preserved).

##### Optional fields (not in this sample)

Some jobs may include extra keys when the job shows them, e.g. **`professionalExperience`** (boolean). Treat the schema as **stable core + optional extensions**.

***

### Benefits of the Willhaben scraper

- **Four verticals** in one actor: **Marktplatz**, **Immobilien**, **Auto & Motor**, and **Jobs**, matching Willhaben’s main navigation.
- **Full details**: every field of the listing, not only the search snippet.
- **Flatten mode** for **CSV**-friendly exports: mapped columns **plus** raw Willhaben attribute keys on one row (**iad** verticals).
- **Traceability**: **`detailUrl`**, **`originalInputUrl`**, **`listPage`** on **iad** rows; **Jobs** rows focus on job + company fields (trace fields are not duplicated on the job object).

***

### Why Choose This Actor?

Built for **Austrian marketplace and classifieds research** on **willhaben.at**: paste a **www** search link and get every listing with its full details. Outputs suit **warehouses**, **BI**, or **price monitoring**.

**Use cases:**

- Track **rentals and sales** in **Immobilien** with filters from the URL.
- Export **Marktplatz** or **Auto** listings with attributes and images.
- Collect **Jobs** in a dedicated job shape plus extracted emails.

***

### Explore More Scrapers

If you found this actor useful, check out other scrapers at [memo23's Apify profile](https://apify.com/memo23).

***

### Support

- For issues or feature requests, use the **Issues** section of this actor on Apify.
- For further assistance, contact the author:
  - Author's website: <https://muhamed-didovic.github.io/>
  - Email: <muhamed.didovic@gmail.com>

***

### Additional Services

- Request customization or a full dataset: <muhamed.didovic@gmail.com>
- Need other platforms scraped? Contact <muhamed.didovic@gmail.com>
- For API services of this actor, reach out to <muhamed.didovic@gmail.com>
- Custom integrations and automation solutions available

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/willhaben-scraper`).

**Purpose:** Scrapes willhaben.at listings across Marktplatz (marketplace), Immobilien (real estate), Auto & Motor (vehicles), and Jobs into structured, flat JSON — one row per listing, with full details.

**Minimal input:**

```json
{
    "startUrls": [
        {
            "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?areaId=5&page=1&PRICE_FROM=150&PRICE_TO=1300"
        }
    ],
    "maxItems": 30
}
```

**Output:** one dataset row per listing. iad rows (real_estate / marketplace / car) key fields: `source`, `willhabenKind`, `listingId`, `url`, `sourceListUrl`, `platform`, `scrapedAt`, `transactionType`, `title`, `description`, `price`, `priceText`, `currency`, `rentGross`, `livingAreaM2`, `roomCount`, `street`, `postalCode`, `city`, `district`, `region`, `country`, `latitude`, `longitude`, `sellerName`, `imageUrls`, `apify_scrapedAt` — plus every flattened Willhaben attribute key (`ADID`, `HEADING`, `PRICE`, `LOCATION`, `COORDINATES`, `SEO_URL`, `ALL_IMAGE_URLS`, …) when `flattenOutput: true`. Jobs rows key fields: `willhabenKind`, `source`, `id`, `title`, `slugTitle`, `description`, `employmentModes`, `jobLocations`, `salary`, `salaryTimeFrame`, `firstPublishDate`, `company` (nested), `contact`, `applyUrl`, `apify_scrapedAt`, `apify_extracted_emails`. (Field names sourced from `data.json` and `data-jobs.json`.)

**Behaviors an agent should know:**

- Always set `maxItems` — it caps listings **per `startUrls` entry** (each URL has its own budget), not a single global cap; two URLs at `maxItems: 30` can yield up to 60 rows.
- `startUrls` accepts any `www.willhaben.at` hub URL: `/jobs/…`, `/iad/immobilien/…`, `/iad/kaufen-und-verkaufen/…` (Marktplatz), or `/iad/…auto…` (vehicles); each returns the listings of that search.
- Jobs rows use a different top-level shape (`source: willhaben_jobs_detail`, `willhabenKind: jobs`); filter iad rows with `source === 'willhaben_detail'` and Jobs with `willhabenKind === 'jobs'`.
- Set `flattenOutput: true` for one flat row per listing (mapped fields + raw attribute keys); default is `false`, which nests iad data under `listingDocument` / `actorMeta`.
- The actor routes traffic through its own built-in residential proxy at no extra cost; only set `proxy` to override with your own.
- 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 willhaben Internet Service GmbH & Co KG or any of its subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available willhaben.at listing pages — no authenticated endpoints, paid features, or content behind the willhaben.at login wall, and it does not bypass authentication. Users are responsible for ensuring their use complies with willhaben.at's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization.

***

### SEO Keywords

willhaben scraper, scrape willhaben, willhaben.at scraper, willhaben API, Apify willhaben, willhaben Marktplatz scraper, willhaben Immobilien scraper, willhaben real estate scraper, willhaben Auto scraper, willhaben Jobs scraper, Austria marketplace scraper, Austrian real estate data, Austria rental listings scraper, Austria classifieds scraper, Austria car listings scraper, Austria job listings scraper, property price monitoring Austria, real estate data extraction, vehicle listings data, job market data Austria, classifieds data export

# Actor input Schema

## `startUrls` (type: `array`):

Here are some example URLs that you can use to start the scraper:

## `flattenOutput` (type: `boolean`):

If true, each listing is one flat object: mapped fields (title, price, imageUrls, …) plus every `listingDocument.attributes` value at the top level (`listingDocument.attributesFlat`). Nested `listingDocument` and `actorMeta` are omitted. If a flat attribute name collides with a mapped field, it is stored as `attr_<name>` instead.

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

Maximum number of **detail** pages queued **per start URL** (each `startUrls[].url` has its own limit). Not a single global cap across all start URLs.

## `maxConcurrency` (type: `integer`):

Maximum number of pages that can be processed at the same time.

## `minConcurrency` (type: `integer`):

Reserved for compatibility; this actor uses **maxConcurrency** only.

## `maxRequestRetries` (type: `integer`):

How many times a failed search or detail page is retried before it is skipped.

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

Leave empty — the actor already routes all traffic through its own built-in residential proxy at no extra cost to you. Only set this if you want to use your own proxies.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.willhaben.at/jobs/suche?employment_type=109&position=13542&operation_area=13375&location=Salzburg&region=14096"
    },
    {
      "url": "https://www.willhaben.at/iad/gebrauchtwagen/auto/volvo-gebrauchtwagen/xc60"
    },
    {
      "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?sfId=0db5c6aa-6f06-4760-a077-5d2d88453916&rows=30&areaId=5&page=1&PRICE_FROM=150&PRICE_TO=1300"
    },
    {
      "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz/kinderfeste-kinderfeiern-4282/a/zustand-neu-22?sfId=60dce79c-cd0d-421b-ab8a-d278d9dea396&rows=30&isNavigation=true&PRICE_FROM=0&PRICE_TO=1"
    },
    {
      "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?NO_OF_ROOMS_BUCKET=10X&page=1&ESTATE_SIZE/LIVING_AREA_FROM=20"
    }
  ],
  "flattenOutput": false,
  "maxItems": 30,
  "maxConcurrency": 50,
  "minConcurrency": 1,
  "maxRequestRetries": 100
}
```

# 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 = {
    "startUrls": [
        {
            "url": "https://www.willhaben.at/jobs/suche?employment_type=109&position=13542&operation_area=13375&location=Salzburg&region=14096"
        },
        {
            "url": "https://www.willhaben.at/iad/gebrauchtwagen/auto/volvo-gebrauchtwagen/xc60"
        },
        {
            "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?sfId=0db5c6aa-6f06-4760-a077-5d2d88453916&rows=30&areaId=5&page=1&PRICE_FROM=150&PRICE_TO=1300"
        },
        {
            "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz/kinderfeste-kinderfeiern-4282/a/zustand-neu-22?sfId=60dce79c-cd0d-421b-ab8a-d278d9dea396&rows=30&isNavigation=true&PRICE_FROM=0&PRICE_TO=1"
        },
        {
            "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?NO_OF_ROOMS_BUCKET=10X&page=1&ESTATE_SIZE/LIVING_AREA_FROM=20"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/willhaben-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 = { "startUrls": [
        { "url": "https://www.willhaben.at/jobs/suche?employment_type=109&position=13542&operation_area=13375&location=Salzburg&region=14096" },
        { "url": "https://www.willhaben.at/iad/gebrauchtwagen/auto/volvo-gebrauchtwagen/xc60" },
        { "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?sfId=0db5c6aa-6f06-4760-a077-5d2d88453916&rows=30&areaId=5&page=1&PRICE_FROM=150&PRICE_TO=1300" },
        { "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz/kinderfeste-kinderfeiern-4282/a/zustand-neu-22?sfId=60dce79c-cd0d-421b-ab8a-d278d9dea396&rows=30&isNavigation=true&PRICE_FROM=0&PRICE_TO=1" },
        { "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?NO_OF_ROOMS_BUCKET=10X&page=1&ESTATE_SIZE/LIVING_AREA_FROM=20" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/willhaben-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 '{
  "startUrls": [
    {
      "url": "https://www.willhaben.at/jobs/suche?employment_type=109&position=13542&operation_area=13375&location=Salzburg&region=14096"
    },
    {
      "url": "https://www.willhaben.at/iad/gebrauchtwagen/auto/volvo-gebrauchtwagen/xc60"
    },
    {
      "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?sfId=0db5c6aa-6f06-4760-a077-5d2d88453916&rows=30&areaId=5&page=1&PRICE_FROM=150&PRICE_TO=1300"
    },
    {
      "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz/kinderfeste-kinderfeiern-4282/a/zustand-neu-22?sfId=60dce79c-cd0d-421b-ab8a-d278d9dea396&rows=30&isNavigation=true&PRICE_FROM=0&PRICE_TO=1"
    },
    {
      "url": "https://www.willhaben.at/iad/immobilien/mietwohnungen/mietwohnung-angebote?NO_OF_ROOMS_BUCKET=10X&page=1&ESTATE_SIZE/LIVING_AREA_FROM=20"
    }
  ]
}' |
apify call memo23/willhaben-scraper --silent --output-dataset

```

## MCP server setup

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