# Bol.com Scraper \[$1.5/1k💰] | Prices & EANs | NL/BE (`ahmed_jasarevic/bol-scraper`) Actor

Scrape bol.com products and prices for price monitoring, market research and competitor analysis in the Netherlands and Belgium. Get titles, prices, ratings, sellers, EANs, specs and availability - with automatic Akamai anti-bot handling and residential proxy support.

- **URL**: https://apify.com/ahmed\_jasarevic/bol-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.47 / 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

## Bol.com Scraper — Products, Prices & EANs (NL/BE)

Extract bol.com product data from the Netherlands and Belgium for price monitoring, market research, and building structured product databases. Get titles, prices, ratings, sellers, EAN-13 barcodes, specifications, and availability from search, category, brand, and product pages — through a fast plain-HTTP engine that handles bol.com's Akamai anti-bot protection automatically.

### Use Cases

- **Price monitoring & price tracking** — track bol.com prices over time, spot price drops and discounts, and compare prices across sellers in NL and BE
- **EAN barcode & product database building** — collect EAN-13 codes, brands, and specifications to build or enrich product catalogs
- **Competitor analysis** — monitor competitor prices, ratings, review counts, and assortment for webshops and bol.com sellers
- **Market research & trend analysis** — analyze price levels, bestsellers, and rating patterns by category
- **Seller & availability analysis** — see which sellers carry a product, at what price, and whether it's in stock
- **Dataset export & AI pipelines** — export structured product data (JSON/CSV via Apify API) into analytics dashboards, price databases, or AI training data

### What is this bol.com scraper?

Bol.com is the largest online marketplace in the Netherlands and Belgium (Ahold Delhaize group), with millions of products sold both by bol.com itself and by third-party marketplace sellers. There is no public bol.com product data API for general consumers — the official Retailer API and Marketing/Catalog API require a seller or affiliate partner account with OAuth2 credentials. This actor gives you structured product data from publicly visible bol.com pages without any bol.com account or API key.

### How It Works — Fast HTTP Extraction with Automatic Akamai Handling

1. **Session bootstrap** — the actor opens exactly one slim headless Chromium page on your first listing URL through the configured residential proxy, solving bol.com's Akamai "Just a moment…" JavaScript challenge once and harvesting valid session cookies pinned to that IP.
2. **Fast plain-HTTP path** — everything else runs as plain HTTP over the same pinned-IP session: search/category/brand listings are parsed with Cheerio, and product details are parsed from the page's JSON-LD ProductGroup data.
3. **Automatic session recovery** — if bol.com revokes the session (HTTP 403/429), the actor refreshes the cookies in the same browser context on the same IP and the crawl resumes without failing the run.
4. **Optional internal GraphQL** — the actor can call bol.com's internal persisted-query API (e.g. TopProductsComponent) for enrichment and to stay compatible with future client-side rendered listings.

The result is an order-of-magnitude faster and cheaper crawl than rendering every page in a browser, while staying reliable against bol.com's anti-bot protection.

### Scrape Bol.com Products Without the Official API

Point the actor at bol.com in three ways — or combine them:

- **Search queries** — pass keywords like `"iPhone 15"` or `"LEGO"`; each keyword becomes a bol.com search URL.
- **Start URLs** — paste bol.com search (`/s/`), category (`/l/`), brand (`/pb/`) or product (`/p/`) listing URLs directly.
- **Default fallback** — if neither is provided, the actor searches `"laptop"` on bol.com so a run always produces data.

Both storefronts are supported: the Dutch shop (`bol.com/nl`) and the Belgian shop (`bol.com/be`). The Belgian shop is the default because it has weaker anti-bot protection than the Dutch shop.

### Input

All parameters come from the actor's actual input schema — nothing here is invented.

