# Marktplaats Classified Listings Scraper (`automation-lab/marktplaats-netherlands-classifieds`) Actor

Search public Marktplaats classified listings by keyword or top-level category. Export ad IDs, titles, asking prices, cities, sellers, URLs and observation timestamps for Dutch secondhand inventory comparisons.

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

## Pricing

from $0.91 / 1,000 item extracteds

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Marktplaats Classified Listings Scraper

Extract public **Marktplaats classified listings** for recurring Dutch secondhand inventory comparisons. Search by keyword, top-level category, or both, and export ad identity, title, asking price, city, seller and listing URL with an observation timestamp.

This Actor returns current search-card observations. It does not buy items, contact sellers, predict sold prices, or provide historical listings.

### Who is it for?

- Resale analysts comparing bicycle, furniture or electronics asking prices.
- Secondhand retailers reviewing the current local assortment.
- Researchers creating timestamped samples of Dutch classified inventory.
- Spreadsheet users who need structured rows rather than manual copying.

### Why use this Actor?

Stable listing IDs let you join repeated snapshots. Price type is preserved so bidding and description-based cards are not mistaken for free items. Deduplication applies across pages, and the maximum listing count applies to the entire run.

The Actor uses public structured search data and does not download images or open every ad detail page. This keeps the workflow focused on inventory comparisons, not unnecessary enrichment.

### Getting started

1. Enter a Dutch search term such as `fiets`.
2. Optionally enter top-level category ID `445` for Fietsen en Brommers.
3. Set `maxItems` and `maxPages` to your desired sample size and request cap.
4. Run the Actor.
5. Open the Listings dataset and export JSON, CSV, Excel or XML using Apify's export controls.

```json
{"query":"fiets","maxItems":20,"maxPages":10}
```

### Input parameters

| Field | Default | Meaning |
| --- | --- | --- |
| `query` | None | Keyword passed to Marktplaats; surrounding whitespace is trimmed. |
| `categoryId` | None | Positive top-level category ID; `445` selects Fietsen en Brommers. |
| `maxItems` | 20 | Global maximum unique usable listings, from 1 to 5,000. |
| `maxPages` | 10 | Sequential search-page cap, from 1 to 167. Each page contains up to 30 source cards. |

Provide `query`, `categoryId`, or both. Empty input is rejected. A subcategory ID is not a supported category input. Unsupported fields are rejected rather than silently ignored.

### Search and category behavior

Marktplaats controls relevance and token matching. A keyword is not an exact phrase or exact whole-title filter; advertised cards may appear. The Actor does not apply a second title filter or silently narrow the source's result set.

When both keyword and category are supplied, both are sent in the same search request. The Actor verifies that the source accepted the requested top-level category; an invalid or ignored category fails instead of producing unfiltered inventory.

Category-only example:

```json
{"categoryId":445,"maxItems":10,"maxPages":2}
```

### Extracted data

| Field | Meaning |
| --- | --- |
| `listingId`, `listingUrl` | Source identity and canonical ad URL. |
| `title` | Search-card title. |
| `price`, `priceType`, `currency` | Numeric asking amount when exposed, original price type, EUR. |
| `city` | Source city, or null. |
| `sellerId`, `sellerName` | Public seller identity/display name when exposed. |
| `categoryId` | Leaf category of the individual listing, not necessarily the requested parent category. |
| `description` | Search-card description; not guaranteed to be the complete ad body. |
| `dateLabel` | Original relative label, such as Vandaag; not an inferred publication date. |
| `reserved` | Source reservation signal, when exposed. |
| `imageUrls` | Public image links; image files are not downloaded. |
| `query`, `sourceUrl`, `pageNumber` | Requested keyword and source-page provenance. |
| `observedAt` | UTC observation time for this row. |

Missing optional values are null. Zero-price sentinels on nonnumeric price cards are represented as null, while genuine free/fixed zero prices remain zero.

### Output example

The following illustrates the shape returned by the tested bicycle search, with seller and item identity anonymized:

