# Marktplaats Ads Scraper (`axiomworks/marktplaats-ads-scraper`) Actor

Scrape public classified ads from Marktplaats and get fields such as title, description, price type, city, coordinates, images, seller details and category attributes. Start searches with keywords, category IDs or Marktplaats URLs. The Actor reads JSON search results and needs no account or login.

- **URL**: https://apify.com/axiomworks/marktplaats-ads-scraper.md
- **Developed by:** [Axiom Works](https://apify.com/axiomworks) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.48 / 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 Ads Scraper

**Returns:** one row per Marktplaats ad with id, title, priceCents, priceType, city, postedLabel, sellerName and sourceUrl.
**Input:** `{"searchQueries":["fiets"],"maxItems":30}`
**Best for:** second-hand price monitoring, local deal research, reseller inventory research.

```json
{
  "id": "m2437404859",
  "title": "Veilig en comfortabel fietsstoeltje, in hoogte verstelbaar",
  "priceCents": 3000,
  "priceType": "MIN_BID",
  "city": "Berkel en Rodenrijs",
  "postedLabel": "Vandaag",
  "sellerName": "Joey",
  "sourceUrl": "https://www.marktplaats.nl/v/fietsen-en-brommers/fietsaccessoires-fietsstoeltjes/m2437404859-veilig-en-comfortabel-fietsstoeltje-in-hoogte-verstelbaar"
}
```

### What does Marktplaats Ads Scraper do?

Marktplaats Ads Scraper collects public classified ads from Marktplaats.nl, the Dutch online marketplace, and returns them as clean, structured records. You give it keywords, top-level category IDs, or Marktplaats search URLs, and it returns the matching ads together with their price, location, seller and category data. It reads the same public search results that any visitor sees in a browser, with no login and no account needed.

Typical uses are price monitoring for second-hand goods, market research on what is being sold in the Netherlands, tracking supply of a product such as a bicycle or a phone model, lead generation for resellers, and building datasets for price-estimation models. Because every record carries a stable ad ID and the ad URL, you can run the Actor on a schedule and compare snapshots over time.

This Actor is an independent tool. It is not affiliated with, endorsed by, or connected to Marktplaats or its owners.

The Actor works in a few simple steps:

1. It builds one or more searches from your keywords, category IDs and search URLs.
2. It requests the search results page by page, up to 100 ads at a time, at a modest rate of about one request per second.
3. It removes duplicates, because promoted ads can show up on several result pages.
4. It stops as soon as it has collected the number of ads set in `maxItems`.

Because the Actor reads a lightweight JSON search response instead of rendering web pages in a browser, runs are fast and use very little compute. A run of a few hundred ads normally finishes in well under a minute.

### What data can you get?

Every ad is one dataset item. Fields that Marktplaats does not provide for an ad are returned as null instead of being invented.

| Field | Type | Description |
|---|---|---|
| `id` | string | Stable Marktplaats ad ID, for example `m2448734979` |
| `sourceUrl` | string | URL of the ad page |
| `title` | string | Ad title |
| `description` | string | Ad description text as shown in the search results |
| `priceCents` | number | Asking price in euro cents, null when the ad has no fixed price |
| `priceType` | string | Price type such as FIXED, MIN_BID, SEE_DESCRIPTION or FREE |
| `city` | string | City of the ad |
| `latitude` | number | Latitude of the ad location |
| `longitude` | number | Longitude of the ad location |
| `postedLabel` | string | Relative posting label such as Vandaag (today), Gisteren (yesterday), or a date |
| `imageUrls` | array | Full https image URLs of the ad |
| `sellerId` | number | Marktplaats seller ID |
| `sellerName` | string | Seller display name |
| `sellerVerified` | boolean | Whether the seller is marked as verified |
| `categoryId` | number | Marktplaats category ID of the ad |
| `attributes` | object | Category-specific attributes such as brand, condition, storage or delivery |
| `distanceMeters` | number | Distance from your `postcode` filter in meters, null when no postcode is set |
| `isPromoted` | boolean | Whether the ad is a paid promotion |
| `priorityProduct` | string | Promotion type such as DAGTOPPER, or NONE |
| `reserved` | boolean | Whether the ad is marked reserved |
| `hasVideo` | boolean | Whether the ad has a video |
| `categoryPath` | array | Main and sub category slugs taken from the ad URL |

A few notes on the data:

- Prices are expressed in euro cents so they stay exact. Divide `priceCents` by 100 to get euros.
- Many ads are listed as "see description" or as bidding ads, so `priceCents` is empty for a share of the results. Check `priceType` to understand why.
- The posting date is only available as the relative Dutch label shown by the site, not as an ISO timestamp. It is exposed unchanged as `postedLabel`.
- `attributes` differs per category. Phones carry fields such as storage and condition, while repair services carry service types.

### How to use Marktplaats Ads Scraper

1. Open the Actor on the Apify Store and click Try for free.
2. Enter one or more keywords in the Search keywords field, for example `fiets` (bicycle). You can also add category IDs or Marktplaats search URLs.
3. Choose a sort order and set Max items to the number of ads you need.
4. Click Start. When the run finishes, open the Output tab to view, filter and download the results as JSON, CSV, Excel or HTML.

The maxItems limit is shared across all targets, rather than applied per keyword or category. Search URLs can be strings or objects with a url field; only their /q/ keyword or query and l1CategoryId parameters are used. Other URL filters must be supplied through the corresponding input fields. Ad-detail URLs are skipped with a message asking for a search URL or searchQueries.

Each keyword is searched separately and the results are combined, with duplicates removed. If you provide both keywords and category IDs, each keyword is searched inside each category. If you provide only category IDs, the Actor browses the newest ads in those categories.

Marktplaats serves roughly 5,000 ads per query. To collect more for a broad term such as `fiets`, split the work by category ID or use more specific keywords.

Tips for good results:
Specific keywords work best, and broad single words return many loosely related ads.
For scheduled monitoring, the default newest-first order with a modest Max items value keeps each run short.

- Use category IDs to browse a whole category without a keyword, for example 91 for cars.

### Input

| Field | Type | Description |
|---|---|---|
| `searchQueries` | array of strings | Keywords to search, for example `fiets` or `iphone 13`. Optional if category IDs or URLs are given. |
| `categoryIds` | array of integers | Top-level category IDs (l1CategoryId), for example 91 for cars or 447 for ladies bikes. |
| `startUrls` | array | Marktplaats search URLs such as `https://www.marktplaats.nl/q/fiets/`. Only the keyword is extracted. |
| `sortBy` | string | One of `newest`, `relevance`, `priceAsc`, `priceDesc`. Default `newest`. |
| `minPrice` | number | Optional minimum price in euros. Ads without a fixed price are excluded when a price filter is set. |
| `maxPrice` | number | Optional maximum price in euros. |
| `postcode` | string | Optional Dutch postcode such as `1012AB`; only ads within `distanceKm` are returned. |
| `distanceKm` | integer | Radius around `postcode` in kilometers. Default 10. |
| `maxItems` | integer | Maximum number of ads to return in total. Default 50. |
| `proxyConfiguration` | object | Apify Proxy settings. Off by default; the Actor switches to Apify Proxy automatically if the site blocks requests. |

Example input:

```json
{
  "searchQueries": ["fiets"],
  "categoryIds": [],
  "sortBy": "newest",
  "maxItems": 30,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

If you provide no keywords, no category IDs and no usable URLs, the run fails with a clear error message. If a search term matches nothing, the run succeeds with zero items and sets a status message suggesting broader keywords or fewer filters. A failed or blocked search is skipped so later targets can continue. If every request fails, the final status recommends retrying or enabling proxyConfiguration, with details in the log. The Actor never pushes placeholder records.

### Output

The Actor writes one item per ad to the default dataset. Example item:

```json
{
  "id": "m2448734979",
  "sourceUrl": "https://www.marktplaats.nl/v/telecommunicatie/mobiele-telefoons-apple-iphone/m2448734979-iphone-13-pro-256-gb",
  "title": "Iphone 13 Pro 256 GB",
  "description": "Iphone 13 pro 256 gb charger included amsterdam",
  "priceCents": 30000,
  "priceType": "MIN_BID",
  "city": "Amsterdam",
  "latitude": 52.37614007429,
  "longitude": 4.9608891362716,
  "postedLabel": "Vandaag",
  "imageUrls": [
    "https://images.marktplaats.com/api/v1/hz-mp-pro-listing/images/de826dd7-9076-4632-a512-1c2917115239?rule=ecg_mp_eps$_82.jpg"
  ],
  "sellerId": 60917532,
  "sellerName": "Esma",
  "sellerVerified": false,
  "categoryId": 1953,
  "attributes": {
    "delivery": "Ophalen",
    "condition": "Zo goed als nieuw",
    "storage": "256 GB",
    "type": "IPhone 13 Pro"
  }
}
```

The default dataset view shows the title, price, price type, city, posting label, seller name, category ID, description, URL and images in a table. You can download the dataset in JSON, CSV, Excel, XML or HTML format from the Output tab or through the API.

### How much does it cost?

The Actor is charged per result: you pay for each ad that is returned in the dataset, and nothing for ads that are filtered out as duplicates. Current pricing is shown on the Pricing tab of the Actor page, which is always the up-to-date source. Because the Actor uses a lightweight JSON endpoint, platform compute usage per run is small. Setting `maxItems` caps both the size of a run and what it can cost.

### Use with AI agents (MCP)

You can call this Actor from any MCP-compatible AI client through the Apify MCP server. Add it with this configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=axiomworks/marktplaats-ads-scraper"
    }
  }
}
```

Example prompts:

- "Find the 20 newest ads for 'iphone 13' on Marktplaats and list title, price and city."
- "Scrape 50 ads for 'fiets' sorted by lowest price and tell me how many have a fixed price."
- "Get ads from category 91 and summarise the cities where they are listed."

The input fields have short descriptions with limits and examples, so an agent can fill them in without extra guidance.

### Integrations and API

You can run the Actor from code with the Apify API clients.

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("axiomworks/marktplaats-ads-scraper").call(
    run_input={"searchQueries": ["fiets"], "maxItems": 30}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["priceCents"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('axiomworks/marktplaats-ads-scraper').call({
    searchQueries: ['fiets'],
    maxItems: 30,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
process.stdout.write(`${items.length} ads\n`);
```

cURL:

```bash
curl -X POST "https://api.apify.com/v2/acts/axiomworks~marktplaats-ads-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["fiets"], "maxItems": 30}'
```

The Actor also works with Apify schedules, webhooks, and integrations such as Zapier, Make, Google Sheets and Airbyte, so you can feed fresh ads into your own tools.

### FAQ

**Do I need a Marktplaats account?**
No. The Actor reads public search results that load without logging in.

**How many ads can I get per search?**
Marktplaats limits each query to about 5,000 results. For broad terms, split by category IDs or use more specific keywords to reach more ads.

**Why is `priceCents` empty for some ads?**
Sellers can choose "see description", bidding or swap listings without a fixed price. In those cases the price is null and `priceType` explains the type.

**Why is the posting date a label such as Vandaag?**
The search results only expose a relative Dutch label. The Actor returns it unchanged as `postedLabel`.

**Do I need a proxy?**
Usually not. The proxy option is off by default and the Actor switches to Apify Proxy on its own if requests are blocked. You can also enable a proxy in the input from the start.

**Why did my run return zero items?**
Your search term probably matched no ads. The run still succeeds and logs a warning. Check the spelling, try a Dutch keyword, or remove the category filter.

**Are duplicate ads removed?**
Yes. Promoted ads can appear on several result pages, and the Actor keeps each ad ID only once per run.

**Can I get the full ad page or contact details?**
No. The Actor returns the data in the search results, including the description text shown there, and does not open individual ad pages or collect contact details.

### Is it legal to scrape Marktplaats?

The Actor only collects information that is publicly visible without logging in, and it requests data at a modest rate to avoid putting load on the site. Whether your use of the data is lawful depends on your jurisdiction and purpose. Ads can contain personal data such as seller names, which falls under the GDPR when you process it in the EU. Make sure you have a legitimate reason to process it, keep only what you need, and respect the site's terms and applicable copyright and database rights. This is general information, not legal advice. If in doubt, consult a lawyer.

### Feedback

If you find a bug, a missing field, or have an idea for an improvement, open an issue from the Issues tab on the Actor page. Include the input you used and the run ID so the problem can be reproduced quickly. Feedback is read and used to keep the Actor working as Marktplaats changes.

# Actor input Schema

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

Search keywords as strings, e.g. \["fiets", "iphone 13"]. Supply searchQueries, categoryIds or startUrls; none is filled automatically in API calls. With categoryIds, each keyword is searched in every supplied category. Results share maxItems.

## `categoryIds` (type: `array`):

Positive integer Marktplaats category IDs (l1CategoryId), e.g. \[91] for cars. Default \[]. Without keywords, browse these categories; with searchQueries, restrict each keyword to each category. All searches share maxItems.

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

Marktplaats search URLs as strings or {"url":"https://www.marktplaats.nl/q/fiets/"} objects. Default \[]. Accepts /q/<keyword>/ or URLs with query and l1CategoryId parameters. Only the keyword and l1CategoryId are extracted; other URL filters are ignored. Adds searches to the other targets.

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

Choose newest (default, recent ads first), relevance, priceAsc (lowest prices first) or priceDesc (highest prices first). Applies to every search, e.g. "priceAsc".

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

Minimum asking price in euros, e.g. 100, zero or more; omitted by default. Excludes ads without a fixed price when a price filter is set. Applies to all searches.

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

Maximum asking price in euros, e.g. 500, zero or more; omitted by default. Use with minPrice to set a range. Applies to all searches.

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

Dutch postcode string, e.g. "1012AB"; omitted by default. Spaces are removed. Searches within distanceKm and supplies distanceMeters when the source provides it.

## `distanceKm` (type: `integer`):

Positive integer radius in kilometers around postcode, e.g. 10 (default). Ignored without postcode.

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

Positive integer maximum of unique ads across all keywords, categories and URLs, e.g. 30; default 50. Duplicates do not use the limit. Fewer ads are returned if searches end or the run cost limit is reached. Marktplaats caps depth per query.

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

Apify Proxy settings, e.g. {"useApifyProxy":false} (default). Direct requests are used first; on Apify, blocked responses trigger automatic proxy fallback. Enable {"useApifyProxy":true} to use it from the start. Blocked targets are retried then skipped; an empty run says what to change.

## Actor input object example

```json
{
  "searchQueries": [
    "fiets"
  ],
  "categoryIds": [],
  "startUrls": [],
  "sortBy": "newest",
  "distanceKm": 10,
  "maxItems": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Collected Marktplaats ads.

# 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": [
        "fiets"
    ],
    "maxItems": 30,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axiomworks/marktplaats-ads-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": ["fiets"],
    "maxItems": 30,
    "proxyConfiguration": { "useApifyProxy": False },
}

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

```

## MCP server setup

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