# Coolblue Price Scraper (Product Price and Stock Changes) (`steadfetch/coolblue-price-monitor`) Actor

Returns public Coolblue product prices, stock, delivery estimates and ratings as JSON by product URL or category: id, title, price, list price, stock, delivery days, rating, review count and price/stock changes versus the previous run. For NL price tracking and restock monitoring. No login.

- **URL**: https://apify.com/steadfetch/coolblue-price-monitor.md
- **Developed by:** [Steadfetch](https://apify.com/steadfetch) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 product snapshot delivereds

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

## Coolblue Price Scraper (Product Price and Stock Changes)

Monitor public **Coolblue** product prices, stock, delivery estimates and
ratings by product URL or public category page. Get one clean, schema-validated
snapshot per product.

### What it does

- Reads public Coolblue product pages (Schema.org `Product` JSON-LD) and public
  category pages with HTTP + Cheerio. No browser, no login, no paywall and no
  CAPTCHA bypass.
- Accepts up to 500 explicit `productUrls` and an optional `categoryUrl`. A
  category page is followed through its public `?page=N` pagination up to 500
  products in total.
- Returns product id, title, current price, list price when published, stock
  status, delivery estimate in days, rating (normalised to a 5-point scale) and
  review count.
- **Monitor mode:** each run compares every product with the snapshot stored by
  the previous run for the same product and adds a `price_change` object with
  the previous price, the price delta and whether stock status changed. The
  first run for a product returns `price_change: null`; nothing is invented.
- **Only product data:** review text and reviewer identities are never parsed
  or emitted (RGPD).
- Pushes only items that pass the dataset output schema. 404s, errors and
  retries never produce billable results.
- Respects Coolblue's published robots.txt rules: it reads public category and
  `/product/<id>/` pages only. Review pages (`/product-reviews/`,
  `/productreviews/`), account, cart and checkout pages are not requested.
- Uses low concurrency and 120 requests per minute.

Check the source terms before using the Actor commercially. If Coolblue starts
requiring a login or serving a CAPTCHA, stop the run and escalate; this Actor
does not bypass either.

### Legal and compliance

- **Source:** public `coolblue.nl` product and category pages. Re-checked on
  2026-10-19 against `https://www.coolblue.nl/robots.txt` and
  `https://www.coolblue.nl/en/c/terms-and-conditions.html`: product and category
  pages are not disallowed (robots.txt only restricts review, account, cart and
  similar paths) and the Terms and Conditions contain no anti-scraping clause.
  Prices are public and visible without an account.
- **No bypass:** no login, paywall or CAPTCHA bypass. If Coolblue starts
  blocking public requests, the run fails visibly; the fix is to stop and
  escalate, never to evade the block.
- **RGPD:** product data only. Review text and reviewer identities are never
  emitted, and the disallowed review pages (`/product-reviews/`,
  `/productreviews/`) are not requested.
- Only aggregate rating value and review count are included.

### Input

```json
{
  "productUrls": [
    "https://www.coolblue.nl/en/product/986764/smeg-klf03nbeu-navy-blue.html"
  ],
  "categoryUrl": "https://www.coolblue.nl/en/electric-kettles"
}
```

`productUrls` is required (max 500). Pass an empty array together with
`categoryUrl` when you only want to monitor a category. `categoryUrl` is
optional and its discovered products count towards the same 500-product cap.

### Example output

```json
{
  "product_id": "986764",
  "title": "SMEG KLF03NBEU Navy Blue",
  "price_eur": 179.0,
  "list_price_eur": null,
  "in_stock": true,
  "delivery_days": 1,
  "rating_average": 4.65,
  "review_count": 191,
  "product_url": "https://www.coolblue.nl/en/product/986764/smeg-klf03nbeu-navy-blue.html",
  "captured_at": "2026-10-20T16:00:00.000Z",
  "price_change": {
    "previous_price_eur": 199.0,
    "price_eur_delta": -20.0,
    "previous_in_stock": true,
    "in_stock_changed": false,
    "previous_captured_at": "2026-10-19T16:00:00.000Z"
  },
  "url": "https://www.coolblue.nl/en/product/986764/smeg-klf03nbeu-navy-blue.html",
  "scrapedAt": "2026-10-20T16:00:00.000Z"
}
```

`url` is the common Steadfetch envelope and mirrors `product_url`;
`scrapedAt` mirrors `captured_at`. `rating_average` is normalised to a 5-point
scale because Coolblue publishes ratings out of 10. `list_price_eur` is `null`
when the page does not publish a comparison price. `delivery_days` is `null`
when no public delivery estimate is shown.

### Monitor mode

The Actor persists one small snapshot per product (`price_eur`, `in_stock` and
`captured_at`) in a named key-value store. On every following run it compares
the fresh item with that snapshot:

- `price_change.price_eur_delta` is the current price minus the previous price.
  A negative value is a price drop; `0` means the price did not change.
- `price_change.in_stock_changed` is `true` only when stock status flipped.
- `previous_price_eur`, `previous_in_stock` and `previous_captured_at` show what
  the comparison was based on, so a change can always be audited.
- On the first run for a product, `price_change` is `null` (no previous data).
- Monitoring only a subset of products in a later run keeps the snapshots of the
  products that were not requested.

Snapshots live in the Actor's named key-value store by user, so they persist
between runs (including `apify run --purge`); no account, login or external
database is involved. The comparison is included in the per-result price.

### Pricing

- **3.00 USD per 1,000 results (products)** (`apify-default-dataset-item` =
  0.003 USD per product), plus the Apify-recommended `apify-actor-start` event
  (0.00005 USD per run, first 5 seconds of compute included).
- Measured **cost per 1,000**: below **0.10 USD**. Local fixture run (real
  saved product pages, no platform credit spent): 2 products in about 1.2 s of
  request time, ~1.4 MB of HTML per page. At the 256 MB minimum memory that
  extrapolates to roughly 0.002 USD of compute per 1,000 products
  (0.25 GB x ~122 s / 3600 x 0.20 USD per GB-hour); the rest of the budget is
  headroom for transfer and variance. HTTP/HTML only, no browser, no proxy.
  Margin is above 90%.
- 404s, errors, retries and rejected items are not charged.

### FAQ

**Does it need a Coolblue account or API key?**
No. It reads public product and category pages anonymously.

**Does it include reviews or reviewer names?**
No. It only emits the aggregate rating value and review count. Review text and
identities are deliberately excluded, and review pages are not requested.

**Why normalise ratings to 5 points?**
Coolblue shows ratings out of 10. Downstream price-monitoring tools usually
expect 5 points; the Actor divides by the published `bestRating` and rounds to
two decimals. The raw scale is not invented.

**How do I see what changed since the last run?**
Check `price_change` on each result: it carries the previous price and stock
status plus the price delta. The first run for a product has
`price_change: null`.

**How is list price obtained?**
From the product page's public embedded product data when Coolblue publishes a
comparison price. If there is no list price, the field is `null`.

**What are the usage limits?**
The Actor uses low concurrency and 120 requests per minute, reads public
category and product pages only, and stops at 500 products per run.

### Maintenance

This Actor is **tested daily**: fixture-based tests and a minimal public run
keep the parser and the output schema honest. If Coolblue changes its page
layout or starts blocking public requests, the run fails visibly instead of
delivering bad data.

### Local fixture run

The committed HTML fixtures under `test/fixtures/` are real public pages. To run
the Actor locally with `apify run` and spend no platform credit:

```bash
node test/fixtures/serve.mjs &                 # serves the fixtures on 127.0.0.1:8799
STEADFETCH_FIXTURE_ORIGIN=http://127.0.0.1:8799 \
  npx -y apify-cli run --purge --input-file test/fixtures/input.json
```

Use `test/fixtures/input-category.json` to exercise category pagination.
`STEADFETCH_FIXTURE_ORIGIN` only rewrites the fetch host; emitted URLs remain
canonical Coolblue URLs.

# Actor input Schema

## `productUrls` (type: `array`):

Public Coolblue product URLs (max 500). Use an empty array together with categoryUrl when you only want to monitor a category.

## `categoryUrl` (type: `string`):

Optional public Coolblue category URL. Its public product links and ?page=N pages are followed up to 500 products.

## Actor input object example

```json
{
  "productUrls": [
    "https://www.coolblue.nl/en/product/986764/smeg-klf03nbeu-navy-blue.html"
  ],
  "categoryUrl": "https://www.coolblue.nl/en/electric-kettles"
}
```

# Actor output Schema

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

Dataset items validated against the item schema in dataset_schema.json (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 = {
    "productUrls": [
        "https://www.coolblue.nl/en/product/986764/smeg-klf03nbeu-navy-blue.html"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadfetch/coolblue-price-monitor").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 = { "productUrls": ["https://www.coolblue.nl/en/product/986764/smeg-klf03nbeu-navy-blue.html"] }

# Run the Actor and wait for it to finish
run = client.actor("steadfetch/coolblue-price-monitor").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 '{
  "productUrls": [
    "https://www.coolblue.nl/en/product/986764/smeg-klf03nbeu-navy-blue.html"
  ]
}' |
apify call steadfetch/coolblue-price-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadfetch/coolblue-price-monitor"
        }
    }
}
```

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/XyptPtH08JInQ5Qaw/builds/IoWvVS2VLzrT3Ijvv/openapi.json
