# Willhaben Scraper — Austria Marketplace Ads & Prices (`yugenox/willhaben-scraper`) Actor

Scrape willhaben.at, Austria's largest marketplace: ads by category, keyword and state or district. Title, price, price drops, condition, shipping or pickup, category, attributes, postcode, town, photos and dates; optional full description. Private sellers' data is never collected. No login.

- **URL**: https://apify.com/yugenox/willhaben-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 ads

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

## Willhaben Scraper: Austria marketplace ads and prices

Scrape **willhaben.at**, Austria's largest marketplace (over 13 million ads), by category, keyword and state or district. Every ad comes with its **title, price, earlier price after a price drop, condition (new, like new, used …), shipping or pickup, PayLivery, category, item attributes (brand, size, colour …), postcode, town, district, state, photos and dates**. Turn on **ad details** to add the **full description, every item attribute, the full category path and the first-published date**. No login, no cookies, no willhaben account.

This Actor is not affiliated with, endorsed by or sponsored by willhaben or willhaben internet service GmbH & Co KG.

### What you get

| | |
|---|---|
| **Ad** | willhaben ad number, URL, title, description (the ~250-character preview, or the full text with details), published, changed and expiry dates, "pushed to the top" flag |
| **Price** | Asking price in EUR, the price text as shown ("€ 800", "zu verschenken"), free-item flag, the earlier price and the date it was lowered |
| **Item** | Condition (New, Like new, Refurbished, Used, Defective, Display item) in English and German, shipping offered, pickup possible, PayLivery |
| **Category** | Category id and name, the full path (e.g. Sport / Sportgeräte › Fahrräder / Radsport › Fahrräder › E-Bikes), item attributes such as Farbe, Rahmengröße, Laufradgröße, Marke |
| **Location** | Postcode, town (or Vienna district), political district, state, country |
| **Seller type** | Private or dealer. **No seller's name, profile, phone, e-mail, street address or coordinates is ever collected.** |
| **Photos** | Main photo and every photo in full size |
| **Ad details** (optional) | Full description, every item attribute (e.g. Fahrradschaltung, Art der Bremse), category names for every ad, first-published date |

### How to use it

1. Pick one or more **Categories**: a German name as on willhaben (`Fahrräder`, `E-Bikes`, `Smartphones / Telefonie`, `Mode / Accessoires`), a category id (`4552`) or a category link. Every sub-category works.
2. Optionally add **Keywords** and **Locations** (`Wien`, `Tirol`, `Upper Austria`, or a district such as `Oberösterreich, Linz Land`).
3. Set **Max ads** and any filters, then run.

Each category is searched separately in each location. **Max ads** is the total for the run, shared fairly between your searches. Results come newest first, like on willhaben, unless you pick a price sort.

**Keywords** work two ways. With categories, the scraper reads every page of those categories and keeps the ads whose title or description contains all words of a keyword, so there is no limit on results. Without categories, it reads willhaben's own page for that keyword (its first 30 best matches), when willhaben has one.

#### Input examples

Used e-bikes in Vienna under €1,500 that can be shipped:

```json
{
  "categories": ["E-Bikes"],
  "locations": ["Wien"],
  "conditions": ["used", "like-new"],
  "priceMax": 1500,
  "delivery": "shipping",
  "maxItems": 200
}
```

Every "Cube Stereo" mountain bike in Austria, with full descriptions and attributes:

```json
{
  "categories": ["Mountainbikes"],
  "keywords": ["Cube Stereo"],
  "includeDetails": true,
  "maxItems": 0
}
```

New smartphone ads from private sellers in Upper Austria from the last day (schedule it daily):

```json
{
  "categories": ["Smartphones / Telefonie"],
  "locations": ["Oberösterreich"],
  "sellerType": "private",
  "publishedWithinDays": 1,
  "maxItems": 0
}
```

Free furniture in Linz:

```json
{
  "categories": ["Wohnen / Haushalt / Gastronomie"],
  "locations": ["Oberösterreich, Linz"],
  "priceMax": 0,
  "maxItems": 100
}
```

willhaben's own keyword page, cheapest first:

```json
{
  "keywords": ["Moped 50"],
  "sortBy": "price-asc"
}
```

You can also paste willhaben links into **willhaben links**: category or search pages such as `https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz/fahrraeder-radsport/fahrraeder-4552?areaId=900` (price, condition, private/dealer and keyword filters in the link are applied too), keyword pages such as `https://www.willhaben.at/iad/kaufen-und-verkaufen/l/moped-50`, or single ads.

### Output example

