# Marktplaats Scraper (`s-r/marktplaats-scraper`) Actor

Search Marktplaats and get classified ads as rows: title, price and what that price means (fixed, minimum bid, auction, swap, on request), city with latitude and longitude, seller id, name and verification, condition, delivery and promotion flags.

- **URL**: https://apify.com/s-r/marktplaats-scraper.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** E-commerce, Business
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 run start fees

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

## Marktplaats Scraper

A Marktplaats scraper that returns classified ads with the price, what that price actually means, the seller, and the exact coordinates of the ad. No login, no API key, no browser.

Marktplaats is the dominant classifieds site in the Netherlands: 245.000 ads under "fiets" and 259.000 under cars as this was written. Nothing on the Apify Store covers it.

### What you get

- **A price you can trust**, because `price_type` tells you what it is. Marktplaats mixes fixed prices, minimum bids, auctions, swaps, free items and price-on-request in the same result list. Treating them all as an asking price is the classic error, and it turns a bidding lot into a bargain that is not there
- **Latitude and longitude on every ad**, which the rendered card never shows. Distance filtering and map work need no geocoding step
- **Seller id, name and verification status**, so one seller's whole inventory can be grouped and a trader can be told apart from a private ad
- **Promotion flags**: whether the seller paid to push the ad, and which promotion they bought
- **Condition and delivery** as their own fields, plus every other attribute the ad carries
- **Real pagination.** The site reports how many pages it allows and the walk follows the path form that actually moves
- **Charged per ad you receive**, plus a flat $0,002 start fee. No per-compute-unit charges

### Why scrape Marktplaats

Second-hand pricing is the least documented price data there is. New goods have RRPs, comparison sites and feeds; used goods have whatever someone is asking today in one town. For anyone in recommerce, refurbished electronics, bicycles, cars or furniture, Marktplaats is where the Dutch market actually clears.

There is no public API. Every result page carries its ads as JSON inside the page for the browser to render, and that payload is richer than what gets drawn: coordinates, seller ids, promotion types and the price semantics all live there and never reach the card.

The price semantics are the part that decides whether your dataset is usable. An ad at "€ 160" might be a fixed price, the opening bid of an auction, or a placeholder on an ad whose real price is "neem contact op". This actor reports the amount only where there is one, and names the type in every case.

### Input

| Field | Type | Required | Default | What it does |
|---|---|---|---|---|
| `query` | string | yes | `fiets` | What to search for. Dutch terms work best |
| `url` | string | no | – | A marktplaats.nl search or category URL to read instead. Takes precedence |
| `sort` | select | no | `relevance` | Relevance, newest, price ascending or descending |
| `price_min` / `price_max` | integer | no | – | Price bounds in euros |
| `limit` | integer | no | `60` | Ads to return, 1 to 900 |
| `retries` | integer | no | `3` | Retry attempts per page |

### Output

```json
{
  "position": 1,
  "item_id": "m2437697090",
  "url": "https://www.marktplaats.nl/v/a2437697090",
  "title": "damesfiets",
  "price": 160.0,
  "price_cents": 16000,
  "price_type": "minimum bid",
  "price_type_code": "MIN_BID",
  "currency": "EUR",
  "city": "Rosmalen",
  "country": "NL",
  "latitude": 51.720768247421,
  "longitude": 5.3649127431692,
  "posted_label": "Vandaag",
  "seller_id": 4063331,
  "seller_name": "niste065",
  "seller_verified": false,
  "condition": "Gebruikt",
  "delivery": "Ophalen",
  "is_promoted": true,
  "promotion_type": "DAGTOPPER",
  "is_reserved": false,
  "images_count": 3
}
```

### Use cases

**Second-hand price research.** Run a model name, filter to `price_type: "fixed price"`, and the distribution is the real Dutch resale market for that item. Excluding minimum bids and auctions matters more than it sounds: they cluster low and drag any average down by a third.

**Recommerce sourcing.** Sort by price ascending with a floor, and check `condition` and `delivery`. Because every ad carries coordinates, you can restrict to what is collectable within an hour's drive before you look at a single listing by hand.

**Watching a trader.** `seller_id` groups a seller's entire inventory and `is_business_seller` separates traders from private sellers. Schedule a run, store ids, and you see what they take in, what sells, and what has been sitting.

