# Bol.com Scraper - Prices, Price History & AI Analysis (`harvestlab/bol-com-scraper`) Actor

The complete Bol.com intelligence toolkit: track competitor prices, monitor price drops, analyze market trends with AI. Supports Netherlands & Belgium storefronts with cross-run price history and real-time market analysis.

- **URL**: https://apify.com/harvestlab/bol-com-scraper.md
- **Developed by:** [Nick](https://apify.com/harvestlab) (community)
- **Categories:** E-commerce, AI
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Bol.com Scraper - Prices, Price History & AI Analysis

Scrape product listings from **Bol.com**, the largest e-commerce platform in the Netherlands and Belgium. Extract prices, list prices, ratings, review counts, seller information, specifications, images, and availability data. Optionally **track price tiers across runs** (standard / Select / refurbished deltas with price-drop alerts) via a named key-value store, and generate **AI-powered price and market analysis** for competitive intelligence and e-commerce research.

### How is this different from the other Bol.com scrapers on Apify Store?

Every other Bol.com scraper in the Apify Store ([studio-amba](https://apify.com/studio-amba/bol-scraper), [muhammetakkurtt](https://apify.com/muhammetakkurtt/bol-com-scraper), [ecomscrape](https://apify.com/ecomscrape/bol-product-search-scraper), [zyra](https://apify.com/zyra/bol-product-scraper)) returns raw product JSON and stops there. **This actor is the only Bol.com scraper that ships an integrated LLM analyst layer** — plug in any of OpenRouter (Gemini / Claude / GPT via one key), Anthropic, Google AI, OpenAI, or self-hosted Ollama, and the actor generates a full market brief on top of the scraped catalog: price distribution, brand landscape, discount-pattern detection, seller concentration, rating outliers, and buying recommendations — in one run, no second tool needed.

Other differentiators:

- **Price-tier intelligence** — only Bol.com scraper on the Store that emits the full price matrix per product. The `alternative_prices` array exposes the **Bol.com Select member price** (€14.99/year subscription, similar to Amazon Prime) and the **refurbished / tweedehands** buy-box price alongside the standard price, each with server-computed savings percentage. Competitor muhammetakkurtt exposes these as separate `select_price` / `refurbished_price` fields — we go further with an extensible array and first-class `has_select_deal` / `has_refurbished` boolean filters. Powers arbitrage, resale-margin, and membership-savings analysis at the brand level.
- **Cross-run price-tier history (watch mode)** — turn on `watchMode: true` and every product carries a `price_tier_trend` field on subsequent runs: `standard_delta_pct`, `select_delta_pct`, `refurb_delta_pct`, `standard_direction` (up/down/flat), `first_seen_at`, `history_sample_count`. Snapshots persist to a **named** Apify key-value store (`bol-com-price-history`) with a 90-entry FIFO cap per product. Seed on run 1, get deltas on every subsequent run. Pairs with the new `price-drop-detected` billable event ($0.005) which fires **only** when the standard price falls by 5 % or more AND prior history exists — clean price-watch signal with no false positives on first-seen SKUs. No other Bol.com scraper on the Apify Store ships cross-run persistence.
- **Bol.com Retailer API bypass** — pass `bolClientId` + `bolClientSecret` and the actor routes through Bol's official seller API instead of Playwright. No WAF, no 403s, no proxies needed.
- **BE-mirror auto-routing** — Bol.com/nl aggressively blocks scraping in April 2026; Bol.com/be serves the same catalog with a much weaker WAF. The actor auto-routes all requests to `be` unless you explicitly pass `country="nl"`.
- **Sponsored-listing flag** — every search-card result carries a `sponsored: true/false` boolean so you can filter out paid-placement SKUs from organic rankings.
- **Six-way sort ordering** — `sortBy` input (relevance, popularity, price ascending/descending, rating, newest) for targeted price-monitoring or trend discovery.

### What does Bol.com Scraper do?

This actor scrapes publicly available product data from Bol.com. It supports three modes of operation:

- **Search mode** - Search Bol.com by keyword and scrape product listings across multiple pages.
- **Product URL mode** - Provide specific Bol.com product URLs to scrape detailed product information including JSON-LD structured data, specifications, and full descriptions.
- **Bestsellers mode** - Scrape bestselling products from Bol.com, optionally filtered by category.

For each product, the scraper extracts: title, price, list price (before discount), discount percentage, brand, seller name, ratings, review count, availability status, delivery information, product images, categories, EAN/barcode, specifications, and description.

The optional AI analysis generates a comprehensive market report covering price distribution, brand landscape, discount patterns, rating analysis, seller comparison, and buying recommendations.

### Why scrape Bol.com?

Bol.com is the dominant e-commerce marketplace in the Benelux region, with over 13 million active customers and 52,000+ sellers. It is essentially the Amazon of the Netherlands and Belgium.

### Use Cases

- **E-commerce sellers**: Monitor competitor pricing, track your own product listings, and discover market gaps on the Bol.com marketplace.
- **Price monitoring**: Set up automated price tracking for products you sell or want to buy. Detect price drops, sales events, and seasonal pricing patterns.
- **Market research**: Understand product categories, brand positioning, and consumer preferences in the Dutch and Belgian markets.
- **Dropshippers**: Find trending products, compare supplier prices, and identify profitable product opportunities on Bol.com.
- **Brand monitoring**: Track how your brand and products appear on Bol.com, including pricing by third-party sellers and customer ratings.
- **Academic research**: Analyze e-commerce market dynamics, pricing strategies, and consumer behavior in the Benelux region.

### How much does it cost to scrape Bol.com?

| Action | Cost |
|--------|------|
| 1 product scraped | $0.005 |
| 1 price-tier detection (product with Select or refurbished offer) | $0.001 |
| 1 price-drop detection (watch mode, standard price down ≥5 % vs prior snapshot) | $0.005 |
| 25 products (default run, no AI) | $0.125 |
| 100 products | $0.50 |
| AI market analysis | $0.05 per analysis |
| 25 products + AI analysis | ~$0.175 |

The `price-tier-detected` event is billed only when a product actually ships with at least one alternative price tier in the `alternative_prices` array (Select or refurbished). Products without secondary offers are never charged the extra $0.001.

The `price-drop-detected` event is billed only when **all three** are true: (1) `watchMode=true`, (2) at least one prior snapshot exists for the product in the `bol-com-price-history` named KV store, and (3) the current standard price is at least 5 % lower than the previous snapshot's standard price. First-seen products are never charged — they seed the history instead.

Apify platform costs (compute and proxy) are charged separately. Residential proxy is recommended for reliable results. A typical run scraping 25 products takes 1-2 minutes and costs approximately $0.20 total including platform fees.

### Quick Start

1. Click **Try for free** to open the actor in Apify Console.
2. Enter a search query (e.g., "laptop", "koptelefoon", "wasmachine").
3. Choose your country store (Netherlands or Belgium).
4. Set the maximum number of products to scrape.
5. (Optional) Enable AI analysis and provide an API key.
6. Click **Start** and wait for results.

#### Example input for search mode

```json
{
    "mode": "search",
    "searchQuery": "draadloze koptelefoon",
    "maxProducts": 25,
    "enableAiAnalysis": false,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
````

Note: `country` is omitted — the actor defaults to `be` (Belgium mirror) for anti-bot reliability. Set `"country": "nl"` explicitly if you specifically need the Dutch store.

#### Example input for product URLs

```json
{
    "mode": "product_url",
    "productUrls": [
        "https://www.bol.com/nl/nl/p/samsung-galaxy-s24-ultra-256gb-titanium-black/9300000164927498/",
        "https://www.bol.com/nl/nl/p/apple-iphone-15-pro-128-gb-natural-titanium/9300000149075693/"
    ],
    "enableAiAnalysis": true,
    "llmProvider": "openrouter",
    "openrouterApiKey": "sk-or-...",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
```

#### Example input for price-history watch mode

Pair `watchMode: true` with the Apify scheduler (e.g. once every 24 hours) and every run after the first will carry `price_tier_trend` deltas on every product:

```json
{
    "mode": "search",
    "searchQuery": "apple iphone 15 pro",
    "maxProducts": 50,
    "watchMode": true,
    "fetchDetails": false,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
```

Run 1 seeds the `bol-com-price-history` named KV store with one snapshot per product. Run 2 (and every subsequent run) returns each product with a `price_tier_trend` object comparing today's prices to the previous snapshot, and bills the `price-drop-detected` event only when the standard price has fallen by 5 % or more. 90 snapshots per product are retained FIFO, so you can eyeball ~3 months of history at a daily cadence.

### Input Parameters

#### Search Settings

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `mode` | string | `search` | Scraping mode: `search`, `product_url`, or `bestsellers` |
| `searchQuery` | string | | Search keywords for Bol.com |
| `productUrls` | array | | List of Bol.com product URLs to scrape |
| `category` | string | | Bestseller category slug (e.g., "elektronica", "boeken") |
| `maxProducts` | integer | 25 | Maximum number of products (1-200) |
| `sortBy` | string | `relevance` | Sort order for search mode: `relevance`, `popularity`, `price_asc`, `price_desc`, `rating`, `newest`. Ignored in `product_url` and `bestsellers` modes. |
| `country` | string | `be` | Country store: `be` (Belgium, default — most reliable) or `nl` (Netherlands). See "Anti-bot status" below. |

#### AI Analysis

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `enableAiAnalysis` | boolean | `false` | Enable AI-powered market analysis. Set to `true` to generate an AI market report. Requires an API key. |
| `llmProvider` | string | `openrouter` | AI provider: `openrouter`, `anthropic`, `google`, `openai`, or `ollama` |
| `llmModel` | string | | Custom model name (leave empty for default) |
| `openrouterApiKey` | string | | OpenRouter API key (get one at openrouter.ai) |
| `anthropicApiKey` | string | | Anthropic API key (get one at console.anthropic.com) |

#### Price-Tier History (watch mode)

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `watchMode` | boolean | `false` | When `true`, persist each product's standard / Select / refurbished prices across runs in the `bol-com-price-history` named KV store, emit a `price_tier_trend` field on every product, and enable the `price-drop-detected` billable event. No extra HTTP calls; no extra charges on the first run. |

#### Advanced

| Parameter | Type | Description |
|-----------|------|-------------|
| `proxyConfiguration` | object | Proxy settings. Residential proxy strongly recommended. |

### Output Format

Each product is stored as a separate dataset item with the following structure:

```json
{
    "product_id": "9300000164927498",
    "ean": "8806095350516",
    "title": "Samsung Galaxy S24 Ultra 256GB - Titanium Black",
    "url": "https://www.bol.com/nl/nl/p/samsung-galaxy-s24-ultra-256gb-titanium-black/9300000164927498/",
    "brand": "Samsung",
    "price": 1349.00,
    "currency": "EUR",
    "price_currency": "EUR",
    "list_price": 1499.00,
    "discount_percentage": 10,
    "availability": "in_stock",
    "delivery_text": "Morgen in huis",
    "rating": 4.5,
    "rating_count": 234,
    "seller": "bol.com",
    "sponsored": false,
    "alternative_prices": [
        {"tier": "select", "price": 1299.00, "savings_pct": 4, "label": "Met Select"},
        {"tier": "refurbished", "price": 899.00, "savings_pct": 33, "label": "Refurbished / tweedehands"}
    ],
    "has_select_deal": true,
    "has_refurbished": true,
    "categories": ["Elektronica", "Smartphones", "Samsung smartphones"],
    "images": ["https://media.s-bol.com/..."],
    "description": "De Samsung Galaxy S24 Ultra...",
    "specifications": {
        "Schermgrootte": "6.8 inch",
        "Intern geheugen": "256 GB",
        "Besturingssysteem": "Android 14"
    },
    "country": "nl",
    "scraped_at": "2026-04-11T12:00:00+00:00",
    "price_tier_trend": {
        "has_history": true,
        "standard_delta_pct": -6.25,
        "select_delta_pct": -5.94,
        "refurb_delta_pct": null,
        "standard_direction": "down",
        "first_seen_at": "2026-04-08T12:00:00+00:00",
        "previous_snapshot_at": "2026-04-10T12:00:00+00:00",
        "previous_standard_price": 1438.90,
        "previous_select_price": 1381.00,
        "previous_refurb_price": null,
        "history_sample_count": 3
    },
    "history_slot_key": "price-tier-9300000164927498"
}
```

The `price_tier_trend` block appears **only when `watchMode: true`**. On the very first run (when no prior snapshot exists yet), it emits `{"has_history": false, "standard_direction": "new", ...}` with all delta fields `null` — the product still gets stored to seed future runs. From run 2 onward, you'll see populated deltas. When `watchMode` is omitted or `false`, the block is absent entirely (fully backward-compatible).

**Field compatibility note**: `price_currency` is deprecated — use `currency` (ISO 4217 string, e.g., `"EUR"`) for cross-actor consistency with Amazon, eBay, Walmart, Shopify, Marktplaats, Booking, and App Store scrapers. Both fields are emitted in this release for backward compatibility; `price_currency` will be removed in a future release. Migrate scripts and filters to `currency` now.

When AI analysis is enabled, a summary item is included with the `ai_analysis` field containing price analysis, brand landscape, discount insights, rating analysis, seller comparison, market positioning, and buying recommendations.

### Frequently Asked Questions

#### Does it work for Belgium?

Yes — in fact, `be` is now the **default** (Cycle 71). The Belgian mirror at bol.com/be serves the same product catalog as bol.com/nl (Bol.com's inventory is unified) with much weaker anti-bot protection, so the actor auto-routes to `country=be` by default. Product pricing, availability windows, and delivery options can differ between stores, but the underlying SKUs are overwhelmingly shared. Set `country="nl"` explicitly only if you need NL-specific pricing or inventory.

#### Do I need a proxy?

A residential proxy is strongly recommended. Bol.com actively blocks automated requests from datacenter IP addresses. The actor is pre-configured to use Apify's residential proxy group for reliable results.

#### How fast is the scraper?

The actor maintains a 2-second delay between requests to avoid rate limiting. A typical run scraping 25 products from search results takes 1-2 minutes. Scraping individual product URLs takes 3-5 seconds per product due to the polite delay.

#### Can I scrape product reviews?

The current version extracts the aggregate rating and review count for each product. Individual review text extraction is not yet supported.

#### What about product variants (sizes, colors)?

The scraper extracts data for the specific product variant shown on the page. If a product URL points to a specific variant (e.g., a particular color or size), that variant's data is returned.

#### How often can I run this?

You can schedule runs at any interval. For price monitoring, hourly or daily runs are common. The actor uses polite crawling practices with delays between requests to minimize impact on Bol.com's servers.

### Tips for Best Results

- **Always use residential proxy**: Bol.com aggressively blocks datacenter IPs. Without a residential proxy, you will likely get 403 errors or empty results. The actor is pre-configured to use Apify's RESIDENTIAL proxy group.
- **Start with a small run**: Test with 5-10 products first to verify the scraper works with your proxy configuration before running large batches.
- **Anti-bot status — BE-mirror is the default for everything (Cycle 71)**: Bol.com's anti-bot is path-specific AND exit-country-specific. As of April 2026, bol.com/nl aggressively blocks both PDP fetches AND list-mode requests, returning 403 on ~100% of runs even through residential proxy with `apifyProxyCountry=NL` (verified Cycles 69–70). The Belgian mirror bol.com/be serves the same catalog with a substantially weaker WAF. The actor auto-routes EVERY request to `country=be` unless you explicitly pass `country="nl"`. Live smoke test (Cycle 71): 5/5 "tas" products extracted in 11 seconds on first try, zero retries. Users who need NL-specific inventory (rare — most SKUs are shared) can force `country=nl` and accept the 403 risk.
- **Prefer `fetchDetails: false` for bulk discovery**: Even on the BE mirror, individual PDP fetches can occasionally 403 due to Bol's rate-limiter. Search-card extraction is the reliable path (live-tested ~64% price coverage on card-only runs — we extracted 5/5 cards with prices in the Cycle 71 smoke test) and returns title, price, rating, url, image — enough for discovery, competitive monitoring, and most price-tracking workflows. Use `fetchDetails=true` or product-URL mode only when you specifically need PDP-only fields (EAN, full description, specifications).
- **Retailer API path — the nuclear option for production**: If you're a Bol.com seller/partner, provide `bolClientId` and `bolClientSecret`. This routes through Bol's official Retailer Catalog API, completely bypassing the WAF, and is the recommended integration for production-grade workflows. Web scraping (Playwright) remains the fallback for users without API credentials.
- **Use product URL mode for detailed data**: Search results contain basic information (title, price, rating). For complete data including specifications, EAN codes, full descriptions, and all images, use product URL mode which fetches each product's detail page. See the note above about occasional PDP throttling.
- **Schedule regular runs for price monitoring**: Use Apify's scheduling feature to run the scraper daily or hourly for consistent price tracking. Export data to Google Sheets or a database for trend analysis.
- **Dutch search terms work best**: Since Bol.com is a Dutch platform, searching in Dutch (e.g., "draadloze koptelefoon" instead of "wireless headphones") typically returns more relevant results. English terms also work for many product categories.
- **Combine with AI analysis**: The AI market analysis feature provides instant insights without manual data processing. It works best with 15+ products to identify meaningful patterns.

### Related Actors

- **[Marktplaats Scraper](https://apify.com/ovular_sponge/marktplaats-scraper)** -- Compare Bol.com retail prices with second-hand prices on Marktplaats, the largest Dutch classifieds platform. Useful for price arbitrage and reseller margin analysis.
- **[Contact Extractor](https://apify.com/ovular_sponge/contact-extractor)** -- Enrich seller data from Bol.com with company contact details, tech stack, and social profiles for B2B outreach and partnership research.
- **[Shopify Scraper](https://apify.com/ovular_sponge/shopify-scraper)** -- Compare Bol.com marketplace prices with independent Shopify stores selling similar products. Useful for cross-channel pricing analysis and identifying direct-to-consumer alternatives.
- **[Trustpilot Scraper](https://apify.com/ovular_sponge/trustpilot-scraper)** -- Check Trustpilot reviews for Bol.com sellers and competing e-commerce brands. Combine product data with reputation insights for sourcing and purchasing decisions.
- **[Grocery Price Monitor](https://apify.com/ovular_sponge/grocery-price-monitor)** -- Track grocery and household product prices alongside Bol.com listings to compare pricing across supermarkets and e-commerce for consumer goods research.

### Limitations

- Search results depend on Bol.com's search algorithm and may vary based on location, proxy IP, and personalization factors. Results may differ from what you see in a regular browser session.
- Some product pages may have different HTML structures that the parser cannot handle. The actor uses multiple fallback strategies (JSON-LD structured data, Open Graph meta tags, CSS selectors) to maximize extraction success across different page layouts.
- Bol.com may change their website structure at any time, which could temporarily affect scraping accuracy. The actor is designed to degrade gracefully, returning partial data rather than failing completely.
- Maximum 200 products per run to prevent excessive resource usage and ensure reasonable run times.
- The actor does not handle CAPTCHAs or JavaScript challenges. If Bol.com serves a CAPTCHA, the request will fail and be retried with exponential backoff.
- Product availability and pricing may change between the time of scraping and when you view the results. Always verify critical pricing decisions with the live Bol.com website.

### Legal and Compliance

This actor scrapes publicly available product information from Bol.com. Users are solely responsible for ensuring their use of this tool complies with:

- **Bol.com's Terms of Service**: Review Bol.com's terms before using this actor for commercial purposes. Automated access may be restricted under their terms.
- **Applicable data protection laws**: Including the GDPR (General Data Protection Regulation), which applies in the Netherlands and Belgium. This actor does not collect personal data; it only extracts publicly displayed product information.
- **Competition law**: If using scraped data for pricing decisions, ensure compliance with applicable competition and antitrust regulations.
- **Copyright**: Product descriptions and images scraped from Bol.com may be protected by copyright. Use the data responsibly and in accordance with applicable intellectual property laws.

The authors of this actor are not responsible for any misuse of the data or violations of third-party terms of service. This tool is provided for legitimate research, analysis, and monitoring purposes only.

**Important**: Bol.com is a private platform and may restrict automated access. Always use this actor responsibly with appropriate rate limiting and proxy configuration.

# Actor input Schema

## `bolClientId` (type: `string`):

Optional: Client ID from Bol.com Retailer API. If provided with Client Secret, uses the official API instead of browser scraping (faster, more reliable).

## `bolClientSecret` (type: `string`):

Optional: Client Secret from Bol.com Retailer API. Required together with Client ID.

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

How to discover products: 'Search by Keyword' (uses the searchQuery field, e.g. 'PlayStation 5'), 'Scrape Product URLs' (pass direct /p/ URLs via productUrls), or 'Bestsellers' (scrape Bol.com's top-sellers list, optionally scoped via category slug). Pick 'search' for keyword discovery, 'product\_url' when you already have ASINs/URLs, 'bestsellers' for catalog-wide trending data.

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

Product keywords to search on Bol.com (e.g. 'PlayStation 5', 'Lego Technic', 'Philips airfryer', 'yoga mat'). Only used in 'Search by Keyword' mode — ignored for 'product\_url' and 'bestsellers' modes. Supports Dutch or English terms; Bol.com localizes matching.

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

CLI alias for searchQuery. Hidden from Console form.

## `q` (type: `string`):

CLI alias for searchQuery. Hidden from Console form.

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

List of Bol.com product URLs to scrape (e.g. https://www.bol.com/nl/nl/p/...). Used in 'Scrape Product URLs' mode.

## `category` (type: `string`):

Optional Bol.com category slug for bestsellers (e.g. 'elektronica' = electronics, 'boeken' = books, 'speelgoed' = toys, 'wonen-en-keuken' = home & kitchen, 'sport-en-outdoor'). Leave empty for overall bestsellers. Only used in 'Bestsellers' mode.

## `maxProducts` (type: `integer`):

Maximum number of products to scrape. Bol.com shows ~24 per search page. Start with 10-25 to test — PDP mode occasionally throttles; list-mode (fetchDetails=false) is more reliable.

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

How Bol.com should sort the search results. Only used in 'Search by Keyword' mode; ignored for 'product\_url' and 'bestsellers'. 'Relevance' is the Bol.com default; 'Price low-to-high' and 'Price high-to-low' are great for price monitoring; 'Rating' surfaces top-rated products first; 'Newest' prioritises recently released products.

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

CLI alias for maxProducts. Hidden from Console form.

## `fetchDetails` (type: `boolean`):

If enabled, fetches each product's detail page to enrich results with EAN, description, specifications, extra images, AND the full price-tier matrix (Bol.com Select member price + refurbished / tweedehands price when offered). Throughput is ~20 seconds per PDP due to polite delays and retries. Leave OFF to get fast, reliable card data (title, price, rating, seller, image, availability, Select/refurbished badges) from the search results page only. Recommended ON when you need alternative\_prices populated with Select / refurbished amounts — OFF for fast price monitoring and catalog discovery.

## `country` (type: `string`):

Choose the Bol.com country store to scrape. DEFAULT: 'be' (Belgium mirror). As of Cycle 71 (April 2026), bol.com/nl blocks both list-mode and PDP requests with ~100% 403s even via residential proxy, while bol.com/be serves the same unified catalog with a much weaker WAF. The actor auto-routes everything to 'be' unless you explicitly pick 'nl' here. Only choose 'nl' if you specifically need NL-exclusive pricing/inventory (rare — most SKUs are shared).

## `watchMode` (type: `boolean`):

Track prices across runs using a named key-value store (`bol-com-price-history`). Each product gets a `price_tier_trend` field comparing current standard/Select/refurbished prices to the prior snapshot — `standard_delta_pct`, `select_delta_pct`, `refurb_delta_pct`, `standard_direction` (up/down/flat), `first_seen_at`, `history_sample_count`. 90-snapshot FIFO per product. Designed for scheduled runs (daily/hourly): seed on the first run, get deltas on every subsequent run. Enables the `price-drop-detected` event ($0.005) — billed only when the standard price falls by 5% or more AND prior history exists. No extra HTTP calls, no extra charges on the first run. Leave OFF for one-shot snapshot scrapes.

## `enableAiAnalysis` (type: `boolean`):

Generate AI-powered price and market analysis from the scraped products. Requires an API key for your chosen LLM provider.

## `llmProvider` (type: `string`):

AI backend for price/market analysis. 'OpenRouter' (default) is cheapest — Gemini Flash via OpenRouter runs ~$0.001 per analysis. 'Anthropic' uses Claude direct, 'Google AI' uses Gemini direct, 'OpenAI' uses GPT-4o mini, 'Ollama' runs on your own server (no API cost). Each provider needs its own API key field below.

## `llmModel` (type: `string`):

Specific model to use. Leave empty for the provider default (google/gemini-2.0-flash-001 for OpenRouter, claude-sonnet-4-20250514 for Anthropic, gemini-2.0-flash for Google AI, gpt-4o-mini for OpenAI, llama3.1 for Ollama).

## `openrouterApiKey` (type: `string`):

Your OpenRouter API key. Get one at openrouter.ai/keys

## `anthropicApiKey` (type: `string`):

Your Anthropic API key. Get one at console.anthropic.com

## `googleApiKey` (type: `string`):

API key for Google AI (Gemini). Get one at https://aistudio.google.com/app/apikey

## `openaiApiKey` (type: `string`):

API key from platform.openai.com (required if using OpenAI provider)

## `ollamaBaseUrl` (type: `string`):

Base URL for Ollama API. Default: http://localhost:11434

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

Proxy settings. Bol.com aggressively blocks datacenter exits with 403 — RESIDENTIAL (default) is strongly recommended. Datacenter proxy users will see near-100% 403s even in list mode. You may override the default, but expect failures on non-residential groups.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "PlayStation 5",
  "maxProducts": 25,
  "sortBy": "relevance",
  "fetchDetails": false,
  "watchMode": false,
  "enableAiAnalysis": false,
  "llmProvider": "openrouter",
  "ollamaBaseUrl": "http://localhost:11434",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "mode": "search",
    "searchQuery": "PlayStation 5",
    "ollamaBaseUrl": "http://localhost:11434",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("harvestlab/bol-com-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 = {
    "mode": "search",
    "searchQuery": "PlayStation 5",
    "ollamaBaseUrl": "http://localhost:11434",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("harvestlab/bol-com-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchQuery": "PlayStation 5",
  "ollamaBaseUrl": "http://localhost:11434",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call harvestlab/bol-com-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=harvestlab/bol-com-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Bol.com Scraper - Prices, Price History & AI Analysis",
        "description": "The complete Bol.com intelligence toolkit: track competitor prices, monitor price drops, analyze market trends with AI. Supports Netherlands & Belgium storefronts with cross-run price history and real-time market analysis.",
        "version": "1.8",
        "x-build-id": "la2IX1IzHafuVoaaj"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/harvestlab~bol-com-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-harvestlab-bol-com-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/harvestlab~bol-com-scraper/runs": {
            "post": {
                "operationId": "runs-sync-harvestlab-bol-com-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/harvestlab~bol-com-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-harvestlab-bol-com-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "bolClientId": {
                        "title": "Bol.com API Client ID",
                        "type": "string",
                        "description": "Optional: Client ID from Bol.com Retailer API. If provided with Client Secret, uses the official API instead of browser scraping (faster, more reliable)."
                    },
                    "bolClientSecret": {
                        "title": "Bol.com API Client Secret",
                        "type": "string",
                        "description": "Optional: Client Secret from Bol.com Retailer API. Required together with Client ID."
                    },
                    "mode": {
                        "title": "Scraping Mode",
                        "enum": [
                            "search",
                            "product_url",
                            "bestsellers"
                        ],
                        "type": "string",
                        "description": "How to discover products: 'Search by Keyword' (uses the searchQuery field, e.g. 'PlayStation 5'), 'Scrape Product URLs' (pass direct /p/ URLs via productUrls), or 'Bestsellers' (scrape Bol.com's top-sellers list, optionally scoped via category slug). Pick 'search' for keyword discovery, 'product_url' when you already have ASINs/URLs, 'bestsellers' for catalog-wide trending data.",
                        "default": "search"
                    },
                    "searchQuery": {
                        "title": "Search Query",
                        "type": "string",
                        "description": "Product keywords to search on Bol.com (e.g. 'PlayStation 5', 'Lego Technic', 'Philips airfryer', 'yoga mat'). Only used in 'Search by Keyword' mode — ignored for 'product_url' and 'bestsellers' modes. Supports Dutch or English terms; Bol.com localizes matching.",
                        "default": "PlayStation 5"
                    },
                    "query": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for searchQuery. Hidden from Console form."
                    },
                    "q": {
                        "title": "Search Query (CLI alias)",
                        "type": "string",
                        "description": "CLI alias for searchQuery. Hidden from Console form."
                    },
                    "productUrls": {
                        "title": "Product URLs",
                        "type": "array",
                        "description": "List of Bol.com product URLs to scrape (e.g. https://www.bol.com/nl/nl/p/...). Used in 'Scrape Product URLs' mode.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "category": {
                        "title": "Bestseller Category",
                        "type": "string",
                        "description": "Optional Bol.com category slug for bestsellers (e.g. 'elektronica' = electronics, 'boeken' = books, 'speelgoed' = toys, 'wonen-en-keuken' = home & kitchen, 'sport-en-outdoor'). Leave empty for overall bestsellers. Only used in 'Bestsellers' mode."
                    },
                    "maxProducts": {
                        "title": "Max Products",
                        "minimum": 1,
                        "maximum": 200,
                        "type": "integer",
                        "description": "Maximum number of products to scrape. Bol.com shows ~24 per search page. Start with 10-25 to test — PDP mode occasionally throttles; list-mode (fetchDetails=false) is more reliable.",
                        "default": 25
                    },
                    "sortBy": {
                        "title": "Sort Order",
                        "enum": [
                            "relevance",
                            "popularity",
                            "price_asc",
                            "price_desc",
                            "rating",
                            "newest"
                        ],
                        "type": "string",
                        "description": "How Bol.com should sort the search results. Only used in 'Search by Keyword' mode; ignored for 'product_url' and 'bestsellers'. 'Relevance' is the Bol.com default; 'Price low-to-high' and 'Price high-to-low' are great for price monitoring; 'Rating' surfaces top-rated products first; 'Newest' prioritises recently released products.",
                        "default": "relevance"
                    },
                    "maxItems": {
                        "title": "Max Items (CLI alias)",
                        "minimum": 1,
                        "maximum": 200,
                        "type": "integer",
                        "description": "CLI alias for maxProducts. Hidden from Console form."
                    },
                    "fetchDetails": {
                        "title": "Fetch Product Details (slower, may fail)",
                        "type": "boolean",
                        "description": "If enabled, fetches each product's detail page to enrich results with EAN, description, specifications, extra images, AND the full price-tier matrix (Bol.com Select member price + refurbished / tweedehands price when offered). Throughput is ~20 seconds per PDP due to polite delays and retries. Leave OFF to get fast, reliable card data (title, price, rating, seller, image, availability, Select/refurbished badges) from the search results page only. Recommended ON when you need alternative_prices populated with Select / refurbished amounts — OFF for fast price monitoring and catalog discovery.",
                        "default": false
                    },
                    "country": {
                        "title": "Country",
                        "enum": [
                            "nl",
                            "be"
                        ],
                        "type": "string",
                        "description": "Choose the Bol.com country store to scrape. DEFAULT: 'be' (Belgium mirror). As of Cycle 71 (April 2026), bol.com/nl blocks both list-mode and PDP requests with ~100% 403s even via residential proxy, while bol.com/be serves the same unified catalog with a much weaker WAF. The actor auto-routes everything to 'be' unless you explicitly pick 'nl' here. Only choose 'nl' if you specifically need NL-exclusive pricing/inventory (rare — most SKUs are shared)."
                    },
                    "watchMode": {
                        "title": "Enable Price-Tier History (watch mode)",
                        "type": "boolean",
                        "description": "Track prices across runs using a named key-value store (`bol-com-price-history`). Each product gets a `price_tier_trend` field comparing current standard/Select/refurbished prices to the prior snapshot — `standard_delta_pct`, `select_delta_pct`, `refurb_delta_pct`, `standard_direction` (up/down/flat), `first_seen_at`, `history_sample_count`. 90-snapshot FIFO per product. Designed for scheduled runs (daily/hourly): seed on the first run, get deltas on every subsequent run. Enables the `price-drop-detected` event ($0.005) — billed only when the standard price falls by 5% or more AND prior history exists. No extra HTTP calls, no extra charges on the first run. Leave OFF for one-shot snapshot scrapes.",
                        "default": false
                    },
                    "enableAiAnalysis": {
                        "title": "Enable AI Analysis",
                        "type": "boolean",
                        "description": "Generate AI-powered price and market analysis from the scraped products. Requires an API key for your chosen LLM provider.",
                        "default": false
                    },
                    "llmProvider": {
                        "title": "LLM Provider",
                        "enum": [
                            "openrouter",
                            "anthropic",
                            "google",
                            "openai",
                            "ollama"
                        ],
                        "type": "string",
                        "description": "AI backend for price/market analysis. 'OpenRouter' (default) is cheapest — Gemini Flash via OpenRouter runs ~$0.001 per analysis. 'Anthropic' uses Claude direct, 'Google AI' uses Gemini direct, 'OpenAI' uses GPT-4o mini, 'Ollama' runs on your own server (no API cost). Each provider needs its own API key field below.",
                        "default": "openrouter"
                    },
                    "llmModel": {
                        "title": "LLM Model",
                        "type": "string",
                        "description": "Specific model to use. Leave empty for the provider default (google/gemini-2.0-flash-001 for OpenRouter, claude-sonnet-4-20250514 for Anthropic, gemini-2.0-flash for Google AI, gpt-4o-mini for OpenAI, llama3.1 for Ollama)."
                    },
                    "openrouterApiKey": {
                        "title": "OpenRouter API Key",
                        "type": "string",
                        "description": "Your OpenRouter API key. Get one at openrouter.ai/keys"
                    },
                    "anthropicApiKey": {
                        "title": "Anthropic API Key",
                        "type": "string",
                        "description": "Your Anthropic API key. Get one at console.anthropic.com"
                    },
                    "googleApiKey": {
                        "title": "Google AI API Key",
                        "type": "string",
                        "description": "API key for Google AI (Gemini). Get one at https://aistudio.google.com/app/apikey"
                    },
                    "openaiApiKey": {
                        "title": "OpenAI API Key",
                        "type": "string",
                        "description": "API key from platform.openai.com (required if using OpenAI provider)"
                    },
                    "ollamaBaseUrl": {
                        "title": "Ollama Base URL",
                        "type": "string",
                        "description": "Base URL for Ollama API. Default: http://localhost:11434"
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Proxy settings. Bol.com aggressively blocks datacenter exits with 403 — RESIDENTIAL (default) is strongly recommended. Datacenter proxy users will see near-100% 403s even in list mode. You may override the default, but expect failures on non-residential groups."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