```json
{
  "adId": "1274023283",
  "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/d/fahrrad-glanzrad-29-italien-klassik-1274023283/",
  "title": "Fahrrad Glanzrad 29'' Italien Klassik",
  "description": "Italienisches Fahrrad 28\nStahlrahmen, Gabel und Sattel sind handgemacht in Italien\n\nAssembling in Österreich (Wien ) Nach nur 4 Stück",
  "price": 200,
  "priceText": "€ 200",
  "currency": "EUR",
  "isFree": false,
  "previousPrice": null,
  "condition": "New",
  "conditionLabel": "Neu",
  "shipping": true,
  "pickup": true,
  "paylivery": false,
  "categoryId": 4554,
  "category": "Citybikes / Stadträder",
  "categoryIds": [4390, 4525, 4552, 4554],
  "categoryPath": ["Sport / Sportgeräte", "Fahrräder / Radsport", "Fahrräder", "Citybikes / Stadträder"],
  "attributes": {
    "Farbe": "Mehrfarbig",
    "Rahmengröße": "48 cm",
    "Laufradgröße": "28 Zoll",
    "Fahrradschaltung": "Kettenschaltung",
    "Art der Bremse": "Felgenbremse"
  },
  "postcode": "1110",
  "town": "Wien, 11. Bezirk, Simmering",
  "district": "Wien",
  "state": "Wien",
  "country": "Österreich",
  "sellerType": "dealer",
  "imageUrl": "https://cache.willhaben.at/mmo/3/127/402/3283_-764085504.jpg",
  "imageCount": 1,
  "publishedAt": "2026-10-01T22:51:34.721Z",
  "firstPublishedAt": "2026-04-12T07:16:00.000Z",
  "expiresAt": null,
  "isBumped": true,
  "searchCategory": 4552,
  "searchLocation": "Wien",
  "position": 2,
  "hasDetails": true,
  "scrapedAt": "2026-10-01T23:20:00.000Z"
}
```

Every row has the same columns; a value the ad doesn't state is `null`. Dates are in UTC.

### Use cases

- **Price research**: what used bikes, phones, furniture or tools really sell for, by condition and region.
- **Deal hunting and reselling**: daily runs with **Published within: 1** and a max price, or price drops (`previousPrice`).
- **Market and brand monitoring**: how many ads a brand or model has, where, and at what prices.
- **Second-hand and circular-economy research**: supply by category, state and condition across Austria.
- **Data for apps and dashboards**: clean, consistent rows with photos and categories.

### FAQ

**Do I need a willhaben account, cookies or a login?** No. The scraper reads the same public pages any visitor sees. It never logs in and needs nothing from you except a category, keyword, location or link.

**Which locations can I search?** The nine states, in German or English (`Wien` / `Vienna`, `Niederösterreich` / `Lower Austria`, `Oberösterreich` / `Upper Austria`, `Steiermark` / `Styria`, `Kärnten` / `Carinthia`, `Tirol` / `Tyrol`, `Salzburg`, `Vorarlberg`, `Burgenland`), or a state and district: `Oberösterreich, Linz Land`, `Oberösterreich, Linz`, `Wien, Simmering` or `Wien, 11. Bezirk`. A district willhaben doesn't know is skipped, not charged, and named in the run's status message.

**Why does a keyword alone return at most 30 ads?** willhaben's robots.txt asks crawlers not to use its keyword search links or to page through its keyword pages, and this scraper respects that. A keyword without a category reads willhaben's own page for that keyword (its first 30 best matches). Add a category and the scraper reads every page of that category and keeps the matching ads, with no limit.

**How do the filters work?** Location, sort order and PayLivery use willhaben's own search. Price, condition, seller type, handover, keywords inside categories and "published within" are checked on every ad as it is read, so a narrow filter reads more pages. With filters on, each search stops after 50 result pages (5,000 ads checked) unless you raise **Max result pages per search**.

**How fast is it?** A result page holds up to 100 ads and takes about a second, so 1,000 ads take well under a minute. **Include ad details** opens each ad's own page, roughly two ads per second.

**Is the description complete?** Search results carry willhaben's preview of the text (about 250 characters). Turn on **Include ad details** for the full description and every item attribute.

**Which proxy should I use?** Keep the default (Apify Proxy, datacenter). It comes with every Apify plan, including the free one. If willhaben refuses datacenter IPs for a while, the run switches to residential proxies when your account has them, and carries on without them when it doesn't.

**What does a run cost?** You pay a small fee per run, per result page read, per ad saved, and per ad whose details were loaded. See the Pricing tab. Ads removed by your filters are never charged as ads.

**Is it legal to scrape willhaben?** This Actor only collects publicly available data: marketplace ads that anyone can see on willhaben without an account, such as the title, price, condition, category, photos and the town the item is in. Collecting publicly available data is generally legal, but you're responsible for how you use it. Many ads are placed by private individuals, so the scraper never collects a seller's name, profile, phone number, e-mail, street address or map coordinates, and it removes phone numbers and personal e-mail addresses from ad texts. You must follow privacy laws such as GDPR, as well as willhaben's terms. If you're unsure, check with a lawyer. More on this: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it access any private data?** No. Everything comes from pages willhaben shows to any visitor without logging in. It never uses a login, never touches user accounts, messages or saved searches, never contacts sellers, and follows the site's robots.txt.

