# Marktplaats Market Watch - new listings and price changes (`oneforall/marktplaats-market-watch`) Actor

Monitor Marktplaats searches with private history. Watch mode returns only new listings, price drops, increases, price-type changes, and disappearances from the fully scanned window. Unchanged listings are not returned or billed. List mode exports current results.

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

## Pricing

$1.00 / 1,000 listing change returneds

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 Market Watch

Scrape Marktplaats.nl search results, and see what changed since the previous run: new listings, price drops, price increases, and listings that disappeared from the watched results.

Most Marktplaats scrapers return the same list every time you run them. This one keeps a history per search, so a daily run tells you what actually moved.

> **Unofficial.** This Actor is not affiliated with, endorsed by, or sponsored by Marktplaats B.V. "Marktplaats" is used only to describe the site this Actor reads, and remains the trademark of its owner.

### What you get

Two modes.

**Watch (default).** Returns only events worth acting on, each with a `status`:

- `new`: seen for the first time by this watch.
- `price_dropped` / `price_increased`: asking price moved. `previousPriceEur`, `priceDeltaEur` and `priceDeltaPct` are filled in.
- `price_changed`: the listing moved between a fixed price and a bid or free listing.
- `disappeared`: was in the previous run and is no longer in the fully covered watched results. This is not proof it sold.
- Unchanged listings are kept in watch history but are not returned or charged.

**List.** Returns current listings without comparison. Every returned row is billable in this mode.

### Output fields

One row per listing:

`itemId`, `url`, `title`, `description`, `priceCents`, `priceEur`, `priceType` (FIXED, MIN_BID, FREE, ...), `priceLabel`, `listingDate`, `city`, `categoryId`, `categoryPath`, `sellerName`, `sellerId`, `sellerIsVerified`, `imageUrls`, `attributes`, `traits`, `isPriority`, `isReserved`, `searchUrl`, plus the watch fields `status`, `statusNote`, `firstSeenAt`, `lastSeenAt`, `previousPriceEur`, `priceDeltaEur`, `priceDeltaPct`.

30 listings per page. One HTTP request per page. No browser, no proxy.

### What "disappeared" means here

The watch compares the pages you asked for, on both runs. `disappeared` therefore means **gone from the watched pages**, not "sold" and not "gone from Marktplaats". Two consequences worth knowing before you plan your runs:

- Keep `maxPages` the same between runs, and keep the same search URL. A narrower window does not trigger false disappearance notices: old items beyond the newly scanned window stay in history until the old window is covered again or you deliberately set `resetWatchState` to redefine the watch. That reset can report current results as new. The history is per search URL: a different URL is a different watch.
- On a very broad search (a single keyword with tens of thousands of results), a listing that gains interest can move to page 4 and read as `disappeared`, then come back as `new`. Narrow, filtered and date-sorted searches give cleaner signals. When a run does not cover the previous page window, disappearance events are suppressed rather than guessed.

Marktplaats does not publish sold status, so no scraper can tell you an item was sold. `statusNote` on each row says the same thing.

### How the history works

The watch history is stored in a named key-value store, one per search URL, inside your own Apify account. It survives between runs and is not shared with anyone. Add or remove a search at any time: each one keeps its own history. Set `resetWatchState` to `true` to start a search from scratch (it is applied only after the pages were fetched successfully, so a network failure cannot wipe your history).

If your subscription budget stops a run in the middle, the rows that were not delivered stay in the history: they come back with their original `firstSeenAt` and status on the next run instead of being reported as `new` again.

### Example input

```json
{
  "searches": [
    "https://www.marktplaats.nl/q/bmw/",
    "https://www.marktplaats.nl/l/auto-s/f/auto-merk/bmw/",
    "iphone"
  ],
  "mode": "watch",
  "maxPages": 3,
  "delaySecs": 1.5,
  "keepDescription": true
}
```

A keyword alone works too (`iphone` is expanded to a search URL, accents are kept: `café` becomes `cafe`). Pagination is handled for you, query parameters are preserved. Only marktplaats.nl URLs are accepted, and a URL fragment is refused rather than silently dropped, so you are never charged for results you did not ask for.

### Pricing

Pay per event: $0.001 per returned listing event ($1 per 1,000 events). In `watch` mode, only new listings, price changes and disappearance events are returned and billed; unchanged listings update private watch history at no event charge. In `list` mode, each returned listing is billed. Apify run spending limits can cap event charges. Platform usage and data-retention charges, if any, are separate from the event price.

The watch history lives in your Apify account's key-value store. It is not shared. A repeated watch with no changes returns an empty dataset and incurs no listing-event charges. Schedule at most one active run per search URL: overlapping runs can compare against the same prior state and duplicate events.

