# Willhaben Marketplace Listings Scraper (`automation-lab/willhaben-marketplace-search-cards`) Actor

Export public Willhaben marketplace search cards with listing IDs, prices, locations, category attributes, preview text, dates and image references for Austrian inventory and price research.

- **URL**: https://apify.com/automation-lab/willhaben-marketplace-search-cards.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.72 / 1,000 item extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Willhaben Marketplace Listings Scraper

Export **willhaben marketplace listings** from anonymous kaufen-und-verkaufen search cards into a structured dataset for Austrian price and inventory research. Supply search URLs copied from Willhaben, including keyword, category or location filters. The Actor does not open listing details or require an account.

### What does it extract?

Each result is one public marketplace search card, deduplicated by stable ad ID across the run. Data reflects the search response at retrieval time, not a historical archive.

| Fields | Meaning |
| --- | --- |
| `listingId`, `title`, `url` | Stable ad identity, card heading and canonical listing link |
| `price`, `priceDisplay`, `currency` | Numeric EUR amount when supplied, original displayed price and EUR |
| `location`, `postcode`, `district`, `state`, `country` | Public locality context, without street address or coordinates |
| `categoryIds`, `categoryAttributes` | Source category hierarchy and category-attribute ID encoding |
| `descriptionPreview` | Search-card preview, not a full detail description |
| `publishedAt`, `changedAt` | Source dates normalized to UTC, when available |
| `images` | Public image references exposed in the search response; no downloads |
| `sellerType` | Explicit private/business flag when present; no seller identity |
| `searchUrl`, `scrapedAt` | Provenance search URL and retrieval timestamp |

Dates use the source's numeric millisecond epochs, not its `*_String` labels, which can contain Austrian local time mislabeled as UTC. Missing or invalid epochs return `null`; string-only dates are not guessed, including ambiguous daylight-saving transitions.

Missing optional scalars are `null`; absent lists are empty arrays. A zero price remains zero and is not treated as missing. Displayed prices may contain negotiability or nonstandard wording: keep `priceDisplay` alongside the numeric amount.

### Who is it for?

- Resellers comparing asking prices for a product category in Austria.
- Analysts refreshing regional inventory snapshots.
- Researchers building repeatable price samples by keyword and location.

This is a search-card product, not a contact database, valuation engine or listing-detail enrichment service.

### Why use this Actor?

It preserves the listing ID for joins between snapshots, retains displayed prices instead of inventing numeric values, and attaches source/retrieval dates. It avoids downloading images and opening every detail page. Global deduplication reduces duplicate exports from overlapping search URLs.

### Getting started

1. Open Willhaben's marketplace and apply your desired filters.
2. Copy the HTTPS search URL, under `/iad/kaufen-und-verkaufen/marktplatz`.
3. Paste it into **Marketplace search URLs**.
4. Choose a global listing limit and a page cap per URL.
5. Run the Actor and download the default dataset as JSON, CSV, Excel or another Apify-supported export.

```json
{
  "startUrls": [{"url":"https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=lego"}],
  "maxItems": 30,
  "maxPages": 10
}
```

### Input parameters

| Parameter | Default | Semantics |
| --- | --- | --- |
| `startUrls` | Required | 1–20 marketplace search/category URLs, processed in order |
| `maxItems` | 20 | Global maximum unique accepted listings; integer 1–10000 |
| `maxPages` | 10 | Pages per starting URL; integer 1–100 |

Only URL-based GET search requests are supported. Custom request methods, payloads, headers, userData and remote request lists are rejected; optional empty fields are harmless. Native URL filters are forwarded to Willhaben unchanged. Keyword matching follows Willhaben's search behavior; there is no extra exact-phrase, substring or whole-word filter in this Actor. The Actor does not translate queries. Use the filters available on the source website rather than assuming arbitrary query parameter names work.

A URL's `page` is the starting page, defaulting to 1. The Actor always requests 30 cards per page, overriding a supplied `rows`. Global limits apply after deduplication, not separately to every source. Zero and unlimited are unsupported. Source exhaustion, page caps, run timeout or platform billing limits may stop collection before `maxItems`.

### Output example

A representative search card, with public sample content simplified:

```json
{
  "listingId": "937007997",
  "title": "Lego Duplo (10848) 80 Stk.",
  "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/d/lego-duplo-10848-80-stk-937007997/",
  "price": 15,
  "priceDisplay": "€ 15",
  "currency": "EUR",
  "location": "Liezen",
  "postcode": "8940",
  "district": "Liezen",
  "state": "Steiermark",
  "country": "Österreich",
  "categoryIds": ["5136", "5182", "5183", "5185"],
  "categoryAttributes": ["[2535;2537, 2535;2536, 21;2546, 5008971;5008972]"],
  "descriptionPreview": "Baustein-Set. 80 Teile.",
  "publishedAt": "2026-10-19T19:59:21.000Z",
  "changedAt": "2026-10-19T19:59:21.000Z",
  "images": [],
  "sellerType": "private",
  "searchUrl": "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=lego&rows=30&page=1",
  "scrapedAt": "2026-10-19T20:00:00.000Z"
}
```