```json
{
  "listingId":"m1234567890",
  "listingUrl":"https://www.marktplaats.nl/v/fietsen-en-brommers/fietsen-dames-damesfietsen/m1234567890-example",
  "title":"Example city bicycle",
  "price":75,
  "priceType":"FIXED",
  "currency":"EUR",
  "city":"Utrecht",
  "sellerId":"12345678",
  "sellerName":"Example seller",
  "categoryId":447,
  "description":"Example bicycle in good condition.",
  "dateLabel":"Vandaag",
  "reserved":false,
  "imageUrls":[],
  "query":"fiets",
  "sourceUrl":"https://www.marktplaats.nl/lrp/api/search?query=fiets&limit=30&offset=0",
  "pageNumber":1,
  "observedAt":"2026-10-01T14:00:00.000Z"
}
```

The default dataset holds listing rows. The key-value store's `SUMMARY` record contains the saved count and requested scope.

### Pagination and limits

Pages are requested sequentially until the global listing limit, page cap, source ceiling, billing budget or natural exhaustion is reached. Listing IDs are deduplicated. Cards without a usable source listing URL are excluded.

Neither the 5,000-item input ceiling nor the 167-page ceiling promises exhaustive marketplace coverage. Live inventory changes between requests, promoted rows can repeat, and the source imposes its own limits. Use comparable inputs and run times when comparing snapshots.

### How much does it cost to extract Marktplaats listings?

A one-time `start` event is charged after input validation. One `item` event is charged for each unique usable listing saved. Empty valid searches have only the start event; invalid input is rejected before charging.