### Use the output

See `examples/watch-input.json` for a compact input and `examples/price-drop-report.py` for a script that reads a downloaded dataset export and prints price drops. `examples/sample-watch-output.json` is a synthetic fixture for trying the script locally. The script has no credentials or network calls.

```bash
python3 examples/price-drop-report.py examples/sample-watch-output.json
```

Example: query the dataset using the Apify API with a total event-charge ceiling:

```bash
curl -X POST "https://api.apify.com/v2/acts/oneforall~marktplaats-market-watch/run-sync-get-dataset-items?maxTotalChargeUsd=0.05" \\
  -H "Authorization: Bearer $APIFY_TOKEN" \\
  -H "Content-Type: application/json" \\
  -d '{"searches":["iphone"],"mode":"watch","maxPages":1,"keepDescription":false}'
```

Keep the token in your environment or secret manager; do not paste it into shared scripts or logs. For a dataset-only workflow, use `mode: "list"`; for recurring checks, keep the same search URL and page count so the watch window remains consistent.

### What this Actor does not do

Being explicit, so you are not surprised:

- No seller email addresses or phone numbers. Only the public seller name and ID.
- No login, no circumvention of any protection, no CAPTCHA solving, no proxy rotation.
- Marktplaats only. Not 2dehands, not Kleinanzeigen, not any other marketplace.
- No dashboard, no alerts by email. The dataset is the product; plug it into your own tooling.

### Data source and responsible use

The Actor reads public Marktplaats search pages. Marktplaats' `robots.txt` (checked on 17 October 2026) does not forbid these search paths, and no login, no private page and no CAPTCHA bypass is involved.

Reading is not the same as being allowed to redistribute. Marktplaats' terms of use (Gebruiksvoorwaarden, version of 22 July 2026) state in article 7.1 that platform content may not be copied or made public except for personal use or news coverage limited to 100 listings, and in article 7.3 that the listings database is protected by the database right: extracting a substantial part, or repeatedly and systematically extracting non-substantial parts, requires Marktplaats' written permission. Check those articles, and your own legal basis, before using this Actor on an ongoing basis or republishing its output. You remain responsible for how you use the data and for complying with Marktplaats' terms of use.

### Support

Something broken or a field missing? Open an issue on the Actor page.

When the page layout changes, the parser stops with an explicit error instead of returning empty rows, so a break is visible immediately. A search that genuinely has no results is reported as such and publishes nothing. Marktplaats rate limiting (HTTP 429) is retried twice, then reported with the advice to lower `maxPages` or raise `delaySecs`.

# Actor input Schema

## `searches` (type: `array`):

One or more Marktplaats searches. Paste a full search URL from your browser, or just type a keyword. Examples: 'https://www.marktplaats.nl/q/bmw/' or 'iphone'.

## `mode` (type: `string`):

watch returns only billable change events (new, price_dropped, price_increased, price_changed, disappeared). Unchanged listings remain in your private watch history but are not returned. list returns all current listings without comparison; every returned listing is billable.

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

30 listings per page. Each page is one HTTP request and one batch of charges. Keep the same value between runs of a watch: changing it changes the watched set and can produce 'disappeared' rows.

## `delaySecs` (type: `number`):

Keep the default unless you have a reason to change it. It avoids hammering the site.

## `keepDescription` (type: `boolean`):

Descriptions make the dataset bigger. Turn off if you only need title, price and location.

## `resetWatchState` (type: `boolean`):

Only for watch mode. Clears the stored history for the given searches, so the next run marks every listing as new. Applied after the pages were fetched successfully, so a network failure cannot wipe your history.

## Actor input object example

```json
{
  "searches": [
    "https://www.marktplaats.nl/q/bmw/"
  ],
  "mode": "watch",
  "maxPages": 3,
  "delaySecs": 1.5,
  "keepDescription": true,
  "resetWatchState": false
}
```

# Actor output Schema

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

Current listings and watch changes, including price and status fields.

# 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 = {
    "searches": [
        "https://www.marktplaats.nl/q/bmw/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("oneforall/marktplaats-market-watch").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 = { "searches": ["https://www.marktplaats.nl/q/bmw/"] }

# Run the Actor and wait for it to finish
run = client.actor("oneforall/marktplaats-market-watch").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 '{
  "searches": [
    "https://www.marktplaats.nl/q/bmw/"
  ]
}' |
apify call oneforall/marktplaats-market-watch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,oneforall/marktplaats-market-watch"
        }
    }
}
```

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/Ao2gadsvVEdTs9nmA/builds/oyPSSfBP15usulg96/openapi.json