| Field | Type | Required | Default | Notes |
|-------|------|----------|---------|-------|
| `searchQueries` | string\[] | No | `[]` | Keywords to search on bol.com. Each query becomes one search URL. |
| `startUrls` | object\[] | No | prefilled laptop search | Direct bol.com URLs: search (`/s/`), category (`/l/`), brand (`/pb/`), product (`/p/`) pages. |
| `country` | string | No | `be` | Storefront: `be` (Belgium, recommended — weaker anti-bot) or `nl` (Netherlands). |
| `maxItems` | integer | No | `50` | Maximum products to scrape. `0` = unlimited (the safe cap enforced by the platform is 2000). |
| `maxPages` | integer | No | `3` | Maximum search-result pages to crawl per query (1–50). |
| `maxConcurrency` | integer | No | `5` | Product detail pages fetched in parallel over plain HTTP (1–50). Higher is faster but more throttle risk. |
| `fetchProductDetails` | boolean | No | `true` | Fetch each product page (plain HTTP) for full data: EAN, specs, images, seller, availability. |
| `includeSponsored` | boolean | No | `true` | Include sponsored/advertised products in the results. |
| `proxyConfiguration` | object | No | Apify RESIDENTIAL, country NL | Residential proxy with NL/BE targeting is **required** — datacenter IPs are blocked with HTTP 403. |

**Example input:**

```json
{
  "searchQueries": ["iPhone 15", "LEGO"],
  "country": "be",
  "maxItems": 100,
  "maxPages": 5,
  "fetchProductDetails": true,
  "includeSponsored": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "NL"
  }
}
```

### Output