The key-value store's `SUMMARY` record contains saved listings, fetched pages, request attempts, retries and response bytes. These counters are included without extra billing.

### How much does it cost to export Willhaben marketplace listings?

Pricing uses a one-time **$0.003 start event** per valid run and one **item event** per accepted unique listing saved to the dataset. The BRONZE item price is **$0.001192 per listing** ($1.192 per 1,000 listings). FREE is $0.0013708, SILVER $0.00092976, and GOLD/PLATINUM/DIAMOND $0.0007152 per listing. Spend tiers follow your qualifying aggregate monthly Apify Store spend, not a private per-Actor volume discount.

Worked example calculation (BRONZE): 10 listings cost $0.01492; 30 cost $0.03876; 100 cost $0.1222. These examples concern Actor event charges, not unrelated services or your subscription. Check the current Store pricing panel for your tier. A valid empty search has the start fee but no item events. Invalid input is rejected before the Actor's start charge. Duplicates and rejected records do not produce item events.

### Pagination and completeness

The source is a changing marketplace: listings may move, expire or be promoted between page requests. IDs are deduplicated, but a run is not a transactionally consistent snapshot and cannot guarantee every listing. Start URLs are processed sequentially, so earlier URLs may consume the global limit. Date fields describe the source card; they are not proof that a change occurred since your previous run.

Use scheduled runs plus your own ID/date comparison if you need recurring research. The Actor itself stores no cross-run history and sends no change alerts.

### Integrations

- Export to Google Sheets or a database through an Apify integration, joining by `listingId`.
- Schedule a category snapshot and compare price/date changes in your downstream pipeline.
- Send successful run notifications through Apify webhooks, then retrieve the default dataset.
- Build a regional inventory dashboard from `location`, `price` and `scrapedAt`.

Keep full exports in storage rather than passing thousands of descriptions directly into a language model.

### API usage

Use your own Apify token as an environment variable; never put credentials into search URLs.

#### cURL

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~willhaben-marketplace-search-cards/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" -H 'Content-Type: application/json' \
  -d '{"startUrls":[{"url":"https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=lego"}],"maxItems":30}'
```

The asynchronous response contains a run ID and storage IDs, not listing rows. Follow that run until terminal success, then read its `defaultDatasetId`.

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/willhaben-marketplace-search-cards').call({
  startUrls: [{ url: 'https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=lego' }],
  maxItems: 30,
});
if (run.status !== 'SUCCEEDED') throw new Error(`Run ${run.id}: ${run.status}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 30 });
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/willhaben-marketplace-search-cards').call(run_input={
    'startUrls': [{'url': 'https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=lego'}],
    'maxItems': 30,
})
if run['status'] != 'SUCCEEDED':
    raise RuntimeError(f"Run {run['id']}: {run['status']}")
print(client.dataset(run['defaultDatasetId']).list_items(limit=30).items)
```

### MCP setup

Use an Actor-specific endpoint and authorized authentication in your client:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/willhaben-marketplace-search-cards"
```