**Market supply monitoring.** `totalResultCount` on every run tells you how many ads exist for a query. Tracked over weeks, that number is a supply curve for a category, and it moves before prices do.

### How it compares

| | this actor | `apify/e-commerce-scraping-tool` |
|---|---|---|
| Per 1.000 ads | **$1,80** | $6,00 |
| Covers Marktplaats | **yes** | generic, needs configuring |
| Price type semantics | **7 types named** | no |
| Coordinates per ad | **yes** | no |
| Seller id and verification | **yes** | no |
| Promotion flags | **yes** | no |

Honest about the other side: `apify/e-commerce-scraping-tool` has 575 monthly users and points at almost any shop. It is a breadth tool. This one knows one marketplace's data model.

### Pricing

Two events. A flat `run_start` of $0,002 covers the run, and `listing` costs $0,0018 per ad returned, which is $1,80 per 1.000. All pricing is pay-per-event, so beyond the start fee you only pay for ads you actually receive. No per-compute-unit charges.

### Limits and gotchas

- **Pagination is a path segment, not a query parameter.** `?page=2` returns HTTP 200 with page one's ads. This actor uses `/p/2/`, which is the form that moves. Worth knowing if you build your own URLs to pass into `url`.
- **Not every ad has a price.** Price-on-request, swap-only and see-description ads carry no number by design. `price` is null and `price_type` says which. The run summary counts them.
- **Coordinates are approximate** for private sellers: Marktplaats reports a point near the ad rather than an address.
- **`description` is the listing excerpt**, not the full ad text. Opening each ad would be a second fetch per row and is not part of this actor.
- **Promoted ads sit at the top** of most result sets. Filter on `is_promoted` before computing market statistics.
- **900 ads per run** across 30 pages, and the site caps how deep any single search goes; the summary reports its own ceiling.

### FAQ

**Can I scrape Marktplaats without an API key?**
Yes. Every result page carries its ads as JSON for the browser to render, and that is what this actor reads. There is no credential in the input.

**Why do some ads have no price?**
Because the seller did not set one. Marktplaats supports price-on-request, swap-only and see-description ads. Those return a null price and a `price_type` saying so, rather than a misleading zero.

**Does it return the location of an ad?**
Yes, city plus latitude and longitude on every row, straight from the payload.

**Can I scrape a category instead of a search?**
Yes. Paste any marktplaats.nl listing URL into `url`, including one with filters already applied.

**How do I track one seller?**
Every row carries `seller_id`. Run a broad search for their category, group by that id, and repeat on a schedule.

### Related Actors

- [AutoScout24 Scraper](https://apify.com/s-r/autoscout24-scraper) — car listings across nine European markets
- [Price Scraper](https://apify.com/s-r/price-scraper---extract-prices-availability-from-any-url) — price and availability from any product URL
- [Bol.com Price Scraper](https://apify.com/s-r/free-bol-com-price-scraper---extract-offers-ean-bol-com-bpid) — Dutch marketplace offers by EAN

# Actor input Schema

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

What to search for on marktplaats.nl. Dutch terms work best.

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

A marktplaats.nl search or category URL to read instead of a search term. Paste it from your browser after applying any filters. Takes precedence.

## `sort` (type: `string`):

How to order the ads.

## `price_min` (type: `integer`):

Lower price bound in euros.

## `price_max` (type: `integer`):

Upper price bound in euros.

## `limit` (type: `integer`):

How many ads to return, 1 to 900. A page carries 30 and the site reports how many pages it allows.

## `retries` (type: `integer`):

Retry attempts per page, each with a rotated user agent and TLS fingerprint.

## Actor input object example

```json
{
  "query": "fiets",
  "url": "https://www.marktplaats.nl/l/auto-s/",
  "sort": "relevance",
  "limit": 60,
  "retries": 3
}
```

# Actor output Schema

## `results` (type: `string`):

One row per classified ad.

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

Total results, pages fetched, how many ads carried a price and how many were geo-coded.

## `errors` (type: `string`):

Per-page failures with a code and a redacted message.

# 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": "fiets",
    "limit": 60,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/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": "fiets",
    "limit": 60,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/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": "fiets",
  "limit": 60,
  "retries": 3
}' |
apify call s-r/marktplaats-scraper --silent --output-dataset

```

## MCP server setup

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