bol.com Scraper — NL/BE Product Prices, Sellers & Ratings avatar

bol.com Scraper — NL/BE Product Prices, Sellers & Ratings

Pricing

Pay per event

Go to Apify Store
bol.com Scraper — NL/BE Product Prices, Sellers & Ratings

bol.com Scraper — NL/BE Product Prices, Sellers & Ratings

Search bol.com (Netherlands and Belgium) by keyword or search/category URL and get product listings: price and old price in EUR, brand, seller (bol or marketplace partner), rating, review count, delivery text and item URL. No login or API key.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Tim Zinin

Tim Zinin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

bol.com Scraper — Netherlands & Belgium Product Prices, Sellers and Ratings

Get live bol.com search and category results as clean rows: price and old price in EUR, brand, seller (bol itself or a marketplace partner), rating, review count, delivery promise and the direct product URL — for any keyword or bol.com URL, no bol.com account and no API key.

bol.com Scraper — what goes in and what comes out

bol.com Scraper — automation workflow

bol.com is the largest online retailer in the Netherlands and the second-largest in Belgium: millions of products, most of them sold either by bol itself or by one of tens of thousands of independent marketplace partners at their own price. This Actor reads the public search and category pages exactly as a shopper in the Netherlands or Belgium sees them and turns every product card into one structured row. You choose keywords or paste a search/category URL with your own filters, set how many products you need, and get a dataset you can download as JSON, CSV or Excel, pull through the Apify API, or send to Google Sheets, Make, n8n, Zapier or an AI agent over MCP.

It is built for recurring price and assortment work: run it daily on the same keywords, compare the rows, and you know who changed a price, which seller undercut bol itself and who is gaining reviews.

What you get