Claude Desktop, Cursor, and VS Code configuration (where the client supports remote HTTP MCP):

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/willhaben-marketplace-search-cards"}}}
```

Discover the actual argument schemas with scoped `tools/list`. Actor selection also adds run/data helpers; it is not exactly one tool. Example prompts: “List the supported input fields without executing the Actor.” For an authorized run: “Export 30 Lego marketplace cards, then summarize displayed prices by locality.”

Start once and retain the run ID and storage IDs. If still running, follow that same run with bounded backoff (for example 2, 4, 8 seconds, capped at 10) and a total 120-second deadline. A start call may wait only 0–45 seconds, depending on its arguments. Set the client request timeout above that wait plus transport margin. If the client times out, recover the known run; do not restart blindly. At the deadline report pending with the run ID, or report terminal failure honestly.

After success, call `get-dataset-items` with the run's `defaultDatasetId`, initial `offset=0`, `limit=20`, and useful `fields` such as `listingId,title,price,priceDisplay,location,publishedAt,changedAt,url,scrapedAt`, using the argument types from scoped discovery. Use untransformed JSON and advance source offsets by the fetched page size. Fetch at most 100 source rows or 64 KiB of serialized UTF-8 results admitted to model context across all pages, whichever comes first. Enforce that byte budget host-side; omit or summarize oversized content with disclosure. Track source offsets, report truncation and continuation offset, and keep full exports outside model context. If your client cannot intercept oversized responses, do not claim a hard byte guarantee. No numeric token-saving claim is made.

### Scope and limitations

- Only anonymous Austrian kaufen-und-verkaufen marketplace search cards.
- No vehicles, property, jobs, login, seller contact enrichment or detail-only fields.
- No raw seller IDs, profiles, street addresses or coordinates.
- Category attributes are source ID encodings, not translated labels or a complete detail specification.
- Preview text is not full description text. Obvious email and phone patterns are removed, but free-text redaction cannot guarantee anonymity.
- Image URLs may expire; the Actor does not download or archive media.
- No automatic proxy or browser fallback is enabled. A blocked source fails rather than pretending the search is empty.

### FAQ and troubleshooting

**Why are there no results?** A valid recognized search with zero source matches returns no rows. Check the keyword and filters in a browser. Unsupported or missing search state is an error, not a successful empty export.

**Why did the run fail?** The Actor rejects unsupported URLs, malformed limits, redirects, blocked pages and invalid search state. Transient network/429/5xx failures receive at most two retries per page; permanent HTTP errors do not. A failure after earlier pages may leave partial rows, which should not be treated as a complete successful export.

**Why fewer rows than requested?** Check the per-URL page cap, source exhaustion, overlapping IDs, runtime and platform charge limits. The listing limit is a ceiling, not a guaranteed count.

### Legality and diagnostic privacy

Use public data lawfully, respect Willhaben's terms and applicable privacy rights, and avoid contacting individuals or republishing personal information without a lawful basis. Public availability is not blanket permission for every downstream use. This Actor is independent and is not affiliated with Willhaben.

No AI model is used during extraction. Apify hosts runtime, storage and charging; source requests go directly to Willhaben, without an external scraping or proxy supplier. Runtime data remains in your run's Apify dataset/KV storage under your account's retention settings until deleted; no cross-run cache is maintained. Delete exports and runs in Apify when no longer needed. For product support, open the Actor's Store Issues tab with a run link and nonsensitive reproduction steps.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days. Do not put credentials or personal secrets into input.

### Related Actors

For a different regional marketplace workflow, see [Gumtree UK Classified Listings Scraper](https://apify.com/automation-lab/gumtree-uk-classified-listings-scraper). It does not extend this Actor's Willhaben coverage.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/willhaben-marketplace-search-cards/changelog.md

# Actor input Schema

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

Provide 1–20 HTTPS willhaben.at/iad/kaufen-und-verkaufen/marktplatz search or category URLs. Native keyword, category, location and other URL filters are passed to Willhaben unchanged; its own matching semantics apply, not an additional Actor text filter. Vehicle, property, job and listing-detail URLs are rejected. Each source starts at its URL page (default 1); rows is always 30. URLs run in order with global ID deduplication. Only URL-based GET requests are supported; custom method, payload, headers, userData and remote request lists are rejected (empty optional fields are harmless).

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

Global maximum unique marketplace cards across all URLs after deduplication. Default 20; 1–10000. Zero and unlimited are unsupported. Source exhaustion, maxPages, timeout or platform charge limits can end collection earlier; not a promise of complete source coverage.

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

Maximum sequential search pages to fetch per starting URL, beginning at its page parameter or 1. Each page requests 30 cards. Default 10; 1–100. Stops sooner at global maxItems or source exhaustion. Promoted or changing listings may overlap between pages and are deduplicated by ID.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.willhaben.at/iad/kaufen-und-verkaufen/marktplatz?keyword=lego"
    }
  ],
  "maxItems": 20,
  "maxPages": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Unique public marketplace search-card records.

## `summary` (type: `string`):

Aggregate request, page, retry, byte and saved-record counts.

# 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/iad/kaufen-und-verkaufen/marktplatz?keyword=lego"
        }
    ],
    "maxItems": 20,
    "maxPages": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/willhaben-marketplace-search-cards").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/iad/kaufen-und-verkaufen/marktplatz?keyword=lego" }],
    "maxItems": 20,
    "maxPages": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/willhaben-marketplace-search-cards").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/iad/kaufen-und-verkaufen/marktplatz?keyword=lego"
    }
  ],
  "maxItems": 20,
  "maxPages": 10
}' |
apify call automation-lab/willhaben-marketplace-search-cards --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/willhaben-marketplace-search-cards"
        }
    }
}
```

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/tHSpwYQvXfVfAGH7d/builds/XLpGPcOHrw6QD0Z71/openapi.json