**Does it scrape willhaben real estate, cars or jobs?** Not yet: this Actor covers the marketplace (Marktplatz / kaufen und verkaufen), which is where most of willhaben's ads are.

# Actor input Schema

## `categories` (type: `array`):

willhaben marketplace categories, by German name as on willhaben ("Fahrräder", "E-Bikes", "Smartphones / Telefonie", "Mode / Accessoires"), by id (4552) or as a category link. Top-level categories: Antiquitäten / Kunst, Baby / Kind, Beauty / Gesundheit / Wellness, Boote / Yachten / Jetskis, Bücher / Filme / Musik, Computer / Software, Dienstleistungen, Freizeit / Instrumente / Kulinarik, Games / Konsolen, Haus / Garten / Werkstatt, Kameras / TV / Multimedia, KFZ-Zubehör / Motorradteile, Mode / Accessoires, Smartphones / Telefonie, Spielen / Spielzeug, Sport / Sportgeräte, Tiere / Tierbedarf, Uhren / Schmuck, Wohnen / Haushalt / Gastronomie. Every sub-category works too. Each category is searched separately, every result page.

## `keywords` (type: `array`):

Words to look for, e.g. "Cube Stereo" or "iPhone 15". With categories: every ad in those categories whose title or description contains all words of a keyword (no limit). Without categories: willhaben's own page for that keyword (its first 30 best matches, when willhaben has a page for the keyword; others are skipped, not charged).

## `locations` (type: `array`):

Austrian states ("Wien", "Tirol", "Steiermark", or in English: "Vienna", "Upper Austria" …) or a state and district separated by a comma ("Oberösterreich, Linz Land", "Wien, Simmering", "Oberösterreich, Linz"). Each location is searched separately. Empty = all of Austria. A district willhaben doesn't know is skipped, not charged.

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

Maximum number of ads for the whole run, shared fairly between your searches. 0 = everything.

## `includeDetails` (type: `boolean`):

Also open every ad's own page for the full description (search results only carry the first ~250 characters), every item attribute (brand, size, model …), the category names and the first-published date. Slower (one extra page per ad) and charged per ad.

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

Optional: willhaben marketplace links. Category or search pages (filters in the link such as price, condition (Zustand), private/dealer and keyword are applied while reading), keyword pages (willhaben.at/iad/kaufen-und-verkaufen/l/…) or single ads (willhaben.at/iad/kaufen-und-verkaufen/d/…). Real estate, cars and jobs links are not supported.

## `sortBy` (type: `string`):

Order of the results. Newest first is willhaben's default for categories; keyword pages default to relevance.

## `priceMin` (type: `integer`):

Only ads at or above this price.

## `priceMax` (type: `integer`):

Only ads at or below this price. 0 finds free items (zu verschenken).

## `conditions` (type: `array`):

Only items in these conditions. Empty = any (also ads without a condition).

## `sellerType` (type: `string`):

Private sellers, dealers or both. No seller's name, profile, phone, e-mail or address is ever collected.

## `delivery` (type: `string`):

Only ads that offer shipping, or only ads that can be picked up.

## `payliveryOnly` (type: `boolean`):

Only ads you can buy online through willhaben PayLivery (buyer protection and delivery).

## `publishedWithinDays` (type: `integer`):

Only ads published (or pushed to the top) in the last N days. With newest-first order the run stops as soon as it reaches older ads — ideal for daily monitoring. Empty = any time.

## `maxPagesPerSearch` (type: `integer`):

Each result page holds up to 100 ads. With filters or keywords inside categories, a search stops after this many pages (default 50 = 5,000 ads checked); without filters it reads as many as Max ads needs. 0 = default.

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

Apify Proxy (datacenter) is the default and comes with every Apify plan. If willhaben refuses datacenter IPs for a while, the run switches to residential proxies when your account has them.

## Actor input object example

```json
{
  "categories": [
    "Fahrräder"
  ],
  "locations": [
    "Wien"
  ],
  "maxItems": 30,
  "includeDetails": false,
  "sellerType": "all",
  "delivery": "any",
  "payliveryOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped ads.

## `run` (type: `string`):

Status and statistics for this run.

## `report` (type: `string`):

RUN_REPORT: how the run ended, counts, requests and proxy use.

# 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 = {
    "categories": [
        "Fahrräder"
    ],
    "locations": [
        "Wien"
    ],
    "maxItems": 30,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/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 = {
    "categories": ["Fahrräder"],
    "locations": ["Wien"],
    "maxItems": 30,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/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 '{
  "categories": [
    "Fahrräder"
  ],
  "locations": [
    "Wien"
  ],
  "maxItems": 30,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/willhaben-scraper --silent --output-dataset

```

## MCP server setup

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