One row per product card on a bol.com search or category results page:

  • Product identity — productId (bol's numeric article id, stable across runs and the last path segment of the product URL), full title, brand when the card shows one, and a short features list of the spec bullets bol prints under the title (capacity, size, ISBN for books, and similar).
  • Price — price as a number in euros (currency is always EUR), read from the accessible price text on the card, not screen-scraped digits. originalPrice (bol's own "Adviesprijs" reference price) and a derived discountPercent are included whenever the card shows a struck-through reference price above the current one.
  • Seller — seller ("bol" when bol.com itself is the seller, otherwise the marketplace partner's registered business name) and a convenience soldByBol boolean.
  • Social proof — rating (0–5, bol's own average) and reviewCount, parsed from the card's accessible star-rating label.
  • Delivery — deliveryText, bol's own delivery promise exactly as shown, e.g. "Op voorraad. Voor 23:59 uur besteld, morgen in huis" (in stock, order before 23:59, delivered tomorrow) or "Nog niet verschenen - reserveer een exemplaar" for pre-orders.
  • Ad transparency — isSponsored marks paid placements ("Gesponsord"). They are excluded by default; switch them on to see which sellers are buying visibility for your keyword.
  • Image and link — imageUrl (bol's product photo CDN) and url, the direct product page.
  • Basket identity — offerUid, the specific offer bol's own "add to basket" link points to. It is null for listing types that don't expose a direct basket link on the card (for example some refurbished or reservation offers) — the product itself is still fully returned, only this one extra id is missing.
  • Position — page and position on the results page, so you can track ranking over time.
  • Context — the query/searchUrl that produced the row, the market (NL or BE) and a scrapedAt timestamp.

Every row is a real product bol.com showed for your search. Keywords bol could not serve a usable page for (after three fresh residential sessions) are reported as a free row with an error field, so you always know what happened and never pay for an explanation.

Who uses it

  • Cross-border sellers and importers checking what bol.com charges for a product in the Netherlands or Belgium before they list it, and which marketplace partners dominate a keyword.
  • Brands and distributors watching authorised and unauthorised resellers of their products on bol.com: price floors, who is undercutting the RRP, sold-through gaps.
  • E-commerce and pricing teams feeding a repricing tool or a BI dashboard with daily bol.com prices next to Amazon.nl/.be and Coolblue data.
  • Market researchers and analysts sizing a category: how many sellers, what price band, how reviews are distributed, how much of page one is sponsored, how bol's own price compares to partners'.
  • Affiliate and content publishers building "beste airfryer onder €100" style pages from fresh prices, review counts and item links.
  • AI agents that need a grounded answer about current Dutch/Belgian retail prices and can call the Actor as an MCP tool.

How to run

  1. Click Try for free (or Start) on this page. You need a free Apify account; no bol.com account, no API key.
  2. In Search keywords, enter one or more keywords, one per line — Dutch or English both work (airfryer, iphone 17, lego).
  3. Pick a Market: Netherlands (bol.com/nl/nl) or Belgium, Dutch (bol.com/be/nl). Both are the same catalogue split by storefront; prices and delivery text can differ between the two.
  4. Optional: paste one or more bol.com search or category URLs into bol.com search or category URLs to reuse filters you already set on the site (category, brand, price range). The Actor keeps every parameter of the URL and only walks its pages.
  5. Set Max products per keyword or URL and click Start. The prefilled example (airfryer, 20 products) finishes in well under a minute.
  6. Open the Output tab: the Products view shows image, title, brand, price, seller, rating and link; the Errors and empty searches view shows the free explanation rows. Download as JSON, CSV, Excel or HTML, or call the dataset through the API.

To run it on a schedule, open Schedules, pick the Actor (or a saved task with your keywords) and choose daily or hourly. To connect it to other tools, see Integration recipes.

Pricing

This Actor uses pay per event pricing. You pay only for products actually delivered to your dataset, plus a small start fee:

EventPriceWhen it is charged
Actor start$0.02 per GB of memoryOnce per run. The default memory means one start event per GB.
Product found$0.0025 per productFor every product row delivered to the dataset.

Example: with the default 2 GB memory, a run that delivers 100 products costs 2 × $0.02 + 100 × $0.0025 = $0.29; 1,000 products cost $2.54. The same price applies on every Apify plan.

What you do not pay for:

  • Rows with an error field — keywords bol.com could not serve a usable page for, and the note written when your spending limit is reached — are free.
  • Proxy traffic, browser time and retries are included in the product price. You never see a separate proxy bill.
  • Sponsored listings are only delivered (and charged) when you switch Include sponsored listings on.

Spending limit. Apify lets you set Max total charge for any run. The Actor checks the remaining budget before every product and stops cleanly when the next product would exceed it, then writes a free row saying so.

Free plan. Apify's free plan includes monthly platform credit that covers small test runs of this Actor. The prefilled example is sized so that it completes well inside that credit.

Input contract

FieldTypeDefaultWhat it does
searchQueriesarray of strings— (prefill ["airfryer"])Keywords to search. One run can hold up to 50 keywords; duplicates are removed.
marketstringNLNL for bol.com/nl/nl (Netherlands) or BE for bol.com/be/nl (Belgium, Dutch). Ignored for pasted startUrls, whose own /nl/nl/ or /be/nl/ path decides the market.
startUrlsarray of URLs[]bol.com search URLs (bol.com/nl/nl/s/?searchtext=... or .../be/nl/s/...) or category URLs (bol.com/nl/nl/l/<slug>/<id>/). Filters in the URL are kept. Other hosts and paths are rejected before any work starts.
maxItemsPerQueryinteger 1–300030 (prefill 20)Products to return per keyword or URL. The Actor stops paginating as soon as the limit is reached.
includeSponsoredbooleanfalseDeliver sponsored ("Gesponsord") placements too, flagged with isSponsored: true.

You must provide at least one keyword or one URL. An empty input fails immediately with a clear message and costs nothing beyond the start event. There is no sort input: bol.com's own sort dropdown (Relevantie/Prijs/Beoordeling/...) is a pure client-side control with no URL parameter — see Limitations.

Minimal input (the prefill):

{
"searchQueries": ["airfryer"],
"market": "NL",
"maxItemsPerQuery": 20
}

Belgium, several keywords, sponsored included:

{
"searchQueries": ["iphone 17", "airfryer", "lego technic"],
"market": "BE",
"maxItemsPerQuery": 40,
"includeSponsored": true
}

Reuse filters you set on the site (category page):

{
"startUrls": ["https://www.bol.com/nl/nl/l/fantasy-sciencefiction/2510/"],
"maxItemsPerQuery": 30
}

Output examples

All examples below are copied from real runs of this Actor on the Apify platform (September 2026). Nothing is invented or edited except whitespace and truncation of the features array where noted.

Happy output

Input {"searchQueries": ["airfryer"], "market": "NL", "maxItemsPerQuery": 10} at 2 GB memory — run finished in 18 seconds, 10 products delivered, 0 errors, $0.0005 platform usage. First two rows:

[
{
"productId": "9300000195535469",
"title": "Inventum GF501HLDB Airfryer XL - 5L - 11 programma's - 70-200 °C - Tot 5 personen - Zwart",
"brand": "Inventum",
"price": 49.99,
"originalPrice": 99.99,
"discountPercent": 50,
"currency": "EUR",
"rating": 4.5,
"reviewCount": 79,
"seller": "bol",
"soldByBol": true,
"deliveryText": "Op voorraad. Voor 23:00 uur besteld, morgen in huis",
"features": ["Inventum", "Max. bakcapaciteit 1000 g", "Geen meegeleverde accessoires inbegrepen", "Inhoud: 5 l"],
"url": "https://www.bol.com/nl/nl/p/inventum-gf501hldb-airfryer-hetelucht-friteuse-5-liter-70-tot-200-graden-1500-watt-zwart/9300000195535469/",
"imageUrl": "https://media.s-bol.com/JBk4xXM2Vjyo/6611koV/550x503.jpg",
"isSponsored": false,
"offerUid": "831a2dae-3968-4488-8d44-5ea4ecc75b2d",
"market": "NL",
"page": 1,
"position": 1,
"query": "airfryer",
"searchUrl": "https://www.bol.com/nl/nl/s/?searchtext=airfryer",
"scrapedAt": "2026-09-25T17:31:22.104Z"
},
{
"productId": "9300000233139234",
"title": "Ninja Dubbele Airfryer XXL - 9.5 L - Nieuw 2026 Model - 40 tot 240 Graden - 2470 W Vermogen - Dual Zone Technologie - DZ400EU",
"brand": "Ninja",
"price": 137.39,
"originalPrice": 229.99,
"discountPercent": 40,
"currency": "EUR",
"rating": 4.7,
"reviewCount": 798,
"seller": "bol",
"soldByBol": true,
"deliveryText": "Op voorraad. Voor 23:00 uur besteld, morgen in huis",
"features": ["Ninja", "Max. bakcapaciteit 2800 g", "Receptenboek inbegrepen", "Inhoud: 9.5 l", "Gelijktijdig klaar"],
"url": "https://www.bol.com/nl/nl/p/ninja-dubbele-airfryer-xxl-9-5-liter-twee-kookvakken-2470-watt-dual-zone-technologie-dz400eu/9300000233139234/",
"imageUrl": "https://media.s-bol.com/x3MkYzGRYp8J/nrQ1V2l/550x459.jpg",
"isSponsored": false,
"offerUid": "9d231711-22ff-4c07-8367-97e403ba5115",
"market": "NL",
"page": 1,
"position": 2,
"query": "airfryer",
"searchUrl": "https://www.bol.com/nl/nl/s/?searchtext=airfryer",
"scrapedAt": "2026-09-25T17:31:22.311Z"
}
]

A marketplace-seller row from the same run (not bol itself):

{
"productId": "9300000238030673",
"title": "Dubbele Airfryer XXL 9L - 2×4.5L - 3200W - 11 Programma's - Smart Finish & Match Cook - 60–200°C Instelbaar - PFAS-vrij - Vaatwasserbestendige Manden - COOK-IT",
"brand": null,
"price": 61.99,
"originalPrice": null,
"discountPercent": null,
"currency": "EUR",
"rating": 4.6,
"reviewCount": 93,
"seller": "Media Evolution B.V.",
"soldByBol": false,
"deliveryText": "Op voorraad. Voor 23:59 uur besteld, morgen in huis",
"url": "https://www.bol.com/nl/nl/p/dubbele-airfryer-xxl-9l-2-4-5l-3200w-11-programma-s-smart-finish-match-cook-60-200-c-instelbaar-pfas-vrij-vaatwasserbestendige-manden-cook-it/9300000238030673/",
"isSponsored": false,
"market": "NL"
}

A larger real run — the same keyword, maxItemsPerQuery: 120 — walked 6 results pages and delivered all 120 products in 76 seconds for $0.0176 in platform usage (2 residential proxy sessions, one retry).

A Belgium-market, third-party-seller example (market: "BE", keyword "iphone"), showing a refurbished offer where the card carries no direct basket link:

{
"productId": "9300000161136181",
"title": "Apple iPhone 15 Pro Max - 256GB - Blauw Titanium",
"brand": "Apple",
"price": 649,
"originalPrice": null,
"discountPercent": null,
"currency": "EUR",
"rating": 4.6,
"reviewCount": 35,
"seller": "Remarketed.nl",
"soldByBol": false,
"deliveryText": "Uiterlijk 30 september in huis",
"features": ["Apple", "2023", "Schermformaat: 6.7 in", "256 GB opslag", "Camera: 48 Megapixel", "5G", "Werkgeheugen: 6 GB", "IOS 17"],
"url": "https://www.bol.com/be/nl/p/apple-iphone-15-pro-max-256gb-blauw-titanium/9300000161136181/?offerType=refurbished",
"imageUrl": "https://media.s-bol.com/W9Y30A9M8wjo/58lkBPv/550x676.jpg",
"isSponsored": false,
"offerUid": null,
"market": "BE"
}

offerUid is null here — the refurbished-offer card links straight to the product page with an ?offerType=refurbished query rather than a direct "add to basket" URL. Every other field is unaffected.

Category-URL output

Input {"startUrls": ["https://www.bol.com/nl/nl/l/fantasy-sciencefiction/2510/"]} — bol.com's category (/l/) pages use the same product-card layout as search, so they work through startUrls without any special handling. A book row from a real run, showing the ISBN inside features (books have no brand field on bol.com; the author's name is the first feature instead):

{
"productId": "9300000369883682",
"title": "Moonfall 2 - The ballad of falling dragons",
"brand": null,
"price": 34.99,
"seller": "bol",
"soldByBol": true,
"deliveryText": "Nog niet verschenen - reserveer een exemplaar",
"features": ["Sarah A. Parker", "Nederlands", "Hardcover", "9789464409598", "24 november 2026"],
"url": "https://www.bol.com/nl/nl/p/moonfall-2-the-ballad-of-falling-dragons/9300000369883682/",
"offerUid": "f9a1d9bc-5c45-43f6-9576-db433591af7a",
"market": "NL"
}

Partial output (mid-run block)

bol.com occasionally blocks one residential session outright rather than serving a challenge (roughly 1 in 3–4 fresh sessions in cloud testing, 25.09.2026). When that happens mid-keyword, after some pages already succeeded, the Actor keeps every row it already delivered (charged) and reports the rest as a free error row — a real run:

{
"productId": null,
"title": null,
"brand": null,
"price": null,
"currency": "EUR",
"url": null,
"query": "qzxjklvwpfmnbqrstuvxyz9988776655",
"searchUrl": "https://www.bol.com/nl/nl/s/?searchtext=qzxjklvwpfmnbqrstuvxyz9988776655",
"market": "NL",
"error": "\"qzxjklvwpfmnbqrstuvxyz9988776655\" (NL): The site did not return a result page after 3 attempts (last HTTP 200, title \"Bol\").",
"scrapedAt": "2026-09-25T17:44:11.208Z"
}

The 9 rows delivered before the block are complete products like the ones above (a Samsung Neo QLED TV at €4,975 sold by a marketplace partner, then a Samsung QLED at €949 sold by bol itself at 37% off, and 7 more) — nothing about this row's error affects the rows already in your dataset or their charge.

Failure output

When bol.com does not return a usable page after three fresh residential sessions for the very first page of a keyword (no rows delivered yet), the keyword gets a free error row and — if every keyword in the run failed this way — the run still ends as Succeeded with the status message "The site blocked all N search(es)… No result was billed, only the run start." — check that message or the error field to retry automatically:

{
"productId": null,
"title": null,
"brand": null,
"price": null,
"currency": "EUR",
"url": null,
"query": "airfryer",
"searchUrl": "https://www.bol.com/nl/nl/s/?searchtext=airfryer",
"market": "NL",
"error": "\"airfryer\" (NL): The site did not return a result page after 3 attempts (last HTTP 403, title \"...\")."
}

No product is charged when this happens; only the start event applies.

Field dictionary

FieldTypeMeaningNotes
productIdstringbol's numeric article idStable across runs; also the last path segment of url. Use it as the primary key when you compare runs. null only on error rows.
titlestringProduct title exactly as bol/the seller wrote itNot translated or cleaned.
brandstringBrand link on the cardnull for products with no brand link (common for books, where the author is in features instead).
pricenumberCurrent selling price shown on the card, in eurosParsed from the card's accessible price text, not the visually-styled digits, so it survives markup changes.
originalPricenumberbol's own "Adviesprijs" reference pricenull when the card shows no reference price, or when it is not higher than price.
discountPercentintegerround(1 - price/originalPrice) × 100null whenever originalPrice is null.
currencystringAlways EURPresent on every row, including error rows, for schema stability.
ratingnumberAverage review score, 0–5null when the card shows no rating (new or low-volume listings).
reviewCountintegerNumber of reviews behind ratingnull together with rating.
sellerstringRegistered seller name"bol" for bol.com's own retail stock, otherwise the marketplace partner's business name exactly as shown.
soldByBolbooleanseller === "bol"Convenience field so you can filter without a string comparison.
deliveryTextstringbol's own delivery promise, verbatimDutch; dates/times are relative to the run time.
featuresarray of stringsShort spec bullets under the titleOrder and content follow bol's own card; for books this includes the ISBN-13. null when the card shows none.
urlstringProduct page URLIncludes any query string bol added (e.g. ?offerType=refurbished).
imageUrlstringProduct photo (bol's media CDN)Images are not downloaded by the Actor; the URL is bol's.
isSponsoredbooleanPaid placement ("Gesponsord")Only true when Include sponsored listings is on; otherwise sponsored cards are filtered out before this field would ever be true.
offerUidstringThe specific offer bol's own basket link targetsnull when the card has no direct "add to basket" link (seen on some refurbished/reservation offers) — every other field is still populated.
marketstringNL or BEThe storefront the row came from.
pageintegerResults page the product appeared on1-based.
positionintegerPosition on that page1-based, organic order after filtering sponsored rows (unless sponsored are included).
querystringKeyword that produced the rownull for URL inputs.
searchUrlstringFirst results page URL for this keyword or URL
scrapedAtstringISO timestamp when the row was writtenUTC.
errorstringPresent only on free explanation rowsNever present on paid product rows.

Evidence and boundaries

What this Actor observes and what it does not:

  • Source. Only the public bol.com search (/s/) and category (/l/) pages, the same pages any visitor in the Netherlands or Belgium sees without logging in. It does not open the product page, cart or account area, so per-variant or per-condition (new/refurbished tier) prices beyond what the card itself shows are out of scope.
  • Price is the card price at run time. bol.com runs frequent deals and partner price changes; the row is what the results card said when the Actor loaded it, not a checkout total, and does not include any personal member pricing.
  • productId is bol's article id, not an EAN. bol.com's search cards do not publish EAN/GTIN codes; only the product page does, which this Actor does not open. Where a card prints an ISBN (books), it is captured inside features as free text, not normalised into its own field.
  • Ranking depends on context. Relevance order on bol.com can vary with time, stock and personalisation. The Actor uses a Dutch residential connection and no login for both markets, which is the closest thing to a neutral shopper view, but two runs minutes apart can order page one differently.
  • No sort control. bol.com's sort dropdown is a pure client-side React component; ?sort=PRICE_ASC and every other query-string variant tested were silently ignored by the live site (cloud probe 25.09.2026: the <select> still reported "Relevantie" selected and product order was byte-identical to the unsorted page). This Actor therefore does not offer a sort input rather than shipping one that would quietly do nothing — if you need a specific order, sort the dataset yourself after the run (price, rating and reviewCount are all plain numbers).
  • bol.com rarely returns a true zero-result page. Its own search falls back to loosely related products for almost any input, including random strings — a genuinely empty results page was not observed during testing. The free error row path exists mainly for pages the Actor could not load at all (blocks, timeouts), demonstrated above, not for "no matching products".
  • Titles are not translated or cleaned. You get them exactly as the seller wrote them, in Dutch, so nothing is lost.

How the Actor reaches the page: bol.com, like most large retailers, protects its pages against automated traffic. The Actor opens each page in a real, privacy-hardened browser (Camoufox) through Dutch residential proxies, waits until bol's own product cards are present in the page, and reads them directly from the rendered DOM. Images, fonts and video are never downloaded, which keeps the run fast and the traffic small. If a page is not usable, it retries with a fresh proxy session up to three times and then reports the keyword or URL as a free error row instead of guessing.

Decision routing

What you see in the dataWhat it usually meansWhat to do next
Your product appears from many sellers within a 5–10% price bandCommodity competition; price is the main leverTrack daily and alert on the lowest price per productId group
soldByBol: false seller sells well below bol's own priceMarketplace clearance, grey import, or a pricing errorCheck the product page and seller history before matching the price
originalPrice present and discountPercent highbol or a partner is running a dealCompare against your own promo calendar before reacting
Many isSponsored rows for a keyword (with sponsored on)Competitive, ad-driven keywordBudget for bol Advertising or target long-tail keywords instead
reviewCount growing fast for a new productIdA rising product, or a seller running a review campaignAdd the item to a watch list; compare against your own listing
deliveryText says "reserveer" / pre-orderProduct not yet released or out of stock everywhereTrack separately from in-stock competitors; don't price-match a pre-order
Free error row about attemptsbol.com did not serve a usable page to three sessionsRe-run later; nothing already delivered is affected or re-charged

Commercial playbooks

1. Daily price watch for a product line. Save a task with your 10–30 product keywords and maxItemsPerQuery: 30. Schedule it daily. In your sheet, group by productId, keep the minimum price per day per seller, and alert when a marketplace partner drops below your floor.

2. Reseller and MAP monitoring for brands. Search your brand name and model numbers in both market: "NL" and market: "BE". Group rows by seller. Sellers not on your authorised list, or selling under your minimum advertised price, are your follow-up list. Store url and scrapedAt as evidence.

3. Category sizing before entering bol.com. Run one category startUrls entry per sub-category with maxItemsPerQuery: 300. Count distinct sellers, look at the price distribution and the share of listings sold by bol itself versus partners. This tells you in an afternoon whether the category is bol-dominated or partner-fragmented.

4. NL vs BE price comparison. Run the same keywords with market: "NL" and market: "BE" and join the results on productId where it matches, or on title similarity where it doesn't (bol.com's Belgian catalogue is a subset of its Dutch one). A consistent EUR gap between the two storefronts for the same article is a real cross-border arbitrage signal.

5. Content and affiliate pages. Pull products for a keyword, filter to reviewCount >= 50 and rating >= 4.5, and use the fresh price, rating and item link in your comparison page. Refresh weekly.

Integration recipes

Apify API (any language). Start a run and get items in one call:

curl -X POST "https://api.apify.com/v2/acts/zinin~bol-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchQueries": ["airfryer"], "market": "NL", "maxItemsPerQuery": 30}'

Python client.

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("zinin/bol-scraper").call(run_input={
"searchQueries": ["airfryer"],
"market": "NL",
"maxItemsPerQuery": 60,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if not item.get("error"):
print(item["price"], item["seller"], item["url"])

JavaScript client.

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('zinin/bol-scraper').call({ searchQueries: ['iphone 17'], market: 'BE', maxItemsPerQuery: 30 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => !i.error).length, 'products');

Google Sheets. Use Apify's Google Sheets integration on a saved task: every finished run appends rows to your sheet. Filter out rows where error is not empty.

Make, n8n and Zapier. Use the Apify app: trigger "Watch Actor runs" (or "Watch task runs"), then "Get dataset items", then your action — Slack message when the minimum price for a productId drops, a row in Airtable, a record in your CRM.

Webhooks. Add a webhook for ACTOR.RUN.SUCCEEDED pointing at your endpoint; it receives the run object with defaultDatasetId, and you fetch items from there.

AI agents (MCP). The Actor is available as a tool through the Apify MCP server (https://mcp.apify.com). An agent can call it with a keyword and use the returned rows to answer "what does X cost on bol.com right now". Keep maxItemsPerQuery small for interactive use.

Comparing runs over time

Most buyers use this Actor to see change, not a single snapshot. A reliable change-detection setup looks like this:

  1. Fix the input. Save a task with the exact keywords, market and maxItemsPerQuery. Changing any of them changes which products are in scope and makes day-to-day comparison noisy.
  2. Key on productId. bol.com keeps it stable for the life of a listing. Titles rarely change; positions move every day.
  3. Store scrapedAt with every row. It is the observation time. Two rows with the same productId and different price are a price change between those two timestamps.
  4. Separate "missing" from "out of stock". bol.com does not publish an explicit stock flag on search cards beyond deliveryText; a productId that disappears from the results may have dropped out of the page window you requested rather than been delisted. For important items, request more pages so the item stays in scope.
  5. Compare like with like across markets. NL and BE are separate storefronts with their own pricing; don't merge them into one price series without keeping the market column.
  6. Track seller changes, not only price. A productId whose seller changes from "bol" to a marketplace partner (or the reverse) between runs is often a stock or fulfilment change worth a closer look on its own.

A simple daily sheet: one tab per day of rows, a pivot of minimum price per productId, and a conditional format that highlights a drop of more than 5%. That is enough to catch most competitor moves on bol.com within a day.

Typical signals worth an alert: a new seller appearing in the top 10 for your brand keyword; the cheapest price for a productId falling below your floor; reviewCount jumping by more than 20 in a week for a competitor item; originalPrice appearing (a deal just started) on a product you track.

Operating guide

  • Memory. 2 GB is enough and was verified in cloud testing (10 products in 18 seconds, $0.0005 platform usage); more memory does not make pages load faster and increases the start fee.
  • Run size. One results page holds roughly 24–30 products and loads in a handful of seconds; a run with 10 keywords × 30 products typically finishes in a few minutes.
  • Scheduling. Daily is enough for most price work.
  • Stable keys. Compare runs on productId, not on title (rarely edited, but not guaranteed stable) and not on position (ranking moves).
  • Many keywords. Put up to 50 keywords into one run rather than starting 50 runs — you pay one start fee instead of fifty.
  • Timeouts. The default run timeout is 30 minutes. For very large jobs (thousands of products), raise the timeout or split keywords across runs.
  • Retries. A keyword that failed with an attempts error is safe to run again; nothing already delivered in that run was re-charged or affected.

Troubleshooting

The run finished but I got fewer products than maxItemsPerQuery. bol.com had fewer matching products for that keyword, or you set a very narrow category URL. Try a broader keyword or a parent category.

I see an error about attempts. bol.com did not serve a usable page to three separate residential sessions — occasionally seen mid-keyword after some pages already succeeded (see Partial output). This is rare and temporary; start the run again for that keyword. Rows already delivered were not affected.

The run stopped early with a spending-limit row. Your Max total charge was reached. Raise it in the run options or lower maxItemsPerQuery.

I expected a "no results" row but got unrelated products instead. That is bol.com's own search behaving as designed — see Evidence and boundaries. It almost never returns a truly empty page.

My pasted URL was rejected. Only https://www.bol.com/nl/nl/s/..., https://www.bol.com/be/nl/s/..., https://www.bol.com/nl/nl/l/... and https://www.bol.com/be/nl/l/... URLs are accepted. Product pages, brand pages and seller pages are not search or category listing pages.

The order I get doesn't match the sort order I picked on the website. There is no sort input — see Evidence and boundaries for why. Sort the returned rows yourself by price, rating or reviewCount.

FAQ / Limitations

Do I need a bol.com account or API key? No. The Actor reads public search and category pages; there is nothing to register.

Is this the official bol.com API? No. It is an independent tool that reads the public website. bol.com's own Open API / Retailer API is a separate, authenticated product aimed at sellers managing their own listings; this Actor needs no credentials and returns what any shopper's browser shows.

What this is NOT. It is not a checkout price calculator (no coupons, no member pricing, no shipping-cost line beyond the card's own delivery text), not an EAN/GTIN lookup (search cards don't publish one), not a stock-quantity feed (only the delivery-promise text), and it does not sort results — see above.

Can I get full product descriptions, all images or every offer on a listing? Not from this Actor. It reads search/category cards, which is what price and assortment work needs at scale. The url field takes you to the full product page.

How fresh is the data? It is read at run time. Schedule the Actor as often as you need fresh prices.

Can I run it from outside the Netherlands or Belgium? Yes. The Actor always uses a Dutch residential connection for both markets, so your own location does not matter.

What happens if bol.com changes its page? Rows would stop appearing and keywords would return free error rows rather than wrong data. The Actor is monitored and updated.

Found a bug or need a doorbraak for your specific use case — issues on this Actor's page.

Nederlandse samenvatting (bol.com Scraper)

Deze tool haalt bol.com zoekresultaten en categoriepagina's automatisch op, op basis van een zoekwoord of een geplakte URL. Geen bol.com account of API-sleutel nodig.

  • Wat je krijgt: producttitel, merk, prijs en adviesprijs in euro's, verkoper (bol of een marketplace-partner), beoordeling en aantal reviews, levertijdtekst, afbeelding en productlink.
  • Markten: bol.com/nl/nl (Nederland) en bol.com/be/nl (België, Nederlandstalig) — beide via het veld Market.
  • Gebruik: vul bij Search keywords een of meer zoektermen in, één per regel (bijvoorbeeld "airfryer", "iphone 17", "lego"), stel het aantal producten in en klik op Start. Je kunt ook een zoek- of categorie-URL van bol.com plakken om je eigen filters te hergebruiken.
  • Sortering: bol.com's eigen sorteermenu werkt niet via de URL — deze tool biedt daarom geen sorteeroptie aan. Sorteer de resultaten zelf achteraf op prijs, beoordeling of aantal reviews.
  • Gesponsorde resultaten: standaard uitgeschakeld; zet Include sponsored listings aan om ze mee te nemen (gemarkeerd met isSponsored).
  • Prijs: betaal per gevonden product. Zoektermen zonder bruikbare pagina (na drie pogingen) leveren een gratis foutregel op, geen kosten.
  • Toepassingen: prijsonderzoek, concurrentiemonitoring van marketplace-verkopers, assortimentsanalyse per categorie, prijsvergelijking Nederland versus België, affiliate-content met actuele prijzen.
  • Uitvoerformaat: JSON, CSV, Excel. Ook te koppelen aan Google Spreadsheets, Make, n8n, Zapier en de API.

Zoekwoordvoorbeelden: bol.com prijzen scrapen, bol.com concurrentie monitoren, bol.com productdata CSV, bol.com verkopers vergelijken, bol.com zoekresultaten ophalen.

Sources and rights

  • Data comes from publicly accessible bol.com search and category result pages. The Actor does not log in, does not bypass paywalls and does not collect personal data about shoppers; seller names are business names published by bol.com on the marketplace.
  • Product titles, prices and images belong to bol.com B.V. and the respective sellers. Use the data in line with bol.com's terms and the laws that apply to you, especially for republication. For large-scale commercial or seller-side integration, consider bol.com's official Retailer/Open API.
  • This Actor is not affiliated with, endorsed by or sponsored by bol.com B.V. "bol" and "bol.com" are trademarks of their owner and are used here only to describe the data source.
  • Report a bug or ask for a feature in the Issues tab of this Actor. Custom fields or coverage for other bol.com storefronts can be built on request.

More marketplace scrapers from the same author

ActorWhat it gives you
Mercado Libre ScraperLatin American product prices and sellers
Rakuten Ichiba ScraperJapanese product prices, shops, reviews and points
Wildberries ScraperRussian marketplace prices, brands and sellers
Shopify Store Price & Catalog Change MonitorPrice and catalogue changes on Shopify stores