# Marktplaats.nl Scraper (`haketa/marktplaats-scraper`) Actor

Marktplaats scraper & API (Netherlands classifieds): search listings by keyword & category and export title, price, description, location, seller, attributes, photos, date and URL. Dutch marketplace, resale and second-hand market data plus seller lead gen — fast, no login.

- **URL**: https://apify.com/haketa/marktplaats-scraper.md
- **Developed by:** [Haketa](https://apify.com/haketa) (community)
- **Categories:** Developer tools, Automation, Lead generation
- **Features:** Limited permissions
- **Stats:** 99 total users, 12 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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.
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

## Marktplaats.nl Scraper — Netherlands & Belgium Classifieds Data (Marktplaats, 2dehands.be & 2ememain.be)

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-0066CC?logo=apify\&logoColor=white)](https://apify.com/haketa/marktplaats-scraper)
[![Platforms](https://img.shields.io/badge/Platforms-Marktplaats.nl%20%F0%9F%87%B3%F0%9F%87%B1%20%7C%202dehands.be%20%F0%9F%87%A7%F0%9F%87%AA%20%7C%202ememain.be%20%F0%9F%87%A7%F0%9F%87%AA-orange)]()
[![Fields](https://img.shields.io/badge/Fields-25%2B%20Normalized%20Attributes-2EA44F)]()
[![Scale](https://img.shields.io/badge/Scale-1%2C000%2B%20Listings%20Pagination-blueviolet)]()
[![Auth](https://img.shields.io/badge/Auth-No%20Login%20Required-success)]()
[![Export](https://img.shields.io/badge/Export-JSON%20%7C%20CSV%20%7C%20Excel%20%7C%20API-0A66C2)]()

**Extract live classified listings from [Marktplaats.nl](https://www.marktplaats.nl) (Netherlands), [2dehands.be](https://www.2dehands.be) (Belgium — Flemish), and [2ememain.be](https://www.2ememain.be) (Belgium — French) in one unified schema.**

For every ad, this **Marktplaats scraper** captures the **listing title, numeric EUR price, normalized price type (`fixed`, `bidding`, `free`, `asking`, `negotiable`), normalized item condition (`new`, `like_new`, `used`), category and subcategory names + IDs, seller name, seller ID, seller type (`private` vs `business` / `zakelijk`), city, country, GPS coordinates (`latitude` / `longitude`), category-specific attributes, high-resolution images, shipping availability, and canonical URL**.

Export your dataset to **JSON, CSV, Excel (XLSX), XML, or the Apify API** with zero coding and no login.

> Built for Benelux e-commerce & recommerce pricing teams, used-car dealers and arbitrage analysts, real-estate and rental researchers, B2B dealer lead-generation agencies, brand-protection teams, and marketplace intelligence analysts.

***

### ⚡ At a glance

| Feature | Details |
|---|---|
| **Supported Platforms** | 🇳🇱 `marktplaats.nl` (Netherlands), 🇧🇪 `2dehands.be` (Belgium NL), 🇧🇪 `2ememain.be` (Belgium FR) |
| **Search Modes** | Keyword search (single or comma-separated multi-keyword), 20 L1 category dropdowns, L2 subcategory ID, or combined keyword + category |
| **Targeting & Filters** | Seller type (`all`, `private` / *particulier*, `business` / *zakelijk*), item condition (`new`, `like_new`, `used`), min/max price (EUR cents), postcode + radius (`distanceMeters`), sort order (`SORT_INDEX`, `PRICE`, `OPTIMIZED`) |
| **Core Extracted Fields** | `listingId`, `title`, `price`, `priceType`, `condition`, `category`, `subcategory`, `sellerName`, `sellerId`, `sellerType`, `location`, `latitude`, `longitude`, `attributes`, `images`, `description`, `url` |
| **Detail Enrichment** | Optional `scrapeDetails: true` visits each listing page for full-length descriptions and extended specifications |
| **Pagination & Scale** | Automatic offset pagination unlocking **1,000+ to 5,000+ listings** per search or multi-keyword run with `0` duplicates |
| **Empty-Input Behavior** | Running with `{}` automatically returns a balanced 100-item sample across 10 popular Benelux categories |
| **Authentication** | None required — keyless & browser-free |
| **Export Formats** | JSON, JSONL, CSV, Excel (XLSX), HTML, XML, RSS, and Apify REST API |

***

### 🔎 Why scrape Marktplaats, 2dehands.be & 2ememain.be?

With over **10 million active listings** and millions of daily transactions, **Marktplaats.nl** is the indisputable backbone of the Dutch second-hand and dealer marketplace economy — from used cars (*Auto's*), auto parts, bicycles (*Fietsen en Brommers*), and consumer electronics (*Telecommunicatie*, *Computers*) to furniture, fashion, real estate, and commercial services. Together with its Belgian sister platforms **2dehands.be** and **2ememain.be**, it covers the entire **Benelux** classifieds market.

Collecting and analyzing Benelux marketplace data manually is slow and error-prone:

- Prices are stored in euro cents and mixed with non-numeric types (*Bieden*, *Gratis*, *Zie omschrijving*, *Nader overeen te komen*).
- Condition and price-type labels appear in Dutch or French depending on the platform.
- Category names are separated from listing records in facet trees.
- Vertical-specific specs (car mileage, build year, fuel type, phone storage, battery health) are split across multiple attribute structures.

This **Marktplaats scraper** normalizes all three platforms into a single, analysis-ready schema — converting cents to EUR, mapping Dutch and French labels into standardized English codes while preserving original strings, resolving category and subcategory names automatically, and deduplicating every record by `listingId`.

#### Key business benefits

- **Live Benelux price benchmarking:** Track median and percentile asking prices by product model, storage capacity, build year, and item condition across the Netherlands and Belgium.
- **Used-car & vehicle arbitrage:** Monitor `Auto's` (`91`), `Auto-onderdelen` (`92`), and `Fietsen en Brommers` (`1655`) for undervalued private-seller inventory (`sellerType: "private"`) before competing dealers spot them.
- **Commercial seller (`zakelijk`) lead generation:** Filter `sellerTypeFilter: "business"` to build verified B2B prospect lists of Dutch and Belgian car dealerships, electronics refurbishers, furniture showrooms, and trade contractors.
- **Recommerce & circular-economy analytics:** Measure listing velocity, regional supply density (via `latitude` / `longitude`), and second-hand depreciation curves.
- **Brand protection & grey-market monitoring:** Detect counterfeit goods, unauthorized commercial resellers, and MAP violations across all three Benelux marketplaces.

***

### ✨ Key features

- ✅ **3-in-1 Benelux coverage:** Scrape **Marktplaats.nl**, **2dehands.be**, and **2ememain.be** with a single dropdown (`platform`) and identical output fields.
- ✅ **1,000+ listing deep pagination:** Automatically advances through search pages (30 listings per page) up to your `maxListings` target, and supports comma-separated multi-keyword queries to scale past single-query limits.
- ✅ **Bilingual label normalization:** Maps Dutch (*Vaste prijs*, *Bieden*, *Gratis*, *Zo goed als nieuw*, *Gebruikt*) and French (*Prix fixe*, *Faire offre*, *Gratuit*, *Comme neuf*, *Utilisé*) into standardized English enums (`fixed`, `bidding`, `free`, `like_new`, `used`) while keeping `priceTypeOriginal` and `conditionOriginal`.
- ✅ **Automatic category & subcategory name resolution:** Harvests L1 and L2 category labels from the marketplace facet tree so every listing includes both human-readable names (`category`, `subcategory`) and stable numeric IDs (`categoryId`, `subcategoryId`).
- ✅ **Seller segmentation (`private` vs `business`):** Identifies commercial EU SOI-498 business accounts (`zakelijk`) vs private individuals (`particulier`) and lets you filter at run time.
- ✅ **Postcode & radius geo-targeting:** Center searches on any Dutch (`1012AB`) or Belgian (`2000`) postcode with a custom `distanceMeters` radius, and export exact listing `latitude` and `longitude`.
- ✅ **High-resolution image & attribute extraction:** Normalizes protocol-relative image URLs to `https://` at maximum resolution and merges all vertical attributes (`attributes` + `extendedAttributes` + `verticals`) into one clean object.

***

### 🎯 Use cases

#### 1. Used-car dealership sourcing & BPM valuation

Dutch and European car dealers track private listings (`sellerTypeFilter: "private"`) in category `91` (*Auto's*) sorted by newest first (`SORT_INDEX` + `DECREASING`), capturing price, location, seller ID, and car attributes to spot underpriced vehicles within minutes of listing.

#### 2. Recommerce & electronics trade-in pricing

Refurbished phone, laptop, and audio retailers scrape iPhone, MacBook, Samsung Galaxy, and PlayStation listings across `new`, `like_new`, and `used` conditions to calibrate automated trade-in and buyback pricing engines.

#### 3. B2B lead generation (`zakelijk` sellers)

Marketing agencies and B2B SaaS vendors filter `sellerTypeFilter: "business"` across categories like *Huis en Inrichting* (`322`), *Auto's* (`91`), or *Diensten en Vakmensen* (`34`) to identify active commercial merchants on Marktplaats and 2dehands.be, grouping by `sellerId` to rank top-volume dealers.

#### 4. Local supply & real-estate research

Analysts monitor category `135` (*Huizen en Kamers*) and `356` (*Vakantie*) alongside postcode radius filters to track private room rentals, garage boxes, vacation chalets, and commercial spaces across Dutch provinces.

***

### 📥 Input parameters

All fields are optional. If you run the Actor with empty input (`{}`), it automatically collects a fresh, balanced 100-listing sample across 10 popular Dutch search terms.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `platform` | `string` | `"marktplaats.nl"` | Marketplace to scrape: `"marktplaats.nl"` (Netherlands), `"2dehands.be"` (Belgium NL), or `"2ememain.be"` (Belgium FR). |
| `query` | `string` | `""` (prefill `"iphone"`) | Search keyword(s) matched against title and description (e.g. `"iphone 15"`, `"bmw 320d"`, `"vouwfiets"`). Supports comma-separated keywords (e.g. `"iphone 15, iphone 16, samsung s24"`) for multi-query sweeps. |
| `category` | `string` | `""` (All categories) | L1 category dropdown: Cars (`91`), Car parts (`92`), Bikes & mopeds (`1655`), Houses & rooms (`135`), Home & furniture (`322`), Phones & telecom (`31`), Computers (`504`), Audio/TV/Photo (`1525`), Clothing (`621`), Sport (`728`), Kids (`1099`), Antiques (`367`), Tickets (`2017`), Books (`380`), Holidays (`356`), Jobs (`33`), Services (`34`), etc. |
| `subcategoryId` | `string` | `""` | Optional L2 subcategory numeric ID (e.g. `"1953"` for Apple iPhone inside Telecom). |
| `sellerTypeFilter` | `string` | `"all"` | Filter by seller type: `"all"`, `"private"` (*particulier*), or `"business"` (*zakelijk* / dealer). |
| `conditionFilter` | `array<string>` | `[]` | Keep only selected conditions: `"new"` (*Nieuw*), `"like_new"` (*Zo goed als nieuw*), `"used"` (*Gebruikt*). Empty = all conditions. |
| `postcode` | `string` | `""` | Dutch or Belgian postcode to center a radius search (e.g. `"1012AB"` Amsterdam, `"3011AA"` Rotterdam, `"2000"` Antwerp). |
| `distanceMeters` | `integer` | `0` | Search radius around `postcode` in meters (e.g. `10000` = 10 km, `50000` = 50 km). `0` = nationwide. |
| `minPrice` | `integer` | `0` | Minimum price in **euro cents** (`10000` = €100.00). `0` = no minimum. |
| `maxPrice` | `integer` | `0` | Maximum price in **euro cents** (`100000` = €1,000.00). `0` = no maximum. |
| `sortBy` | `string` | `"SORT_INDEX"` | Sort order: `"SORT_INDEX"` (newest first), `"PRICE"` (by price), or `"OPTIMIZED"` (best relevance match). |
| `sortOrder` | `string` | `"DECREASING"` | Sort direction: `"DECREASING"` (high-to-low / newest first) or `"INCREASING"` (low-to-high / oldest first). |
| `scrapeDetails` | `boolean` | `false` | When `true`, also opens each individual listing page to enrich the full-length description and detail-only attributes. |
| `maxListings` | `integer` | `0` (prefill `100`) | Maximum total listings to collect. Set `0` for unlimited (up to **1,000–5,000+ listings** per search or multi-keyword run). |
| `requestDelay` | `integer` | `500` | Pause between page requests in milliseconds (`0`–`10000`). |
| `proxyConfiguration` | `object` | `{ "useApifyProxy": true }` | Apify Proxy configuration. Default datacenter proxy works out of the box. |

#### Example input — 1,000+ iPhone & smartphone price benchmark (NL)

```json
{
  "platform": "marktplaats.nl",
  "query": "iphone 15, iphone 16",
  "category": "31",
  "sellerTypeFilter": "all",
  "conditionFilter": ["new", "like_new", "used"],
  "minPrice": 20000,
  "maxPrice": 150000,
  "sortBy": "SORT_INDEX",
  "sortOrder": "DECREASING",
  "scrapeDetails": false,
  "maxListings": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

#### Example input — Belgian used cars from private sellers (2dehands.be)

```json
{
  "platform": "2dehands.be",
  "query": "volkswagen golf",
  "category": "91",
  "sellerTypeFilter": "private",
  "postcode": "2000",
  "distanceMeters": 50000,
  "sortBy": "PRICE",
  "sortOrder": "INCREASING",
  "maxListings": 500
}
```

***

### 📤 Output schema & field reference

Every listing is saved as a flat, normalized JSON object across all three Benelux platforms.

#### 🏷️ Core Listing & Pricing Fields

| Field | Type | Coverage | Description |
|---|---|---|---|
| `listingId` | `string` | 🟢 High (100%) | Unique marketplace listing ID (e.g. `"m2460844151"`). Used for automatic deduplication. |
| `platform` | `string` | 🟢 High (100%) | Source marketplace: `"marktplaats.nl"`, `"2dehands.be"`, or `"2ememain.be"`. |
| `title` | `string` | 🟢 High (100%) | Full ad title as displayed on the marketplace. |
| `price` | `number \| null` | 🟢 High (~92%) | Numeric asking price in EUR (converted from cents). `null` when `priceType` is `bidding`, `free`, or `see_description`. |
| `priceType` | `string` | 🟢 High (100%) | Normalized price type: `fixed`, `bidding`, `free`, `asking`, `trade`, `negotiable`, `on_request`, `see_description`, or `reserved`. |
| `priceTypeOriginal` | `string` | 🟢 High (100%) | Original Dutch or French price label (`Vaste prijs`, `Bieden`, `Gratis`, `Prix fixe`, `Faire offre`). |
| `currency` | `string` | 🟢 High (100%) | Always `"EUR"`. |
| `condition` | `string \| null` | 🟢 High (~95%) | Normalized item condition: `new`, `like_new`, `used`, or `not_applicable`. |
| `conditionOriginal` | `string \| null` | 🟢 High (~95%) | Original Dutch or French condition string (`Nieuw`, `Zo goed als nieuw`, `Gebruikt`, `Comme neuf`). |

#### 📂 Category, Specifications & Media

| Field | Type | Coverage | Description |
|---|---|---|---|
| `category` | `string \| null` | 🟢 High (~99%) | Human-readable L1 category name (e.g. `"Telecommunicatie"`, `"Auto's"`). |
| `categoryId` | `string \| null` | 🟢 High (100%) | Numeric L1 category ID (e.g. `"31"`, `"91"`). |
| `subcategory` | `string \| null` | 🟢 High (~95%) | Human-readable L2 subcategory name (e.g. `"Mobiele telefoons \| Apple iPhone"`). |
| `subcategoryId` | `string \| null` | 🟢 High (100%) | Numeric L2 subcategory ID (e.g. `"1953"`). |
| `attributes` | `object \| null` | 🟢 High (~98%) | Category-specific key/value attributes (e.g. `storage`, `battery health`, `color`, `delivery`, `bouwjaar`, `kilometerstand`). |
| `description` | `string \| null` | 🟢 High (100%) | Listing description snippet (or full text when `scrapeDetails: true`). |
| `images` | `array<string> \| null` | 🟢 High (~99%) | Deduplicated array of absolute `https://` high-resolution image URLs. |
| `shippingAvailable` | `boolean` | 🟢 High (100%) | `true` if Marktplaats/2dehands shipping (*verzendservice*) is enabled on the listing. |

#### 👤 Seller, Geo-Location & Metadata

| Field | Type | Coverage | Description |
|---|---|---|---|
| `sellerName` | `string \| null` | 🟢 High (100%) | Seller or dealership display name. |
| `sellerId` | `string \| null` | 🟢 High (100%) | Unique internal seller ID — ideal for grouping all listings from a single dealer. |
| `sellerType` | `string` | 🟢 High (100%) | `"private"` (*particulier*) or `"business"` (*zakelijk* commercial seller). |
| `location` | `string \| null` | 🟢 High (100%) | Seller city or municipality (e.g. `"Amsterdam"`, `"Rotterdam"`, `"Antwerpen"`). |
| `country` | `string \| null` | 🟢 High (100%) | Country name (`"Nederland"` or `"België"`). |
| `latitude` | `number \| null` | 🟢 High (~99%) | Approximate listing latitude for geographic heatmaps and distance calculations. |
| `longitude` | `number \| null` | 🟢 High (~99%) | Approximate listing longitude. |
| `date` | `string \| null` | 🟢 High (100%) | Listing date label (`"Vandaag"`, `"Gisteren"`, `"Eergisteren"`, or date string). |
| `url` | `string` | 🟢 High (100%) | Full canonical URL to the listing on Marktplaats.nl, 2dehands.be, or 2ememain.be. |
| `searchQuery` | `string \| null` | 🟢 High | Search keyword that produced the record. |
| `searchCategory` | `string \| null` | 🟡 Conditional | Category filter ID used in the search, if any. |
| `scrapedAt` | `string` | 🟢 High (100%) | ISO-8601 UTC extraction timestamp. |

#### Sample output record

```json
{
  "listingId": "m2460844151",
  "platform": "marktplaats.nl",
  "title": "Apple iPhone 16 Blauw 128GB - Zo goed als nieuw",
  "price": 600,
  "priceType": "fixed",
  "priceTypeOriginal": "Vaste prijs",
  "currency": "EUR",
  "condition": "like_new",
  "conditionOriginal": "Zo goed als nieuw",
  "category": "Telecommunicatie",
  "categoryId": "820",
  "subcategory": "Mobiele telefoons | Apple iPhone",
  "subcategoryId": "1953",
  "sellerName": "Artem",
  "sellerId": "55707512",
  "sellerType": "private",
  "location": "Lemele",
  "country": "Nederland",
  "latitude": 52.458435120409,
  "longitude": 6.4229765265222,
  "date": "Vandaag",
  "description": "Er zit een doos bij, maar deze is niet origineel. De kabel en het hoesje worden meegeleverd.",
  "attributes": {
    "delivery": "Ophalen of Verzenden",
    "battery health": "97 %",
    "storage": "128 GB",
    "color": "Blauw",
    "type": "IPhone 16"
  },
  "images": [
    "https://images.marktplaats.com/api/v1/hz-mp-pro-listing/images/c37a528b-6f61-4d00-85c4-9ab03927645c?rule=ecg_mp_eps$_82.jpg"
  ],
  "shippingAvailable": false,
  "url": "https://www.marktplaats.nl/v/telecommunicatie/mobiele-telefoons-apple-iphone/m2460844151-apple-iphone-16-blauw-128gb-zo-goed-als-nieuw",
  "searchQuery": "iphone",
  "searchCategory": null,
  "scrapedAt": "2026-11-10T02:00:00.000Z"
}
```

***

### 🔁 Pagination & scaling to 1,000+ listings

- **Automatic multi-page pagination:** The scraper automatically steps through result pages (30 listings per request) until `maxListings` is reached or all matching ads are consumed.
- **1,000+ to 10,000+ listings per run:** Set `maxListings: 1000` (or `0` for unlimited) to collect thousands of listings in a single run. You can also pass multiple comma-separated keywords in `query` (e.g. `"iphone 14, iphone 15, iphone 16"`) to sweep entire product families across tens of thousands of listings.
- **Cross-page & cross-keyword deduplication:** An in-memory `seenIds` set keyed on `listingId` guarantees **0 duplicate listings** even when search terms overlap or new ads push pagination offsets mid-run.

***

### ⚙️ How to use & integrate

#### Python SDK example

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("haketa/marktplaats-scraper").call(run_input={
    "platform": "marktplaats.nl",
    "query": "bmw 320d",
    "category": "91",
    "sellerTypeFilter": "all",
    "postcode": "3011AA",
    "distanceMeters": 50000,
    "sortBy": "PRICE",
    "sortOrder": "INCREASING",
    "maxListings": 1000,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"{item['listingId']} | €{item['price']} ({item['priceType']}) | {item['title']} | {item['location']}")
```

#### Node.js SDK example

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('haketa/marktplaats-scraper').call({
    platform: 'marktplaats.nl',
    query: 'iphone 15, iphone 16',
    category: '31',
    subcategoryId: '1953',
    maxListings: 1000,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Extracted ${items.length} unique Marktplaats listings`);
```

***

### 💡 Pro tips for Benelux marketplace scraping

- **Remember `minPrice` and `maxPrice` use euro cents:** Enter `10000` for €100 or `250000` for €2,500. To isolate free giveaway items (*Gratis*), set `maxPrice: 1`.
- **Keep `scrapeDetails: false` for fast bulk runs:** The search response already includes price, condition, seller ID, coordinates, images, description snippet, and key vertical attributes (`attributes`). Turn on `scrapeDetails: true` only when you need the full un-truncated description text.
- **Segment large categories by price band or postcode:** When scraping ultra-dense verticals like *Auto's* (`91`) or *Huis en Inrichting* (`322`), combine `minPrice`/`maxPrice` brackets or comma-separated keywords in `query` to capture 10,000+ listings cleanly.
- **Switch `platform` for instant Belgian market comps:** Compare Dutch and Belgian second-hand prices by running the exact same query against `marktplaats.nl`, `2dehands.be`, and `2ememain.be`.

***

### ❓ Frequently Asked Questions

#### How fast is the Marktplaats scraper?

With `scrapeDetails: false` (the default), the Actor collects **100 listings in ~10 seconds** and **1,000 listings in ~60–90 seconds** using lightweight HTTP requests.

#### Do I need residential proxies or a Marktplaats account?

No account or login is ever required. Standard Apify Proxy (`useApifyProxy: true`, enabled by default) works reliably for both Dutch and Belgian platforms without expensive residential proxy overhead.

#### Why is `price` sometimes `null`?

On Marktplaats and 2dehands.be, sellers can list items as *Bieden* (`bidding`), *Gratis* (`free`), *Ruilen* (`trade`), *Zie omschrijving* (`see_description`), or *Nader overeen te komen* (`negotiable`) without a fixed numeric price. Check `priceType` and `priceTypeOriginal` to see the exact pricing model.

#### Can I filter out commercial dealers or keep only business sellers?

Yes. Set `sellerTypeFilter: "private"` to keep only private individuals (*particulieren*), or `sellerTypeFilter: "business"` to extract only commercial sellers (*zakelijke verkopers*).

#### Does this Actor work for 2dehands.be and 2ememain.be in Belgium?

Yes. Select `2dehands.be` (Dutch/Flemish) or `2ememain.be` (French) in the `platform` dropdown. All output fields (`priceType`, `condition`, `currency`, `latitude`, `longitude`, etc.) are normalized to the exact same schema.

#### Can I scrape multiple keywords in a single run?

Yes — pass comma-separated keywords in `query` (for example, `"iphone 14, iphone 15, iphone 16, samsung s24"`) and set `maxListings: 1000` (or `0` for unlimited). The Actor paginates through each keyword in sequence and deduplicates all results by `listingId`.

#### How do I track new listings or price drops every day?

Use Apify's built-in **Scheduler** to run the Actor hourly or daily with `sortBy: "SORT_INDEX"` and `sortOrder: "DECREASING"` (newest first), then push the dataset into Google Sheets, Make, Zapier, n8n, BigQuery, or PostgreSQL keyed on `listingId`.

***

### 🗂️ Supported Benelux L1 categories (`category` parameter)

| Category ID | Dutch Name (`marktplaats.nl` / `2dehands.be`) | English Vertical | Typical High-Value Use Cases |
|---|---|---|---|
| `"91"` | `Auto's` | Cars & Vehicles | Used-car dealership arbitrage, BPM valuation, EV depreciation tracking |
| `"92"` | `Auto-onderdelen` | Car Parts & Accessories | OEM/aftermarket parts pricing, dismantler inventory monitoring |
| `"1655"` | `Fietsen en Brommers` | Bikes, E-Bikes & Mopeds | E-bike resale benchmarks (VanMoof, Gazelle, Cortina, Fatbikes) |
| `"135"` | `Huizen en Kamers` | Houses, Rooms & Property | Private room rentals (*kamer te huur*), vacation homes, garage boxes |
| `"322"` | `Huis en Inrichting` | Home, Furniture & Interior | Designer furniture sourcing, showroom dealer lead generation |
| `"31"` | `Telecommunicatie` | Phones & Telecom | Smartphone trade-in & buyback pricing (iPhone, Samsung Galaxy) |
| `"504"` | `Computers en Software` | Computers, Laptops & Tech | MacBook, GPU, monitor & enterprise hardware recommerce |
| `"1525"` | `Audio, Tv en Foto` | Audio, TV & Cameras | Mirrorless camera, lens, and hi-fi equipment valuation |
| `"1953"` | `Sieraden, Tassen en Uiterlijk` | Luxury, Watches & Bags | Luxury watch & designer handbag authentication/price tracking |
| `"621"` | `Kleding` | Clothing & Fashion | Second-hand brand demand and resale volume analytics |
| `"728"` | `Sport en Fitness` | Sports & Fitness | Gym equipment, road bikes, water sports gear |
| `"1099"` | `Kinderen en Baby's` | Kids & Baby Gear | Strollers (Bugaboo, Joolz), LEGO, and baby furniture resale |
| `"367"` | `Antiek en Kunst` | Antiques & Art | Auction house and antique dealer sourcing |
| `"33"` / `"34"` | `Werk en Personeel` / `Diensten` | Jobs, Trades & Services | Local Dutch & Belgian contractor (`zakelijk`) lead generation |

***

### 🛠️ Ready-to-run workflow recipes

#### Recipe 1: Used-car private seller scout across the Netherlands

```json
{
  "platform": "marktplaats.nl",
  "query": "bmw 320, audi a4, mercedes c180",
  "category": "91",
  "sellerTypeFilter": "private",
  "minPrice": 500000,
  "maxPrice": 3500000,
  "sortBy": "SORT_INDEX",
  "sortOrder": "DECREASING",
  "maxListings": 1000
}
```

#### Recipe 2: Commercial dealer (`zakelijk`) lead generation in Belgium

```json
{
  "platform": "2dehands.be",
  "query": "",
  "category": "91",
  "sellerTypeFilter": "business",
  "sortBy": "SORT_INDEX",
  "sortOrder": "DECREASING",
  "maxListings": 1000
}
```

***

### 🔗 Related European marketplace & classifieds scrapers

- [Kleinanzeigen.de Scraper (Germany)](https://apify.com/haketa/kleinanzeigen-scraper) — Germany's #1 classifieds marketplace
- [Leboncoin.fr Scraper (France)](https://apify.com/haketa/leboncoin-scraper) — France's leading classifieds & real-estate portal
- [Subito.it Scraper (Italy)](https://apify.com/haketa/subito-scraper) — Italian classifieds for cars, property, and electronics
- [Wallapop Scraper (Spain, Italy & Portugal)](https://apify.com/haketa/wallapop-scraper) — Southern Europe's largest second-hand marketplace
- [Willhaben.at Scraper (Austria)](https://apify.com/haketa/willhaben-scraper) — Austria's largest classifieds, auto, and property site
- [Immoweb.be Scraper (Belgium)](https://apify.com/haketa/immoweb-scraper) — Belgium's #1 real-estate portal

***

### 📋 Changelog

- **v1.2 (2026-11)** — **Unlocked 1,000+ multi-keyword pagination & empty-run scaling:** Added support for comma-separated multi-keyword queries in `query`, raised offset pagination ceiling to 10,000, honored explicit `maxListings > 100` on default sample runs, and upgraded documentation with field coverage indicators.
- **v1.1 (2026-08)** — Added automatic L1/L2 category and subcategory name resolution from facet trees, normalized protocol-relative image URLs to `https://`, and merged `extendedAttributes` for automotive verticals.
- **v1.0 (2026-05)** — Initial release supporting `marktplaats.nl`, `2dehands.be`, and `2ememain.be` with Dutch/French price-type and condition normalization, postcode radius filtering, and optional detail-page enrichment.

# Changelog

This Actor's version history is a separate document: https://apify.com/haketa/marktplaats-scraper/changelog.md

# Actor input Schema

## `platform` (type: `string`):

Which marketplace to scrape. All three cover the Benelux second-hand market.

## `query` (type: `string`):

Type what you are looking for, e.g. 'iphone 15', 'bmw 320', 'vouwfiets', 'bank'. Leave empty to browse a whole category, or leave everything empty for a mixed sample of popular listings.

## `category` (type: `string`):

Pick a category to browse an entire vertical. Works with or without a search keyword. Choose 'All categories' to search across the whole marketplace.

## `sellerTypeFilter` (type: `string`):

Keep only private sellers (particulieren) or only business sellers / dealers (zakelijk), or return both.

## `conditionFilter` (type: `array`):

Keep only listings in the selected condition(s). Leave empty to include every condition.

## `postcode` (type: `string`):

Center your search on a Dutch or Belgian postcode, e.g. '1012AB' (Amsterdam) or '3011AA' (Rotterdam). Combine with a distance below to search a local area.

## `distanceMeters` (type: `integer`):

How far around the postcode to search, in meters. Only used together with a postcode. Example: 10000 = 10 km, 50000 = 50 km. Leave 0 for the whole country.

## `minPrice` (type: `integer`):

Lowest price to include, in euro cents. Example: 10000 = €100. Leave 0 for no lower limit.

## `maxPrice` (type: `integer`):

Highest price to include, in euro cents. Example: 100000 = €1000. Leave 0 for no upper limit.

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

How to order the listings that come back.

## `sortOrder` (type: `string`):

Ascending or descending order for the sort above (e.g. price low-to-high vs high-to-low).

## `scrapeDetails` (type: `boolean`):

Also open each listing page for the full description and category-specific attributes (car specs, property details, etc.). More complete, but slower.

## `maxListings` (type: `integer`):

How many listings to collect in total. Set 0 for unlimited (Marktplaats caps a single search at ~5000).

## `subcategoryId` (type: `string`):

Power users only: narrow a category to one subcategory using its Marktplaats L2 ID (e.g. 1953 = Apple iPhone inside Telecom). Leave empty to include the whole category.

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

Proxy settings used to reach the marketplace reliably. The default works well for most runs.

## `requestDelay` (type: `integer`):

Pause between page requests, in milliseconds. Higher values are gentler on the site.

## Actor input object example

```json
{
  "platform": "marktplaats.nl",
  "query": "iphone",
  "category": "",
  "sellerTypeFilter": "all",
  "conditionFilter": [],
  "postcode": "",
  "distanceMeters": 0,
  "minPrice": 0,
  "maxPrice": 0,
  "sortBy": "SORT_INDEX",
  "sortOrder": "DECREASING",
  "scrapeDetails": false,
  "maxListings": 100,
  "subcategoryId": "",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "requestDelay": 500
}
```

# Actor output Schema

## `listingId` (type: `string`):

Marktplaats listing ID (m-format)

## `platform` (type: `string`):

marktplaats.nl / 2dehands.be / 2ememain.be

## `title` (type: `string`):

Listing title

## `price` (type: `string`):

Price in EUR

## `priceType` (type: `string`):

fixed / bidding / free / asking / trade

## `priceTypeOriginal` (type: `string`):

Original Dutch price type

## `currency` (type: `string`):

EUR

## `condition` (type: `string`):

new / like_new / used / not_specified

## `conditionOriginal` (type: `string`):

Original Dutch condition text

## `category` (type: `string`):

L1 category name

## `categoryId` (type: `string`):

L1 category numeric id

## `subcategory` (type: `string`):

L2 subcategory name

## `subcategoryId` (type: `string`):

L2 subcategory numeric id

## `sellerName` (type: `string`):

Seller display name

## `sellerId` (type: `string`):

Seller unique ID

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

private / business

## `location` (type: `string`):

City or area name

## `country` (type: `string`):

Country (Nederland / België)

## `latitude` (type: `string`):

Approx. listing latitude (when available)

## `longitude` (type: `string`):

Approx. listing longitude (when available)

## `date` (type: `string`):

Listing date ISO format

## `description` (type: `string`):

Full listing description

## `attributes` (type: `string`):

Category-specific attributes

## `images` (type: `string`):

Image URLs array

## `shippingAvailable` (type: `string`):

Shipping available

## `url` (type: `string`):

Full listing URL

## `searchQuery` (type: `string`):

Query used to find this listing

## `searchCategory` (type: `string`):

Category ID used in search

## `scrapedAt` (type: `string`):

ISO timestamp when scraped

# 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 = {
    "query": "iphone",
    "postcode": "",
    "maxListings": 100,
    "subcategoryId": "",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/marktplaats-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 = {
    "query": "iphone",
    "postcode": "",
    "maxListings": 100,
    "subcategoryId": "",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/marktplaats-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 '{
  "query": "iphone",
  "postcode": "",
  "maxListings": 100,
  "subcategoryId": "",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call haketa/marktplaats-scraper --silent --output-dataset

```

## MCP server setup

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