Each dataset record contains the following fields (field names are fixed by the actor's output logic and match the dataset schema):

| Field | Type | Description |
|-------|------|-------------|
| `productId` | string | bol.com product ID (BPID) |
| `title` | string | Full product title |
| `price` | number | Current selling price in EUR |
| `originalPrice` | number | bol's recommended ("Adviesprijs") price, when present |
| `currency` | string | Always `EUR` |
| `rating` | number | Average customer rating (0–5), when present |
| `reviewCount` | number | Number of customer reviews, when present |
| `seller` | string | Marketplace seller name ("Sold by"), when present |
| `sellerRating` | number | Reserved — currently always `null` |
| `brand` | string | Brand name, when present |
| `ean` | string | EAN-13 barcode (from the product detail JSON-LD) |
| `category` | string | Breadcrumb category path (e.g. `Elektronica > Telefoons > Smartphones`) |
| `availability` | string | Schema.org availability (e.g. `InStock`) |
| `deliveryInfo` | string | Reserved — currently always `null` |
| `imageUrl` | string | Primary product image URL |
| `images` | string\[] | De-duplicated product image URLs |
| `url` | string | Canonical product URL |
| `sourceUrl` | string | URL the record was found on |
| `sourcePage` | string | `search` (listing) or `product-detail` (enriched) |
| `scrapedAt` | string | ISO-8601 timestamp of the scrape |
| `specs` | object | Structured attributes (Model, Kleur/Color, Materiaal/Material, dimensions, Gewicht/Weight) when available |
| `isSponsored` | boolean | `true` when the product was a sponsored/advertised placement |
| `isSelect` | boolean | `true` when the product carries a bol.com "Select" badge |
| `condition` | string | Schema.org OfferItemCondition (e.g. `NewCondition`) |

**Example output (real field names, illustrative values):**

```json
{
  "productId": "9300000155985953",
  "title": "Samsung Galaxy S24 Ultra 256GB Titanium Black",
  "price": 1299.0,
  "originalPrice": 1449.0,
  "currency": "EUR",
  "rating": 4.6,
  "reviewCount": 142,
  "seller": "bol",
  "sellerRating": null,
  "brand": "Samsung",
  "ean": "0887276789460",
  "category": "Elektronica > Telefoons > Smartphones",
  "availability": "InStock",
  "deliveryInfo": null,
  "imageUrl": "https://media.s-bol.com/example/550x550.jpg",
  "images": [
    "https://media.s-bol.com/example/550x550.jpg"
  ],
  "url": "https://www.bol.com/nl/nl/p/samsung-galaxy-s24-ultra-256gb-titanium-black/9300000155985953/",
  "sourceUrl": "https://www.bol.com/be/nl/s/?searchtext=iphone",
  "sourcePage": "product-detail",
  "scrapedAt": "2026-09-18T12:00:00.000Z",
  "specs": {
    "Model": "SM-S928B",
    "Kleur": "Titanium Black",
    "Gewicht": "232 g"
  },
  "isSponsored": false,
  "isSelect": true,
  "condition": "NewCondition"
}
```

### Monitor Bol.com Prices and Price History Over Time

Bol.com itself has no price-history feature — the only way to build one is to collect price snapshots on a schedule. Because this actor runs over plain HTTP (cheap per run), you can schedule it daily or weekly via Apify's scheduler and store each run in its own dataset (or keep appending to one dataset keyed by `productId` + `scrapedAt`). That gives you:

- price-drop and discount detection (`originalPrice` vs `price`)
- long-term price history charts for individual products
- deal/special monitoring across an assortment

### Build an EAN Product Database for the Dutch & Belgian Market

With `fetchProductDetails` enabled, every product record includes the EAN-13 barcode, brand, and structured specifications. This makes the actor a practical tool for:

- building or enriching a product catalog/PIM with Dutch-market products
- matching products across marketplaces by EAN (e.g. against Amazon.nl or Google Shopping offers)
- SKU-to-EAN mapping for dropshipping or reseller workflows

### Analyze Bol.com Sellers, Ratings and Availability

Every record carries the marketplace seller, average rating, review count, and stock availability. Filter `includeSponsored` to remove advertising placements, or use `isSelect` to focus on bol.com "Select" products. This data supports seller analytics, buy-box-style price comparisons, and stock/availability monitoring across the marketplace.

### Integrations & Automation

- **API** — run the actor and pull results through the Apify REST API (`POST /v2/acts/{actorId}/runs`, dataset export to JSON/CSV/Excel/XML).
- **Webhooks** — trigger downstream workflows when each run finishes.
- **Scheduler** — schedule runs daily for price monitoring, weekly for market research, or on-demand for one-off data pulls. Recurring usage also keeps the actor's data fresh.
- **Apify integrations** — push results to Google Sheets, Airtable, Slack, or other tools through the Apify integration platform.

**Recommended schedule:** daily runs (any time) for price/discount monitoring; weekly for trend and assortment research; the actor is cheap enough that even hourly is affordable for small product sets.

### Related Actors

These actors extend the same "marketplace & price data" topic cluster. All links point to real, publicly available actors on the Apify Store:

- [Amazon Product Scraper](https://apify.com/junglee/Amazon-crawler) — the largest e-commerce marketplace scraper on Apify; pair it with this actor for full Benelux + Amazon.nl price coverage
- [Google Shopping Scraper](https://apify.com/burbn/google-shopping-scraper) — bol.com products also appear in Google Shopping NL/BE; useful for cross-marketplace EAN price comparison
- [Coolblue Scraper (NL/BE/DE)](https://apify.com/ahmed_jasarevic/coolblue-nl-scraper) — same owner, same use case (price monitoring) on the other big Dutch/Belgian online retailer
- [AliExpress Scraper](https://apify.com/epctex/aliexpress-scraper) — cross-border e-commerce price and product data for import/reseller research
- [eBay Scraper](https://apify.com/automation-lab/ebay-scraper) — marketplace price and listing data for EU markets

The cluster teaches search engines that this actor belongs to the "e-commerce marketplace price monitoring / product data extraction" topic — far stronger than isolated cross-promotional linking.

### FAQ

#### Why use this actor instead of bol.com's official API?

bol.com's official APIs are the **Retailer API** (for sellers, partners, suppliers) and the **Marketing/Catalog API** (for affiliates). Both require a partner account and OAuth2 credentials, are scoped to partner use cases (selling, content management, affiliate marketing), and are not a general product-query API for researchers or e-commerce analysts. There is no public API giving read access to product data, prices, and seller offers at large scale for consumers/analysts. This actor needs no bol.com account and returns publicly visible product data directly.

#### Is it legal to scrape bol.com? What about bol.com's terms?

Bol.com is a private platform with its own Terms of Service. Web scraping of publicly available pages is generally legal in the Netherlands and Belgium, but you are responsible for complying with bol.com's Terms of Service and applicable law (including Dutch/Belgian data-protection rules and GDPR where personal data is involved). Use the data for legitimate research/business purposes, respect rate limits, and do not use it to breach bol.com's site security. The actor accesses only publicly available pages and does not bypass login, payment, or gated content.

#### How much does it cost to scrape bol.com?

Costs are Apify platform usage costs: compute time plus residential-proxy data transfer (the actor requires a residential proxy). The actor itself has a **free tier of 10 items per run** for non-paying users; `maxItems` runs up to 2000. For comparison, the actor's plain-HTTP path makes per-item cost far lower than browser-rendered scraping.

#### How do I scrape bol.com products by keyword?

Set `searchQueries` with one or more keywords (e.g. `["laptop", "koffiezetapparaat"]`), choose `country` (`be` or `nl`), set `maxItems`/`maxPages`, and run. Each keyword becomes a bol.com search URL automatically.

#### Can I look up products by EAN barcode?

The actor **extracts** EAN-13 barcodes from product detail pages when `fetchProductDetails` is enabled — it does **not** accept an EAN as search input (see the "Behaviors an agent should know" section below for why EAN enrichment is gated on the detail phase).

#### How can I build bol.com price history or get price-drop alerts?

Schedule daily runs (Apify scheduler) and store each run's dataset. Compare `price` vs `originalPrice` for discounts, and track `price` by `productId` over time. Webhooks can alert you when a tracked product drops below a threshold.

#### Does it work for both the Netherlands and Belgium?

Yes. `country: "be"` targets `bol.com/be` (default — recommended, weaker anti-bot) and `country: "nl"` targets `bol.com/nl`.

#### How do I avoid being blocked by bol.com's anti-bot protection?

The actor handles Akamai automatically: one headless session bootstrap per run, then plain HTTP with automatic cookie refresh on 403/429. What **you** must do: use a residential proxy with NL/BE targeting (default), keep `maxConcurrency` moderate, and prefer the `be` storefront. Datacenter IPs are blocked outright (HTTP 403).

#### What is a good alternative to a bol.com scraping API?

Options depend on your goal. For structured product/price data at scale, an Apify actor like this one is the common route. Bright Data, Oxylabs, and Zyte sell managed bol.com scrapers/APIs at enterprise prices ($500+/month typically), and bol.com's own affiliate Marketing Catalog API covers partner use cases only. This actor offers a pay-as-you-go alternative with no subscription and no partner account.

### SEO Keywords

bol.com scraper, bol.com product scraper, scrape bol.com, bol.com api alternative, bol.com product data, bol.com data extraction, bol.com scraping api, bol.com ean, bol.com ean lookup, bol.com price monitoring, bol.com price tracker, bol.com prijsvergelijking, bol.com prijzen scrapen, bol.com producten exporteren, bol.com dataset, bol.com market research, bol.com competitor analysis, bol.com seller data, bol.com category scraping, benelux e-commerce data, bol.com prices api, bol.com product database, bol.com verkopers analyseren, bol.com aanbiedingen volgen

### For AI Agents & LLM Apps

This actor is callable via the Apify MCP server (model context protocol) and the Apify REST API, so agents can invoke it directly, pass a valid input, and read structured dataset records.

**Purpose:** returns structured bol.com (NL/BE) product records — prices, ratings, sellers, EAN-13, specs, availability — from search, category, brand, or product URLs.

**Minimal working input:**

```json
{
  "searchQueries": ["laptop"],
  "country": "be",
  "maxItems": 10
}
```

**Variant — scrape specific product URLs (skips the listing phase):**

```json
{
  "startUrls": [{ "url": "https://www.bol.com/be/nl/p/example-product/9300000000000/" }],
  "country": "be",
  "maxItems": 10,
  "fetchProductDetails": true
}
```

**Output fields (dataset record):** `productId`, `title`, `price`, `originalPrice`, `currency`, `rating`, `reviewCount`, `seller`, `sellerRating`, `brand`, `ean`, `category`, `availability`, `deliveryInfo`, `imageUrl`, `images`, `url`, `sourceUrl`, `sourcePage`, `scrapedAt`, `specs`, `isSponsored`, `isSelect`, `condition`.

**Behaviors an agent should know (from the actor's actual source):**

- **Duplicate records:** with `fetchProductDetails: true` (the default), each product is pushed **twice** — once with listing data (`sourcePage: "search"`) and once enriched (`sourcePage: "product-detail"`). Deduplicate by `productId` and prefer the `product-detail` record.
- **Detail phase gating:** if `maxItems` budget is fully consumed during the listing phase, the detail phase is skipped entirely — to receive EAN/specs, set `maxItems` above the expected number of listing results.
- **Budget counts pushed records,** not unique products (both phases consume it).
- **Default behavior:** if neither `searchQueries` nor `startUrls` is provided, the actor searches `"laptop"` on bol.com.
- **Free-tier cap:** non-paying accounts are capped at **10 pushed items per run**.
- **Proxy requirement:** residential proxy with NL/BE targeting is required; datacenter IPs get HTTP 403. The schema default (`useApifyProxy: true`, group `RESIDENTIAL`, country `NL`) is correct — don't override it without reason.
- **Preferred storefront:** `country: "be"` has weaker anti-bot protection than `"nl"` and is the default; only switch to `"nl"` if you specifically need Dutch-shop data.
- **Billing:** Apify platform usage pricing (compute time + residential proxy data). The actor code has no per-item fee; non-paying runs are capped at 10 items.

### Legal & Compliance Disclaimer

This actor is an independent tool and is **not affiliated with, endorsed by, or sponsored by bol.com** or Ahold Delhaize. It accesses only publicly available bol.com product, search, category, and brand pages — it does not log in, does not bypass paywalls or gated content, and does not circumvent site security. The actor handles bol.com's JavaScript bot-detection challenge exactly as a normal public visitor's browser would.

Users are responsible for complying with bol.com's Terms of Service and all applicable laws, including Dutch/Belgian data-protection regulations and the GDPR where personal data (e.g. seller contact details found on pages) is involved. If you export seller names, business data, or any personal data, ensure your use is lawful (legitimate interest, consent, or another valid legal basis) and do not use extracted data for unsolicited commercial outreach. This text is not legal advice.

# Actor input Schema

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

Keywords to search for on bol.com. Each query becomes a search URL.

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

Direct bol.com URLs to scrape. Supported: search (/s/), category (/l/), brand (/pb/), product detail (/p/) pages.

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

Which bol.com shop to use. Belgian (be) is recommended as it has weaker anti-bot protection than NL.

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

Maximum number of products to scrape. 0 = unlimited.

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

Maximum number of search result pages to scrape per query.

## `maxConcurrency` (type: `integer`):

How many product detail pages to fetch in parallel (plain HTTP, so this is safe). Higher = faster but more throttle risk.

## `fetchProductDetails` (type: `boolean`):

Fetch each product page (plain HTTP) for full data (EAN, specs, images, seller info). Slower but richer data.

## `includeSponsored` (type: `boolean`):

Include sponsored/advertisement products in results.

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

Proxy settings. Residential proxy with NL/BE country targeting is REQUIRED for bol.com - datacenter IPs are blocked (403). Use the RESIDENTIAL group.

## Actor input object example

```json
{
  "searchQueries": [],
  "startUrls": [
    {
      "url": "https://www.bol.com/be/nl/s/?searchtext=laptop"
    }
  ],
  "country": "be",
  "maxItems": 50,
  "maxPages": 3,
  "maxConcurrency": 5,
  "fetchProductDetails": true,
  "includeSponsored": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "NL"
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "startUrls": [
        {
            "url": "https://www.bol.com/be/nl/s/?searchtext=laptop"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/bol-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 = { "startUrls": [{ "url": "https://www.bol.com/be/nl/s/?searchtext=laptop" }] }

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/bol-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 '{
  "startUrls": [
    {
      "url": "https://www.bol.com/be/nl/s/?searchtext=laptop"
    }
  ]
}' |
apify call ahmed_jasarevic/bol-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/bol-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/JkhSCXfhiiX9zMlMP/builds/5ybJVIHu213vIygJF/openapi.json
