Piotr VassevPiotr Vassev

How to Scrape Bol.com Listings and Compare Product Variants

A Bol.com search for Harry Potter can mix translations, individual volumes, audiobooks, and boxed sets. A price comparison becomes useful only after those editions are separated.

The Bol.com Listings Scraper collects listing cards or richer product-detail records from the Dutch and Belgian shops. I maintain this Apify Actor. Start with listings to understand the assortment, then use detail mode when you need identifiers such as EAN or ISBN to match products.

Bol.com product and variant research

Select the shop before the search

The country input is nl for the Netherlands or be for Belgium. It controls keyword-search URLs and the residential proxy country. Keep any URLs you provide consistent with that selection.

The deployed input documentation requires a residential IP and says datacenter proxies are blocked. Residential proxy use is therefore a prerequisite for this workflow, not an optional performance tweak. Check access and account usage before a larger run.

You can provide keywords in searchQueries or URLs in startUrls. Supported paths include search /s/, category /l/, brand /pb/, series /serie/, and individual products /p/. Although neither list is individually marked required in the schema, the README requires at least one source.

Inspect twenty listing cards

On Apify, open the Actor's Input, choose JSON, and paste:

{
  "searchQueries": ["harry potter boek"],
  "country": "nl",
  "maxItems": 20,
  "includeProductDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}

Click Start and open the output dataset after completion. The current schema defaults maxItems to fifty; this example explicitly uses twenty. Zero removes the limit. The cap applies across the entire run, so it does not provide twenty records for each keyword.

Listing rows include type: "listing", productId, title, price, priceCurrency, brandName, rating, reviewCount, specs, sellerHint, deliveryText, and sponsored. Keep sourceUrl and sourcePage for context.

In the Actor's historical sample, the listing titled “Harry Potter - Full Boxset” has price: 56.93, priceCurrency: "EUR", and specs that include “Engels,” “Boxset paperback,” and “9781408856772.” Those specifications make clear that it is an English paperback set. brandName in the sample is the author's name, so do not assume that column always means a manufacturing brand.

Historical Bol.com listing and detail output

Request identifiers when the card is ambiguous

Switch includeProductDetails to true for a small second run. The deployed README describes detail mode as using listings to discover product URLs and writing one rich record per product detail page. Its dataset shape is different from the listing-card shape.

A detail record has type: "product", a productGroupId, and a variants array. Each variant can include productId, gtin13, color, weightGrams, price, priceCurrency, condition, availability, and sellerName. For books, the README says ISBN-13 is mapped into gtin13.

For a fixed shortlist, copy the selected /p/ URLs from the first export into startUrls, remove the broad search query, and keep detail mode enabled. This avoids relying on a changing search to rediscover precisely the same books. Use the input form to enter those real URLs rather than constructing product IDs from titles.

maxItems counts dataset records, not nested variants. Ten detail records may contain more than ten variants. Export JSON to preserve the array; flatten it only after you have inspected the shape.

Compare editions and sellers, not just titles

For a listing-only worksheet, retain productId, url, title, specs, price, priceCurrency, sellerHint, deliveryText, sponsored, and scrapedAt. Separate individual books from sets, then separate language and binding. Leave unclear editions out of the price summary until reviewed.

For detail output, create one row per variant. Copy the parent group ID and shop onto each row, and store gtin13 as text to preserve leading zeroes. Match editions by the identifier, then confirm language, condition, and bundle contents from the source. A shared title is insufficient.

If you run Belgium separately, join the two exports by matching variant identifier and condition. Keep the shop, seller, and delivery text alongside each price. A lower price with different delivery terms or a different edition is not automatically a better offer. Preserve missing prices or identifiers as missing rather than substituting a group's lowest price for every variant.

Budget for the chosen mode

Rates as of September 6, 2026 are $0.003 per listing card or $0.010 per product-detail record. According to the deployed README, these are alternative modes, not an automatic $0.013 charge for every enriched product. Twenty listing records therefore cost $0.06 in listing events; twenty detail records cost $0.20 in detail events.

The start event is $0.00005 per GB of allocated memory, minimum one event. Review residential proxy and any separate platform usage in addition to Actor event pricing.

If a run encounters 403 or 429 responses, check the residential proxy setup and country alignment before broadening the crawl. If listing rows appear where you expected variants, check the mode and the output type. Neither a successful run nor a product-group price range guarantees every variant has a populated offer.

The Bol.com Node.js example is a starting point for repeat exports. For a wider regional assortment study, keep Amazon, Allegro, and used Kleinanzeigen listings in separate market comparisons.

Frequently asked questions

Do listing and detail events add together?

The deployed README describes mutually exclusive modes: listing mode writes listing cards; detail mode uses listings to discover URLs and writes product-detail records.

Can I compare the Dutch and Belgian shops?

Yes. Run each country shop separately using country set to nl or be, keep URLs consistent with that shop, and match equivalent variants before comparing prices.

Is maxItems a count of individual variants?

No. It limits dataset records across the run. A product-detail record can contain multiple entries in its variants array.

Piotr Vassev

Piotr Vassev

Founder of FalconScrape. Building production-grade web scraping systems and data automation pipelines for businesses worldwide.

Connect on LinkedIn