# Marktplaats Scraper: Dutch & Belgian Classifieds (`reventadata/marktplaats-scraper`) Actor

Scrape Marktplaats.nl, 2dehands.be and 2ememain.be ads by keyword or category: title, price and price type, condition, delivery, city and coordinates, seller name, images and attributes. Filter by price, seller type, promoted and reserved. Unofficial.

- **URL**: https://apify.com/reventadata/marktplaats-scraper.md
- **Developed by:** [reventadata](https://apify.com/reventadata) (community)
- **Categories:** E-commerce, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 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.

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

> **Unofficial.** This Actor is not affiliated with, authorised by, endorsed by, or in any
> way officially connected to Marktplaats, 2dehands or 2ememain. The names are trademarks of
> their respective owners and are used here only to describe what this tool reads. It reads
> the same public search pages a browser does.

Marktplaats Scraper reads classified ads from [marktplaats.nl](https://www.marktplaats.nl)
(Netherlands), [2dehands.be](https://www.2dehands.be) (Belgium, Dutch) and
[2ememain.be](https://www.2ememain.be) (Belgium, French). One input, `country`, picks the site;
the output fields are the same on all three.

### Supported sites

| `country` | Site | Market | Language of text fields | `countryCode` |
|---|---|---|---|---|
| `nl` | [marktplaats.nl](https://www.marktplaats.nl) | Netherlands | Dutch | `NL` |
| `be-nl` | [2dehands.be](https://www.2dehands.be) | Belgium | Dutch | `BE` |
| `be-fr` | [2ememain.be](https://www.2ememain.be) | Belgium | French | `BE` |

The two Belgian sites are one inventory shown in two languages: a search for the same term returns
largely the same ads, with category names, condition and delivery in the site's language. Pick the
one whose language you want. Category ids are the same on all three sites; category paths are not
(see "Things to know").

### What data can you extract?

For every listing the search pages show: title, a short text snippet, price and price type,
the placement date as the site words it, city and coordinates, up to four image URLs, seller
name and id, category ids, condition and delivery, the site's other attributes (storage, colour
and so on, per category), and flags for promoted and reserved listings.

### How it works

- **Keyword or category.** Give `searchQueries` (one search per term) and/or `categoryPaths`
  (the part of a category address after `/l/`, for example
  `telecommunicatie/mobiele-telefoons-apple-iphone`).
- **Filters run after fetching.** This Actor applies price range, seller kind,
  promoted/reserved exclusion and the optional keyword check after fetching. There is no sort
  option: the order is the site's own.
- **30 listings per page.** The sites stop serving results after a certain page (167 at most,
  sometimes fewer). The run ends on the first empty page.
- **No proxy required by default.** An optional proxy setting is available if needed.

### Things to know

- **Dates are the site's own words** ("Vandaag", "Gisteren", "Aujourd'hui", or a short date),
  with no time of day. The Actor does not invent an ISO date.
- **`sellerTypeInferred` is inferred.** The sites do not label private and business sellers; a
  listing with the business-profile traits counts as business.
- **`price` is null** for `FAST_BID` listings (the site gives no amount) and for `SEE_DESCRIPTION` listings. `MIN_BID` prices
  are a starting bid.
- **The text is a snippet** of about 200 characters. Phone numbers and e-mail addresses are not
  in search results and are not fetched.
- Sponsored placements above the results are left out. Paid "top of the day" listings repeat
  across pages; each id is returned once and flagged `isPromoted`.
- The two Belgian sites carry the same ads in two languages.
- **Category paths are language-specific.** On `be-fr` (2ememain.be) the slugs are French, for
  example `telecoms/telephonie-mobile-apple-iphone`; the Dutch slug
  `telecommunicatie/mobiele-telefoons-apple-iphone` answers 404 there and the run fails with a
  message saying so. Copy the path from the site you search.
- **`countryCode`** is the site's own value when a listing carries one; the sites do not send it
  in search results, so it is `NL` for `nl` and `BE` for `be-nl` and `be-fr`.

### Input

| Field | Meaning |
|---|---|
| `country` | `nl`, `be-nl` or `be-fr` |
| `searchQueries`, `categoryPaths` | what to search; at least one is required |
| `minPrice`, `maxPrice` | euro bounds, inclusive, applied after fetching |
| `sellers` | `any`, `private` or `business` (inferred) |
| `excludePromoted`, `excludeReserved` | drop those listings |
| `requireKeywordMatch` | every query word must be in the title or snippet |
| `maxItems`, `maxPages` | caps (30 listings per page) |
| `proxyConfiguration` | optional; off by default |

### Example output

One row, from a platform run on 2026-11-11 (keyword `iphone`, `country` `nl`; the description snippet is shortened and the image list cut to 2 entries). All 36 fields are shown; a field the site gives no value for is `null`:

```json
{
  "id": "m2463299456",
  "url": "https://www.marktplaats.nl/v/telecommunicatie/mobiele-telefoons-apple-iphone/m2463299456-iphone-16-pro-128gb-zwart-zo-goed-als-nieuw",
  "title": "iPhone 16 Pro 128GB Zwart - Zo goed als nieuw",
  "descriptionSnippet": "Iphone 16 pro 128 gb - zwart te koop! Batterijconditie is 89%, en altijd hoesje en screenp...",
  "price": 600,
  "priceType": "MIN_BID",
  "currency": "EUR",
  "dateText": "Vandaag",
  "city": "Enschede",
  "country": "Nederland",
  "countryCode": "NL",
  "latitude": 52.216171164603,
  "longitude": 6.8516277982403,
  "abroad": false,
  "imageUrls": [
    "https://images.marktplaats.com/api/v1/hz-mp-pro-listing/images/b2267465-d9df-4adc-8a34-5eabd356e11c?rule=ecg_mp_eps$_82.jpg",
    "https://images.marktplaats.com/api/v1/hz-mp-pro-listing/images/df3a4468-3b67-4ac2-951d-7d6c436a7466?rule=ecg_mp_eps$_82.jpg"
  ],
  "sellerId": 23257313,
  "sellerName": "Isabelle",
  "sellerVerified": false,
  "sellerWebsiteUrl": null,
  "sellerTypeInferred": "private",
  "categoryId": 1953,
  "parentCategoryId": 820,
  "verticals": [
    "mobile_phones_apple_iphone",
    "telecommunication"
  ],
  "condition": "Zo goed als nieuw",
  "delivery": "Ophalen",
  "attributes": {
    "condition": "Zo goed als nieuw",
    "delivery": "Ophalen",
    "storage": "128 GB",
    "battery health": "89 %",
    "color": "Zwart"
  },
  "traits": [
    "DAG_TOPPER",
    "PACKAGE_FREE"
  ],
  "priorityProduct": "DAGTOPPER",
  "isPromoted": true,
  "isReserved": false,
  "hasPhoneFlag": false,
  "searchCountry": "nl",
  "searchQuery": "iphone",
  "categoryPath": null,
  "searchUrl": "https://www.marktplaats.nl/q/iphone/",
  "scrapedAt": "2026-11-11T06:32:21.482807+00:00"
}
```

### Pricing

The current price is on the Actor's Pricing tab.

# Actor input Schema

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

Which site to search. nl = marktplaats.nl (Netherlands). be-nl = 2dehands.be (Belgium, Dutch). be-fr = 2ememain.be (Belgium, French). The two Belgian sites show the same ads in two languages, so choose by the language you want for fields such as condition and delivery.

## `searchQueries` (type: `array`):

One search per term, across every category of the chosen site. Optional when you give a category path instead. Several words are searched together.

## `categoryPaths` (type: `array`):

Optional. Lists a whole category, one entry per category: the part of a category page address between /l/ and the end, for example telecommunicatie/mobiele-telefoons-apple-iphone from marktplaats.nl/l/telecommunicatie/mobiele-telefoons-apple-iphone/. A pasted full address works too. Category slugs are language-specific: on be-fr (2ememain.be) they are French, for example telecoms/telephonie-mobile-apple-iphone from 2ememain.be/l/telecoms/telephonie-mobile-apple-iphone/, and the Dutch slug of the same category returns a 404 there. Category ids are shared across the three sites, the slugs are not, so copy the path from the site you search. A category and a search term are not combined; each entry is its own search.

## `sellers` (type: `string`):

The sites do not label private and business sellers. This is inferred: a listing that carries the site's business-profile markers counts as business, every other listing as private. Applied after fetching.

## `minPrice` (type: `number`):

Optional, inclusive. Applied after fetching, because the sites' own price filter is not offered on the pages this Actor is allowed to fetch. Listings without an amount (bidding, see description) are dropped whenever a bound is set. Some prices are a starting bid (priceType MIN_BID).

## `maxPrice` (type: `number`):

Optional, inclusive. Applied after fetching, like the minimum.

## `excludePromoted` (type: `boolean`):

Drops the paid top-of-the-day listings that repeat across result pages. Off by default; they are returned once and flagged isPromoted. Sponsored placements above the results are always left out.

## `excludeReserved` (type: `boolean`):

Drops listings the seller has marked as reserved for a buyer.

## `requireKeywordMatch` (type: `boolean`):

Drops listings where a word of the search term is missing from both the title and the short text snippet (about 200 characters). The sites also search the full text, so a search for a phone can return cases and accessories. Ignored for category paths. Off by default.

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

Hard cap on rows pushed across all searches.

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

30 listings a page. The sites stop serving results after a certain page (167 at most, and sometimes fewer); the run ends on the first empty page.

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

Optional. Left off by default; only turn it on if you see request failures.

## Actor input object example

```json
{
  "country": "nl",
  "searchQueries": [
    "iphone"
  ],
  "sellers": "any",
  "excludePromoted": false,
  "excludeReserved": false,
  "requireKeywordMatch": false,
  "maxItems": 10,
  "maxPages": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Items scraped from the target website.

# 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 = {
    "searchQueries": [
        "iphone"
    ],
    "maxItems": 10,
    "maxPages": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("reventadata/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 = {
    "searchQueries": ["iphone"],
    "maxItems": 10,
    "maxPages": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("reventadata/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 '{
  "searchQueries": [
    "iphone"
  ],
  "maxItems": 10,
  "maxPages": 1
}' |
apify call reventadata/marktplaats-scraper --silent --output-dataset

```

## MCP server setup

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