Willhaben.at Scraper at a glance
Willhaben.at Scraper is a ready-to-run Apify actor from abotapi for collecting ecommerce data. Its documented output includes category, id, url, title and price, which you can export as JSON, CSV or Excel or read through the Apify API. It costs $1.80 per 1,000 results on the Apify Free plan and from $1.00 on paid plans, with smaller per-run or optional feature charges listed on Apify.
- Runs on
- Apify cloud: console, API, schedules and integrations
- Data type
- Ecommerce, Real Estate, Automation
- Pricing
- $1.80 per 1,000 results (Free plan), from $1.00 on paid plans
- Output
- Documented below · JSON, CSV, Excel
- Inputs
- 25 options
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.
What Willhaben.at Scraper can do
- 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.
Choose what to collect
These inputs let you configure Willhaben.at Scraper - Listings, Prices, Sellers & 40+ Fields. Open the actor on Apify to enter your values and review the current options.
| Input | What it controls |
|---|---|
modestring · required | How to find listings: build a search from filters, or paste search-page URLs. |
categorystring | Listing category (Search mode). Pick a section of willhaben.at to scrape. |
categoryPathstring | 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. |
keywordstring | Free-text keyword to search within the category, e.g. 'iphone', 'bmw', 'altbau'. Leave empty to list everything in the category. |
areaIdinteger | 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. |
regionstring | 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. |
minPriceinteger | Minimum price filter. Leave empty for no minimum. Not applied to the Jobs category. |
Show 18 more inputs
| Input | What it controls |
|---|---|
maxPriceinteger | Maximum price filter. Leave empty for no maximum. |
sortstring | Result ordering (Search mode). |
customParametersarray | 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. |
urlsarray | 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. |
startUrlstring | Shortcut for one willhaben.at link: scrapes only that link in URL mode and replaces Search URLs. Leave empty to use the other fields. |
fetchDetailsboolean | 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). |
maxListingsinteger | Maximum number of listings to scrape across all searches. Set 0 for unlimited. |
maxItemsinteger | Alias of Max listings. Used when Max listings is left at its default of 20. |
maxPagesinteger | Optional bound on result pages walked per search/URL (~25 listings/page). Leave empty to walk every result page. |
resumeFromRunIdstring | 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. |
incrementalModeboolean | 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. |
stateKeystring | 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. |
emitUnchangedboolean | 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. |
emitExpiredboolean | 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. |
ignoreFieldsForChangesarray | 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. |
mcpConnectorsarray | 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). |
notionParentPageUrlstring | URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors. |
maxNotifyListingsinteger | Cap on items written to each connector per run. Does not affect the dataset. |
Fields in the output
The documented dataset includes the fields below. Availability can depend on the source page and selected mode.
categoryCategoryidIDurlURL (link)titleTitlepricePrice (number)priceDisplayPrice (display)changeTypeChange type (incremental mode) (text)salarySalarysalaryTimeFrameSalary time framecompanyNameCompanypositionPositionemploymentTimeEmployment timeShow 20 more fields
employmentModesEmployment modeslocationNameLocationpostcodePostcodestateStatelatitudeLat (number)longitudeLng (number)advertiserLabelSellerisPrivatePrivate (boolean)publishedDatePublishedimageCount# Images (number)mainImageMain image (image)changedFieldsChanged fields (incremental mode) (array)firstSeenAtFirst seen (incremental mode) (date)lastSeenAtLast seen (incremental mode) (date)descriptionDescriptioncontactNameContact nameorganisationNameOrganisationadvertiserInfoAdvertiser info (object)imagesImages (array)seoUrlSEO URLExample output
An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.
{
"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.2,
"longitude": 16.37,
"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"
]
}Ways to use this data
- 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.
Before you run
Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.
Questions about this actor
What data does Willhaben.at Scraper return?
The documented output for Willhaben.at Scraper includes category, id, url, title, price, price (display), change type (incremental mode) and salary. Availability of each field depends on the source page and the selected mode.
How much does Willhaben.at Scraper cost?
It costs $1.80 per 1,000 results on the Apify Free plan and from $1.00 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.
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".
