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

Scrape Marktplaats.nl as clean JSON: paste your search links, filters included, or run several searches at once, and get only new listings on a schedule. Prices, condition, category specs, locations and images. GDPR-clean: no seller names or contact data, ever.

- **URL**: https://apify.com/lowlanddata/marktplaats-scraper.md
- **Developed by:** [Lowland Data](https://apify.com/lowlanddata) (community)
- **Categories:** E-commerce, Automation, Business
- **Features:** Limited permissions
- **Stats:** 20 total users, 9 monthly users, 99.9% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.59 / 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

Marktplaats.nl is the default place Dutch people sell things, which makes it the best price reference the Netherlands has and the most tedious one to read. A plain question like what a used Gazelle goes for in Utrecht turns into fifty tabs and a notepad.

This hands you the whole search as rows: the price and what the price means, condition and category specs from attributes, municipality, photos, date. Postcode radius, price band and condition are filters you set before the run, not cleanup afterwards. Seller identity never enters the output.

**Live health record:** [lowlanddata.com/status/marktplaats-scraper](https://lowlanddata.com/status/marktplaats-scraper/) - 30-day success rate and daily canary results, failures included. Full input/output reference: [tool page](https://lowlanddata.com/tools/marktplaats-scraper/).

No login, no CAPTCHA solving, no private endpoints: the actor reads what Marktplaats shows a logged-out visitor. Names, IDs, phone numbers and profile links are not collected. The only seller field is `sellerType`, and it is a signal rather than a verified status: an account is marked `business` when it exposes a website link, so `private` means "no business signal was found" and some dealers will sit in it.

### Quick start (30 seconds)

1. Put what you'd type in the Marktplaats search box into **searchQuery** (e.g. `gazelle`).
2. Click **Start**. That's the whole minimum setup.
3. When the run finishes, open the dataset's **Overview** tab for a clean table, or **Export** it as CSV/Excel/JSON.

Everything else (price range, category, sorting) is an optional refinement, explained in the Input table below. To make it run by itself, create a **Schedule** in the Apify Console with the same input and add an email or Slack integration on the runs.

**Already have your search set up on marktplaats.nl?** Paste the address of the results page into **startUrls** instead, with every filter you picked on the site (category, brand, condition, distance...). Paste several to watch several searches in one run, and switch on **onlyNewListings** so each scheduled run returns only the listings you have not received yet.

### What you can build with it

- **Flip underpriced finds before anyone else.** Sort by date, watch a category like road bikes or PS5 consoles, and get every new listing the minute it posts, with the price and condition already parsed, so your alert fires while the deal is still live.
- **Price a car properly.** Pull every BMW 5-Series within a year range and read `mileage`, `constructionYear`, `fuel`, `transmission` and the options list straight from `attributes`: build the price-vs-mileage curve that tells you what a car is really worth.
- **Track a market week over week.** Snapshot "e-bikes in Utrecht under €800" on a schedule and watch supply, median price and how fast things sell move over time, the raw material for a pricing report or a dashboard.
- **Feed an AI agent or a pipeline.** Every field is structured and predictable, so an assistant can consume it directly with no parsing step.

### What you get

Each listing is one dataset item:

```json
{
  "listingId": "m2420174899",
  "title": "Gazelle fiets 57 cm met Nexusnaaf",
  "description": "Degelijke gazelle fiets met een framehoogte van 57 cm...",
  "priceCents": 9000,
  "priceType": "MIN_BID",
  "city": "Utrecht",
  "country": "Nederland",
  "latitude": 52.0908,
  "longitude": 5.1222,
  "postedAt": "Vandaag",
  "postedTimestamp": "2026-09-30T14:07:30Z",
  "imageUrls": ["//images.marktplaats.com/api/v1/..."],
  "categoryId": 447,
  "url": "https://www.marktplaats.nl/v/fietsen-en-brommers/...",
  "attributes": { "condition": "Gebruikt", "frameHeight": "57 cm", "color": "Zwart" },
  "reserved": false,
  "sellerType": "private"
}
```

Category-specific attributes come through automatically. A car carries `constructionYear`, `mileage`, `fuel`, `transmission`, `body` and its full `options` list; a house carries `livingArea` and `numberOfRooms`; a phone carries `storage` and `cameraResolution`, whatever the marketplace records for that category, minus anything that identifies a seller.

Field notes, so you know exactly what you are buying:

- `priceCents` is the number the marketplace attaches to the listing, in euro cents; **read it together with `priceType`, which says what it means**: `FIXED` is an asking price, `MIN_BID` is a minimum bid (not what the seller expects to get), and `FAST_BID` / `SEE_DESCRIPTION` mean the figure is indicative at best. Listings without a price omit the field.
- `postedAt` is the posting date exactly as the marketplace displays it, a date like `5 jul 26`, or a relative label like `Vandaag` (today) for recent listings. `postedDate` is the same date normalized to `YYYY-MM-DD` (relative labels resolved in Dutch local time); it is omitted on the rare row whose label cannot be parsed.
- `postedTimestamp` is the listing's real creation instant, second-precise ISO-8601 (`2026-09-30T14:07:30Z`), read from the listing's own page. It only appears when **Fetch full descriptions & photos** (`scrapeDetails`) is on - the search results `postedAt`/`postedDate` come from only carry day granularity.
- `latitude`/`longitude` are the marketplace's own municipality-level coordinates, not a seller address.
- `description` is the ~200-character snippet the marketplace search returns. Turn on **Fetch full descriptions & photos** (`scrapeDetails`) to replace it with the seller's full listing text, read from each listing's own page. The seller/trader block is never read, so no seller contact is added.
- `imageUrls` carries one photo by default - the same limit the search results have. Turn on **Fetch full descriptions & photos** (`scrapeDetails`) to get every photo in the listing's gallery, read from its own page. URLs are protocol-relative (`//images...`); prepend `https:` to use them.
- `attributes` is the whitelisted, non-personal spec set for the listing's category, `condition` and `delivery` on anything, plus e.g. car specs, property size or phone storage where they apply. Multi-value fields like a car's `options` come through as a list.
- `reserved` tells you whether the marketplace currently marks the listing as reserved.

### How much does it cost to scrape Marktplaats?

$1.99 per 1,000 listings delivered, pay-as-you-go, no subscription, and you pay per result rather than per hour. A run that finds nothing still costs the $0.005 actor start. In plain dollars:

- **100 listings ≈ $0.20**, a daily category watch.
- **500 listings ≈ $1.00**, a solid market snapshot.
- **A 5,000-listing crawl ≈ $10**, a full category, deduplicated.

The per-result price is all-inclusive, no separate platform-usage charges, no residential proxy surcharge. Higher Apify plans get automatic unit-price discounts (down to $1.99/1,000 on Gold).

Free-plan runs are limited to a sample of 5 items, enough to evaluate the output format against your real query.

### Not technical? Let your AI assistant set it up

Copy this into ChatGPT, Claude or any AI assistant, fill in the one line, and follow the conversation:

```text
Help me set up the "Marktplaats.nl Scraper" actor on Apify
(https://apify.com/lowlanddata/marktplaats-scraper). Guide me one step at a time.

What I want to watch: [E.G. "electric bikes under 800 EUR, alert me on new listings"]

Guide me to:
1. Propose my input values: searchQuery (what I'd type in the Marktplaats search
   box, Dutch terms work best), optional priceMinEur/priceMaxEur, optional
   postcode + radiusKm if I only want listings near me, optional conditions
   (new/as-new/used/refurbished/not-working), delivery (pickup/shipping),
   sortBy "date" for newest-first monitoring, postedAfter/postedBefore
   (YYYY-MM-DD) to keep only listings from a date window, and maxItems. To re-check specific
   listings I already know, use listingIds with the ids from their URLs instead.
   If I already have searches set up on marktplaats.nl, use startUrls with the full
   addresses of their results pages instead, and onlyNewListings true so scheduled
   runs return only listings I have not received yet.
2. If my watch fits one category: run one broad search first, read the categoryId
   from the results I care about, and add it to the input for precise runs.
3. Create a free Apify account (apify.com), open the actor page, paste the values
   into the Input form, and start a run.
4. Set up a daily Schedule in the Apify Console with the same input, plus an email
   or Slack integration so new results reach me automatically.
5. Show me how to export results as CSV/Excel, or read them from the API if I code.
6. If the results are what I wanted, remind me at the end to leave a quick rating on the actor page, and to report anything broken or missing on its Issues tab.
```

### Input

| Field                          | Description                                                                                                                                                                                                                                                                                                                   |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `searchQuery`                  | Free-text search, e.g. `fiets`. Provide this, a category, or both.                                                                                                                                                                                                                                                            |
| `searchQueries`                | Several free-text searches in one run, each with the filters below. A listing found by two searches is delivered once. Up to 50.                                                                                                                                                                                              |
| `startUrls`                    | Addresses of Marktplaats results pages, copied from your browser including everything after the `#`. Every filter in the URL applies. When given, the form's search fields are ignored and the run status says which. Up to 50.                                                                                               |
| `categoryId`                   | Numeric category ID to scope the crawl. See below for finding IDs.                                                                                                                                                                                                                                                            |
| `listingIds`                   | Fetch these exact listings instead of searching, e.g. `["m2421501285"]` (the id in every listing URL and dataset item). Great for re-checking price or availability.                                                                                                                                                          |
| `postcode` / `radiusKm`        | Radius search: Dutch postcode (e.g. `3511AA`) plus a radius in km (up to 300). This narrows the search; the distance itself is not returned as a field.                                                                                                                                                                       |
| `conditions`                   | Only these conditions: `new`, `as-new`, `used`, `refurbished`, `not-working`.                                                                                                                                                                                                                                                 |
| `delivery`                     | `pickup`, `shipping`, or `any` (default).                                                                                                                                                                                                                                                                                     |
| `buyItNowOnly`                 | Only listings with the "Direct Kopen" option.                                                                                                                                                                                                                                                                                 |
| `priceMinEur` / `priceMaxEur`  | Only listings within this price range, in EUR.                                                                                                                                                                                                                                                                                |
| `sortBy`                       | `date` (newest first, default), `price_asc`, `price_desc`, or `relevance`.                                                                                                                                                                                                                                                    |
| `postedAfter` / `postedBefore` | Date window (`YYYY-MM-DD`). Keeps only listings posted inside it, using the normalized `postedDate` field. Listings whose date cannot be read are excluded while a window is set. The site serves only its newest results per query, so a window further back may need a narrower query. Not applied to `listingIds` lookups. |
| `maxItems`                     | Stop after this many listings, across all searches of the run. Default 1,000. With several searches, each gets an equal share of what is left.                                                                                                                                                                                |
| `onlyNewListings`              | Return only listings this search has not delivered before. The first run returns the usual results and remembers them; skipped listings are not charged.                                                                                                                                                                      |
| `resetState`                   | With `onlyNewListings`: forget what was delivered and return the full results once.                                                                                                                                                                                                                                           |
| `scrapeDetails`                | `true` replaces the description snippet with the seller's full listing text and the single search photo with the full gallery, and adds `postedTimestamp`, a second-precise posting time, all read from each listing's own page (default off). One extra request per listing.                                                 |

A run needs at least a search query, a category or a search URL: the marketplace has no "everything" feed, and the actor tells you so up front instead of returning an empty dataset.

**Condition, delivery and buy-it-now filters work inside a category.** Marktplaats ignores them on a search across all categories. When you set them without a `categoryId`, the actor runs your search category by category with the filters applied, and skips categories that have no such filter (tickets have no condition), so the results are right. If you choose a category yourself and it has no such filter, the run stops before delivering anything and tells you which filter; it does not hand you listings that only look filtered; it is a little slower, and the listings come grouped by category. Add a `categoryId` to avoid both.

#### Pasting a search URL

Run your search on marktplaats.nl, pick every filter you want, and copy the whole address, e.g. `https://www.marktplaats.nl/l/fietsen-en-brommers/fietsen-dames-damesfietsen/#q:gazelle|f:31|PriceCentsTo:50000|postcode:3511AA|distanceMeters:10000`. Filters live after the `#`, so copy that part too. The page number does not matter, every run starts from the first page. Without a sort order in the URL the run goes newest first. A radius in the URL needs its postcode there as well: the site otherwise takes the postcode from your browser, which the actor cannot see, and the status says so. A listing page (`/v/...`) or an address from another site is refused before the run starts.

#### Finding a category ID

Every result includes its `categoryId`. The quickest way to discover IDs is to run a broad search once and read the category IDs off the results you care about, then use them as input for scoped crawls.

#### A note on Dutch search matching

Marktplaats matches compound words, so a broad term returns more than you might expect: `fiets` (bike) also matches `rolstoelfiets` (wheelchair bike) and `bakfiets` (cargo bike). That is the marketplace's own search behaviour, not a quirk of this scraper. Narrow with a `categoryId` or a price band when you need a tighter set. Occasional listings marked `Vandaag` mid-list are paid "bumped" ads, also marketplace behaviour.

### Use it from your code

Run the actor and get items straight back with one HTTP call (fine for scoped runs up to ~5 minutes):

```bash
curl "https://api.apify.com/v2/acts/lowlanddata~marktplaats-scraper/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -X POST -H "Content-Type: application/json" \
  -d '{"searchQuery": "fiets", "maxItems": 100}'
```

Node.js:

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('lowlanddata/marktplaats-scraper').call({
  searchQuery: 'fiets',
  maxItems: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("lowlanddata/marktplaats-scraper").call(
    run_input={"searchQuery": "fiets", "maxItems": 100})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

For large split crawls, start the run asynchronously and fetch the dataset when the finished-run webhook fires. Schedules (e.g. a daily price snapshot), webhooks and the Make/Zapier/n8n integrations all work out of the box: this is a standard Apify actor.

### Use it in n8n

There is a dedicated community node, so Marktplaats listings can flow straight into an n8n workflow - a scheduled search into Sheets, Telegram alerts on new rows, a database sync - with the Actor's full input as a form.

In n8n: **Settings → Community nodes → Install**, enter:

```
n8n-nodes-marktplaats-scraper
```

Connect it with your Apify API token (or OAuth2 on n8n Cloud) and the Actor runs on your Apify account, billed exactly like a run started here.

### Use it with AI agents (MCP)

Claude, Cursor and other MCP-capable agents can run this scraper as a tool through Apify's hosted MCP server: the agent fills in the search itself, starts the run and reads the results, no glue code.

Claude Code:

```bash
claude mcp add apify --transport http "https://mcp.apify.com?actors=lowlanddata/marktplaats-scraper"
```

Cursor or Claude Desktop (add a custom connector / MCP server with this URL):

```text
https://mcp.apify.com?actors=lowlanddata/marktplaats-scraper
```

**Want the whole family at once?** Point one connector at every tool in the EU Marketplaces bundle:

```text
https://mcp.apify.com?actors=lowlanddata/kleinanzeigen-scraper,lowlanddata/marktplaats-scraper,lowlanddata/tweedehands-scraper,lowlanddata/olx-pl-scraper,lowlanddata/olx-ro-scraper,lowlanddata/olx-pt-scraper,lowlanddata/willhaben-scraper,lowlanddata/finn-no-scraper,lowlanddata/tori-fi-scraper,lowlanddata/blocket-se-scraper,lowlanddata/dba-dk-scraper,lowlanddata/gumtree-uk-scraper,lowlanddata/vinted-scraper,lowlanddata/bol-com-scraper,lowlanddata/subito-scraper,lowlanddata/bazos-cz-scraper,lowlanddata/bazos-sk-scraper,lowlanddata/olx-bg-scraper,lowlanddata/olx-ua-scraper,lowlanddata/willhaben-car-scraper,lowlanddata/kleinanzeigen-free-finds,lowlanddata/olx-listing-watch,lowlanddata/marktplaats-new-listing-alerts,lowlanddata/tweedehands-new-listing-alerts,lowlanddata/kleinanzeigen-deal-radar
```

Sign in with your Apify account when prompted. Runs are billed to it. Setup details per client: [Apify MCP docs](https://docs.apify.com/platform/integrations/mcp), or start from this actor's own MCP page: [apify.com/lowlanddata/marktplaats-scraper/api/mcp](https://apify.com/lowlanddata/marktplaats-scraper/api/mcp).

Prompts that work once connected:

- "Find the 20 newest racefiets listings under €800 on Marktplaats and put them in a table by price."
- "Snapshot iPhone 15 listings and tell me the median asking price by storage size."
- "Check Marktplaats for camper vans posted today and flag anything at least 20% under the median."

### Beyond the 5,000-result window

The marketplace API returns at most 5,000 results per query. This scraper automatically splits large queries by category and price band and deduplicates across the shards, so broad crawls are not silently truncated at 5,000 items, a common limitation of similar scrapers. Marktplaats is a large market (a bare `fiets` search returns well over 200,000 listings), so this matters more here than on smaller sites.

### Is it legal to scrape Marktplaats?

Public listing data (prices, descriptions, categories, locations) is public commercial information, and this scraper is built so that the hard part of the question never arises: **no personal data enters your dataset in the first place.** The Dutch data-protection authority (Autoriteit Persoonsgegevens) is strict on personal data; an output that carries none is the point of this actor, not an afterthought.

Seller-identifying fields are removed structurally before anything reaches your dataset: the mapper whitelists fields in, it does not filter fields out, so new marketplace fields can never leak through. The only seller signal kept is business vs private. Requests are rate-limited to keep load on the marketplace negligible, and no anti-bot protections are bypassed.

One honest limit: titles and descriptions are the seller's own words, delivered as-is. If a seller chooses to type contact details into their listing text, that text is not rewritten: the guarantee covers the marketplace's data fields, not the content sellers publish about themselves.

This actor is an independent tool. It is not affiliated with, endorsed by or sponsored by Marktplaats or its owners, and trademarks belong to their respective holders.

### Troubleshooting

The actor fails fast with the reason in the run's status message:

- **"Provide a search query, a category, search URLs, specific listingIds, or a combination."**: the input was empty; fill in `searchQuery`, `categoryId` or `startUrls`.
- **"... is a single listing, not a search" / "... is not a search results page"**: a URL in `startUrls` points at a listing or another page. Open the search results on marktplaats.nl and copy that address, or put a listing's m-number in `listingIds`.
- **"... is not a category on this marketplace"**: the category in a pasted URL no longer exists; run the search on the site again and copy the fresh address.
- **"priceMinEur must not be higher than priceMaxEur."**: inverted price range.
- **Unknown category errors**: the marketplace rejected the `categoryId`; re-check it against a recent result.
- **Fewer items than requested on a free plan**: the 5-item free sample cap; run on a paid Apify plan for full results.

### Is there a Marktplaats API alternative?

Marktplaats publishes no public API for listings. This actor is the practical alternative: the same listings as structured JSON through one HTTP call (`run-sync-get-dataset-items`), on a schedule, or as an MCP tool for AI agents, with the GDPR question already answered in the data itself.

### Does Marktplaats block scrapers?

The marketplace serves search results openly, and this actor stays inside that welcome: paced requests, standard datacenter proxies, load kept negligible. No CAPTCHA fights, no bot-wall cat-and-mouse, which is also why runs are fast and reliable enough for daily schedules.

### How do I monitor Marktplaats prices?

Paste your searches into `startUrls` (or list them in `searchQueries`), switch on `onlyNewListings`, and add a daily (or hourly) Schedule in the Apify Console with an email/Slack integration on the runs. The first run returns the current listings; every run after that returns only the listings that appeared since, with the price already parsed. Listings you already received are skipped and not charged, and so are listings posted two days or more before your previous run (one day's grace against the clock, so a listing posted just before local midnight is never mistaken for old news), so a watch never back-fills older listings it simply had not reached. A newest-first search stops reading as soon as it reaches known listings from a previous day - not from today, since today is not over and a listing you already have can still resurface near the top (edited, bumped, or just reshuffled by the site). Paid "dagtopper" listings that sit on top of every search do not stop it early either: they are skipped when you have them and delivered when you do not. Keep the default newest-first order for this: sorted by price there is no "since", and older listings you never received count as new. The AI-assistant prompt above walks a non-technical user through exactly this setup.

### FAQ

**Can I get seller names or phone numbers?** No, by design. That is the product: data you can store, share and process without a GDPR review. The output tells you only whether the seller is a business or a private person.

**How is this different from other Marktplaats scrapers?** Structural GDPR safety (seller identity is unreachable, not just switched off), honest handling of the 5,000-result window, and rich category attributes (car specs, property size, phone storage) without a single personal field.

**How fresh is the data?** Live at run time: every run queries the marketplace directly. For continuous freshness, schedule the actor.

**Can I export to Excel or CSV?** Yes. Every dataset exports as CSV, Excel, JSON or XML from the Apify Console or API.

**What happens above 5,000 results?** The marketplace API caps any single query at 5,000; the actor automatically splits by category and price band and deduplicates, so large crawls come through complete (a bare `fiets` search is 200,000+ listings).

**Do I get full listing descriptions?** By default the row carries the ~200-character snippet the marketplace search returns. Turn on **Fetch full descriptions & photos** (`scrapeDetails`) and each row gets the seller's full listing text, read from its own page - at the cost of one extra request per listing.

**Why does `imageUrls` only have one photo?** The search results the actor reads carry one photo per listing; that is what `imageUrls` has by default. Turn on **Fetch full descriptions & photos** (`scrapeDetails`) to get every photo in the listing's own gallery instead, at the same one-extra-request cost as the full description - both come from the same page fetch.

**Is there a precise posting time, not just a date?** `postedDate` from search results is day granularity only. Turn on **Fetch full descriptions & photos** (`scrapeDetails`) and each row also gets `postedTimestamp`, a second-precise ISO-8601 timestamp read from the listing's own page - useful for sorting or backfilling by exact posting order, at the same one-extra-request cost as the full description and gallery.

**Does it work with Make, Zapier or n8n?** Yes. It is a standard Apify actor; all platform integrations, webhooks and schedules apply.

**Is there a Marktplaats API I could use instead?** No public one exists. This actor fills that gap: structured JSON per listing via a single HTTP call, a scheduled run, or an MCP tool, no API key beyond your Apify token.

**What does scraping Marktplaats cost?** $1.99 per 1,000 delivered listings, and that price is all-inclusive: platform usage and proxies are covered. On the Apify Gold plan the unit price drops to $1.99/1,000.

**Why does the free plan stop at 5 items?** That is Apify's free-tier sample cap for this actor, deliberately enough to judge the output shape on your own query before paying anything.

**Can I search within a distance of my postcode?** Yes: give a Dutch `postcode` like `3511AA` and a `radiusKm` up to 300. That narrows which listings come back; the rows carry the seller’s stated `city`, not a computed distance.

**Can I re-check listings I already found?** Pass their IDs in `listingIds` (the `m…` id from any listing URL or dataset item) and the actor fetches exactly those, ignoring all search filters, handy for price or availability re-checks.

**Can I filter on condition or delivery?** Both. `conditions` accepts any mix of new, as-new, used, refurbished and not-working; `delivery` narrows to pickup-only or shipping-only, and `buyItNowOnly` keeps just "Direct Kopen" listings. Marktplaats applies these filters only inside a category, so without a `categoryId` the actor searches category by category to keep them working.

**Can I paste a search URL from Marktplaats?** Yes, put the full results page address into `startUrls`, including the part after the `#` where the site keeps its filters. Category, brand and other attribute filters, price, postcode radius, posting age and sort order all apply. Paste up to 50 to run them all in one go.

**Can a scheduled run return only the new listings?** Yes, switch on `onlyNewListings`. Each search remembers what it delivered, for 90 days, in a key-value store in your own Apify account. Listings you already received are skipped and not charged. Changing a search's filters starts a fresh memory, and `resetState` clears it on purpose.

**Why does searching "fiets" also return bakfiets listings?** Marktplaats matches Dutch compound words. That is the site's own search behaviour. Add a `categoryId` or a price band when you need a tighter result set.

**Does it return car specs like mileage and year?** Yes. Car listings carry `constructionYear`, `mileage`, `fuel`, `transmission`, `body` and the full options list in `attributes`, with nothing seller-identifying among them.

**What does priceType MIN_BID mean?** The listing has a minimum bid rather than an asking price. `FIXED` is a normal asking price; the marketplace's other kinds (`FAST_BID`, `SEE_DESCRIPTION`, …) come through when they occur.

**Are the coordinates a seller's address?** No. `latitude`/`longitude` are the marketplace's own municipality-level coordinates. Location is area-level by design.

**Why do image URLs start with //?** They are protocol-relative, exactly as the marketplace serves them. Prepend `https:` before use.

**Can I use the extra photos for image analysis?** Yes - `imageUrls` with `scrapeDetails` on gives every URL in the listing's gallery, not just the one search returns, so a vision model or manual review has the full set to work from.

**Can an AI agent search Marktplaats through this?** Yes. Via Apify's hosted MCP server, agents like Claude or Cursor pick the input, launch the run and read the dataset themselves.

**Is scraping Marktplaats legal?** Prices, descriptions and categories are public commercial information, and the mapper whitelists fields in rather than filtering out: seller identity is structurally unreachable, so no personal data ever lands in your dataset.

**Does the same scraper exist for Belgium?** Yes. The 2dehands & 2ememain scraper covers Belgium's sister marketplaces on the same platform family, with the identical output shape.

### Related scrapers

Working the Benelux/German second-hand market? Same output discipline, same public health record:

- [2dehands & 2ememain Scraper](https://apify.com/lowlanddata/tweedehands-scraper), Belgium's largest second-hand marketplaces (same platform family as Marktplaats).
- [Kleinanzeigen.de Scraper](https://apify.com/lowlanddata/kleinanzeigen-scraper), Germany's largest classifieds site, with full descriptions and spec tables.

### Support

Found an issue or missing a field you need? The fastest route is the actor's **Issues tab** - it is monitored and reports get fixed. For anything private, email **contact@lowlanddata.com**. This actor is actively maintained.

Working well for you? A **rating on this page** takes ten seconds and helps other buyers find it: it is also the clearest signal of what we should build next.

# Actor input Schema

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

Free-text search. Provide this, a category, or both.

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

Several searches in one run, one per line, e.g. <code>gazelle</code> and <code>sparta</code>. Each one uses the filters below. A listing found by two searches is delivered once. Up to 50.

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

Set up a search on marktplaats.nl with any filters you like (category, condition, brand, price, distance...), then paste the address of the results page here, including everything after the <code>#</code>. Add as many as you watch, up to 50. <b>When URLs are given, the search fields of this form are ignored</b> and the run status says which ones were. Posted-date window, max items and only-new mode still apply. A listing page or another site is refused with the reason.

## `categoryId` (type: `integer`):

Numeric marketplace category ID to scope the crawl. Every result includes its <code>categoryId</code>, so run a broad search first to discover the IDs you need. Optional.

## `listingIds` (type: `array`):

Fetch these exact listings instead of searching, e.g. \["m2421501285"] - the id is in every listing URL and every dataset item. Useful to re-check price or availability of listings you found earlier. When set, all search filters are ignored.

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

Dutch postcode to search around, e.g. "3511AA". Combine with radiusKm to narrow the search area. The distance itself is not returned as a field.

## `radiusKm` (type: `integer`):

Only return listings within this many km of the postcode. Requires postcode.

## `conditions` (type: `array`):

Only return listings in these conditions. Empty = all conditions. Without a category ID the search runs category by category, because the marketplace applies this filter only inside a category.

## `delivery` (type: `string`):

Only listings offering pickup, or only listings that ship. "any" = no filter.

## `buyItNowOnly` (type: `boolean`):

Only listings with the "Direct Kopen" (buy it now) option.

## `priceMinEur` (type: `integer`):

Only return listings priced at or above this amount. Optional.

## `priceMaxEur` (type: `integer`):

Only return listings priced at or below this amount. Optional.

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

Order of the results. Newest first is the default and the right choice for monitoring.

## `postedAfter` (type: `string`):

Only keep listings posted on or after this date. Listings whose posting date cannot be read are excluded when a window is set. <b>Format: <code>YYYY-MM-DD</code></b> (for example <code>2026-09-08</code>) or a full ISO timestamp such as <code>2026-09-08T14:30:00Z</code>. Slash dates like <code>08/09/2026</code> are refused on purpose: they are read as September in Europe and August in the United States, and this actor will not guess which you meant.

## `postedBefore` (type: `string`):

Only keep listings posted on or before this date. <b>Format: <code>YYYY-MM-DD</code></b> (for example <code>2026-09-08</code>) or a full ISO timestamp such as <code>2026-09-08T14:30:00Z</code>. Slash dates like <code>08/09/2026</code> are refused on purpose: they are read as September in Europe and August in the United States, and this actor will not guess which you meant.

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

Stop after this many listings, across all searches of the run. With several searches, each gets an equal share of what is left.

## `onlyNewListings` (type: `boolean`):

For scheduled runs: return only listings this search has not delivered before. The first run returns the usual results and remembers them; later runs skip them, and skipped listings are not charged. The memory is kept per search (and per task) in a key-value store named <code>marktplaats-scraper-seen-listings</code> in your own account, for 90 days. Changing a search's filters starts a fresh memory.

## `resetState` (type: `boolean`):

With only-new mode on: forget what the searches of this run delivered before, return the full results once and remember again.

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

The search returns only a ~200-character description snippet and one photo per listing. Turn this on to read each listing's own page for the seller's full description and every photo in its gallery. This makes one extra request per listing, so a run is slower and costs one more page fetch each. The seller/trader block is never read; only the description text and image URLs reach the output. Leave off for a fast search-only sweep.

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

Proxy settings. Keep Apify proxy enabled.

## `reportFailures` (type: `boolean`):

When a run fails, send the developer the actor name and build, an error category, the error message (truncated) and a one-way hash of your input - so a bug can be fixed without anyone asking you to reproduce it. <b>Your input itself, your results and your identity are never sent, and successful runs send nothing at all.</b> Switch this off and the actor behaves exactly the same.

## Actor input object example

```json
{
  "searchQuery": "fiets",
  "listingIds": [],
  "conditions": [],
  "delivery": "any",
  "buyItNowOnly": false,
  "sortBy": "date",
  "maxItems": 1000,
  "onlyNewListings": false,
  "resetState": false,
  "scrapeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "reportFailures": true
}
```

# Actor output Schema

## `listings` (type: `string`):

Scraped marketplace listings, one JSON object per listing.

# 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 = {
    "searchQuery": "fiets",
    "listingIds": [],
    "conditions": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("lowlanddata/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 = {
    "searchQuery": "fiets",
    "listingIds": [],
    "conditions": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("lowlanddata/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 '{
  "searchQuery": "fiets",
  "listingIds": [],
  "conditions": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call lowlanddata/marktplaats-scraper --silent --output-dataset

```

## MCP server setup

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