Each run costs **$0.005 to start**, plus a per-listing price based on your qualifying aggregate monthly Apify Store spend tier (not this Actor's private volume):

| Tier | Price per listing |
| --- | --- |
| FREE | $0.0017434 |
| BRONZE | $0.001516 |
| SILVER | $0.0011825 |
| GOLD | $0.0009096 |
| PLATINUM | $0.0009096 |
| DIAMOND | $0.0009096 |

At BRONZE, estimated totals are $0.006516 for 1 listing, $0.02016 for 10, $0.0808 for 50, and $0.1566 for 100. At FREE, 50 listings cost an estimated $0.09217 including the start fee. Actual billing follows the active Pricing tab, saved output and your account tier. No separate event is charged for seller fields, image links or the summary.

### Integrations

Export to Google Sheets or Excel for price comparisons. Send dataset rows to a warehouse through Apify webhooks or your ingestion job. Join snapshots by `listingId` to compare price, reservation and availability observations.

Schedule this Actor using Apify schedules if you want repeated snapshots. Change detection, alerts and retained historical comparisons require your own downstream workflow; they are not built into this Actor.

### API usage

Use your Apify token as a secret, never as public spreadsheet content.

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/automation-lab~marktplaats-netherlands-classifieds/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"query":"fiets","maxItems":10,"maxPages":2}'
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/marktplaats-netherlands-classifieds')
  .call({ query: 'fiets', maxItems: 10, maxPages: 2 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/marktplaats-netherlands-classifieds').call(
    run_input={'query': 'fiets', 'maxItems': 10, 'maxPages': 2})
print(client.dataset(run['defaultDatasetId']).list_items().items)
```

### MCP usage

Add the Actor-specific Apify hosted MCP endpoint:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/marktplaats-netherlands-classifieds"
```

For Claude Desktop, Cursor, and VS Code clients that support HTTP MCP:

```json
{"mcpServers":{"marktplaats":{"url":"https://mcp.apify.com?tools=automation-lab/marktplaats-netherlands-classifieds"}}}
```

Authorize with your Apify account as required by your client. Example prompt: “Find 50 fiets listings on Marktplaats and summarize asking prices by city, keeping bidding cards separate.” Hosted tool availability depends on Actor visibility and account access.

### Reliability and troubleshooting

Transient network failures, 429s and server errors get at most two retries with bounded backoff. Permanent upstream failures, malformed responses and ignored categories fail the run rather than reporting false empty success. Some already saved rows may remain after a later-page failure.

No proxy, login cookies or browser fallback is enabled. If the public route becomes protected, the run fails visibly. Do not assume a failed run means the query has no inventory.

### Legality and responsible use

Use public listing data for legitimate research and comparisons. Respect applicable laws, Marktplaats terms, personal-data obligations and reasonable request volumes. Public seller names may be personal data; minimize retention and avoid unsolicited outreach. This Actor is independent and is not endorsed by Marktplaats.

### Data handling and support

No AI model, paid data API, proxy service or off-platform payment is used at runtime. The company pays Apify infrastructure usage within the documented Actor pricing. Marktplaats receives the search keyword/category and request metadata. Apify stores the input, dataset, summary and operational logs in your account under your storage settings and applicable Apify terms; this Actor adds no separate retention database or background cache.

Public seller names and identifiers are included in dataset rows but are not deliberately logged or published to a global named dataset. Delete unwanted datasets, key-value stores and runs through your Apify account; the Actor does not automatically delete saved results. Do not put secrets or sensitive personal data into search keywords.

Report problems through the Actor's Apify Issues tab with the run link and input, without tokens. User-reported issues receive a substantive response within 14 calendar days.

### FAQ

#### Does it show sold prices or transaction history?

No. Prices are asking prices from current public search cards. Missing ads in a later snapshot are not proof of a sale.

#### Why are some prices null?

The source may show bidding, swapping or “see description” instead of a numeric asking price. Check `priceType`; do not coerce null to zero.

#### Why did I get fewer rows than maxItems?

The source may have exhausted results, reached its page limit, or repeated cards. Your page or billing cap may also stop the run. maxItems is an upper bound, not a guaranteed count.

#### Can I supply listing URLs or subcategory IDs?

No. This version supports keyword and top-level category searches only. It does not fetch ad detail pages.

### Related Actors

- [FINN.no Multi-Category Listings](https://apify.com/automation-lab/finn-no-multi-category-listings) for Norwegian marketplace comparisons.
- [DBA Denmark Marketplace Listings](https://apify.com/automation-lab/dba-denmark-marketplace-listings-scraper) for Danish secondhand inventory.

These cover different countries and sources; they do not expand this Actor's Dutch scope.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/marktplaats-netherlands-classifieds/changelog.md

# Actor input Schema

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

Optional when categoryId is supplied. Whitespace is trimmed. Marktplaats applies its own relevance/token matching, not exact phrase or whole-field equality; advertised listings can appear. No client-side title filter is applied.

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

Optional positive Marktplaats top-level category ID, for example 445 (Fietsen en Brommers). Combined with query when both are supplied. Subcategory IDs are not supported; an unaccepted category fails rather than returning unfiltered results.

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

Run-wide maximum number of unique usable listing rows, after deduplication. Default 20; zero/unlimited is not supported. Stops earlier if results or maxPages are exhausted or the billing budget is reached.

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

Maximum number of sequential source pages (30 upstream cards each). Default 10, range 1–167. This is a request/coverage cap, not an output count; duplicates and cards without a listing URL are excluded. Also stops at maxItems and the source page ceiling.

## Actor input object example

```json
{
  "query": "fiets",
  "maxItems": 20,
  "maxPages": 10
}
```

# Actor output Schema

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

Structured classified listing rows.

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

Run counts and requested scope.

# 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",
    "maxItems": 20,
    "maxPages": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/marktplaats-netherlands-classifieds").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",
    "maxItems": 20,
    "maxPages": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/marktplaats-netherlands-classifieds").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",
  "maxItems": 20,
  "maxPages": 10
}' |
apify call automation-lab/marktplaats-netherlands-classifieds --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/marktplaats-netherlands-classifieds"
        }
    }
}
```

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/FgNL9RQvmcJ8xdzzK/builds/cMhp6JB2oywkL5rjD/openapi.json
