Willhaben.at Scraper - Listings, Prices, Sellers & 40+ Fields
Pricing
from $1.00 / 1,000 results
Willhaben.at Scraper - Listings, Prices, Sellers & 40+ Fields
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.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
6
Total users
0
Monthly active users
16 days ago
Last modified
Categories
Share
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
- Pick a mode:
searchbuilds a query from the filters below,urlscrapes any willhaben search-page link you paste. - 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.
- 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.
- Click Start. Download the dataset as JSON, CSV or Excel, or read it through the API.
Search used cars, list-only:
{"mode": "search","category": "gebrauchtwagen/auto/gebrauchtwagenboerse","maxListings": 20,"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AT" }}
Search the marketplace by keyword with a price range:
{"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:
{"mode": "search","category": "immobilien/mietwohnungen","areaId": 900,"fetchDetails": true,"maxListings": 50,"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "AT" }}
Paste willhaben search URLs (multi-URL, forward pagination):
{"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:
from apify_client import ApifyClientclient = 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:
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 sameidfield, 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 asNEW. Later runs returnNEWandUPDATED(withchangedFields) by default;UNCHANGEDlistings are suppressed, neither returned nor billed, unlessemitUnchangedis on.EXPIREDis only produced after a run that fully scans the tracked search (not capped bymaxListings, not a Resume run), and only whenemitExpiredis on.REAPPEAREDonly appears for a listing that an earlier complete-scan run withemitExpiredon had recorded as gone. State is kept separately per search/category/URL/filter combination unless you setstateKeyto name or deliberately share it. 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.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto 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. |
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 | none (prefill: one example search URL) | willhaben search-page URLs to scrape (URL mode). Multiple URLs are supported; filter fields above are ignored in this mode. |
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. |
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. |
proxy | object | none (prefill: Apify Proxy, Residential group, country AT) | Connection settings. willhaben.at rejects non-residential connections; 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.
{"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 accepts traffic only from Austrian, or nearby EU, residential connections; connections from typical datacenter ranges are rejected outright. 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 every attempt is still rejected, the run logs a clear warning explaining the fix 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 per-listing surcharge only when Fetch detail pages is on. 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:
| 🏷️ Mudah.my Cars, Property, Jobs & Marketplace Scraper Scrape Mudah.my listings across every category: cars, motorcycles, property, mobiles... | 🏷️ Subito.it Scrape Subito.it listings across cars, real estate, marketplace, jobs, and more. Search... |
| 🏷️ Gumtree UK Scrape gumtree.com classifieds across motors, property, jobs, services, pets and goods... | 🚗 Trade Me NZ Scraper Scrape trademe.co.nz across all five sections: Marketplace goods and auctions, Property... |
| 🚗 Marktplaats.nl Scraper From $1/1K. Scrape Marktplaats.nl listings by keyword or URL. Extract title, price... | 🚗 Autotrader US Scrape US car listings from Autotrader by make, model, ZIP, price, year, body style... |
💬 Support & custom scrapers
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.