bol.com API & Scraper
The bol.com API returns the leading online retailer of the Netherlands and Belgium as clean JSON, across four actions - search, product/detail, product/offers and category - and three storefronts: Dutch (nl), Belgian Dutch (be) and Belgian French (be-fr).
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
product/offers is the cheap and interesting one: bol is a marketplace and publishes its whole competing-offer table, so one call returns every seller for a product with that seller's price, condition, offer type, shipping cost, delivery promise and bol's own 1-to-10 seller rating - and it costs about a sixth of what a product page costs upstream. Listing rows carry the product id and URL, title, brand, the current price and bol's 90-day reference price, the discount, the seller behind the buy-box, the delivery promise, the rating and the category path; a 199-product grid across 7 bol departments filled every one of those on every row. product/detail adds the EAN and MPN, the full image gallery, the spec groups, the description and the sibling products that differ only by colour or size. Prices are EUR. No bol account, no browser - one ReefAPI key and the standard envelope.
Seven bol behaviours worth knowing before you build against it
All measured on 2026-09-06 across 135 live calls, three storefronts and 199 products in 7 departments.
| Behaviour | Measured | What to do |
|---|---|---|
| NL and BE are different prices, not different languages | product 9200000032872507 was 15.00 EUR on the Dutch storefront and 13.05 EUR on both Belgian storefronts, in the same minute | treat market as a real parameter. If you are price-monitoring the Benelux, you need both |
| The competing-seller table is far cheaper than the product page | product/offers has a median of 1,224 ms against 1,550 ms for product/detail, and moves 45-48 KB against 288 KB | if what you want is who sells it and for how much, call product/offers directly rather than the product page |
| The numeric id is the key; the slug is decoration | the same product resolves under any slug, and a category URL carrying the koptelefoons slug with id 4770 serves Laptops - because 4770 IS Laptops | pass the bare id. The category action echoes bol's own name for what it resolved, so a wrong id is visible instead of silent |
| Search stops at page 10; category browse does not | search page 11 returns 0 rows and a total of 0. The Laptops category reported 114 pages | the page limit is published on the parameter, so page 11 is rejected rather than charged for. Browse a category when you need depth |
| total_results is sometimes an estimate and says so | one query's total moved between 10,006 and 60,468 on identical calls; large categories clamp at exactly 300,000 | read total_results_is_estimate before you put the number in a report |
| EAN is on the product, not on the listing row | 0 of 199 listing rows carried an EAN; product/detail returned a real one on 7 of 7 products checked, one per department | bol ships a summary spec block on a card and the full spec groups only on the product, and EAN lives in a group |
| Two sort values actually bite | price ascending and price descending were both proven on the ORGANIC rows only, because sponsored tiles are injected out of order. Everything else returned a byte-identical default order | only the two that were proven are published; the rest are rejected rather than accepted and ignored |
bol translates spec VALUES per storefront, not only labels: the same product's Model reads "Ace" on the Dutch storefront and "As" on the Belgian French one. The engine keys off bol's stable English machine keys (EAN, Mpn, Model, Colour) but returns whatever value that storefront publishes - inventing a canonical value would be fabricating one.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/bol/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "koptelefoon"
}
}{
"ok": true,
"meta": {
"api": "bol",
"endpoint": "search",
"mode": "live",
"latency_ms": 2460,
"record_count": 29,
"bytes": 667684,
"cache_hit": false,
"page": 1,
"market": "nl"
},
"data": {
"products": [
{
"product_id": "[redacted-phone]",
"title": "Sonos Ace - Draadloze hoofdtelefoon - Noise Cancelling - Dolby Atmos - TV Audio Swap - Zwart",
"url": "https://www.bol.com/nl/nl/p/sonos-ace-zwart/[redacted-phone]/",
"brand": {
"id": "4253982",
"name": "Sonos",
"url": "https://www.bol.com/nl/nl/pb/sonos/4253982/"
},
"price": 307,
"currency": "EUR",
"reference_price": 342,
"discount_percent": 10,
"condition": "new",
"seller": {
"id": "0",
"name": "bol",
"is_bol": true,
"rating": null,
"rating_count": null,
"top_seller": null
},
"delivery": {
"description": "Op voorraad. Voor 23:59 uur besteld, morgen in huis",
"arrives_by": "[redacted-phone]",
"release_date": null,
"shipping_cost": null,
"within_48h": true
},
"low_stock": false,
"promotions": [
"deal"
],
"offer_id": "4c86098a-554a-45f3-875f-54922835c040",
"rating": 4.8,
"review_count": 6,
"image": "https://media.s-bol.com/43p9oWPzz557/rkV8946/550x838.jpg",
"category": "Koptelefoons",
"category_path": [
"Elektronica",
"Audio & Hifi",
"Audio voor jou"
],
"ean": null,
"highlights": [
"Over-ear",
"Draadloos",
"Noise cancelling"
],
"pack_quantity": null,
"energy_label": null,
"sponsored": true,
"offers_url": "https://www.bol.com/nl/nl/prijsoverzicht/sonos-ace-zwart/[redacted-phone]/?filter=new&sort=price&sortOrder=asc"
},
{
"product_id": "[redacted-phone]",
"title": "PowerLocus MoonFly Bluetooth Koptelefoon – Over-Ear Active Noise Cancelling Koptelefoon – 70 uur batterijduur – Microfoon – incl. Premium Case - Zwart",
"url": "https://www.bol.com/nl/nl/p/powerlocus-moonfly-bluetooth-koptelefoon-over-ear-active-noise-cancelling-koptelefoon-70-uur-batterijduur-microfoon-incl-premium-case-zwart/[redacted-phone]/",
"brand": {
"id": "16984845",
"name": "PowerLocus",
"url": "https://www.bol.com/nl/nl/b/powerlocus/16984845/"
},
"price": 41.99,
"currency": "EUR",
"reference_price": 49.99,
"discount_percent": 16,
"condition": "new",
"seller": {
"id": "1320796",
"name": "Mition",
"is_bol": false,
"rating": null,
"rating_count": null,
"top_seller": null
},
"delivery": {
"description": "Op voorraad. Voor 23:59 uur besteld, morgen in huis",
"arrives_by": "[redacted-phone]",
"release_date": null,
"shipping_cost": null,
"within_48h": true
},
"low_stock": false,
"promotions": [
"deal"
],
"offer_id": "a9d50d66-edd4-cf6f-e[redacted-phone]a535f",
"rating": 4.3,
"review_count": 304,
"image": "https://media.s-bol.com/gqg3BrMQGgWl/ADxKqwB/550x587.jpg",
"category": "Koptelefoons",
"category_path": [
"Elektronica",
"Audio & Hifi",
"Audio voor jou"
],
"ean": null,
"highlights": [
"Over-ear",
"Draadloos",
"Noise cancelling"
],
"pack_quantity": null,
"energy_label": null,
"sponsored": true,
"offers_url": "https://www.bol.com/nl/nl/prijsoverzicht/powerlocus-moonfly-bluetooth-koptelefoon-over-ear-active-noise-cancelling-koptelefoon-70-uur-batterijduur-microfoon-incl-premium-case-zwart/[redacted-phone]/?filter=new&sort=price&sortOrder=asc"
},
{
"product_id": "[redacted-phone]",
"title": "Sony MDR-ZX110 - On-ear koptelefoon - Zwart",
"url": "https://www.bol.com/nl/nl/p/sony-mdr-zx110-on-ear-koptelefoon-zwart/[redacted-phone]/",
"brand": {
"id": "3123135",
"name": "Sony",
"url": "https://www.bol.com/nl/nl/pb/sony/3123135/"
},
"price": 15,
"currency": "EUR",
"reference_price": null,
"discount_percent": null,
"condition": "new",
"seller": {
"id": "0",
"name": "bol",
"is_bol": true,
"rating": null,
"rating_count": null,
"top_seller": null
},
"delivery": {
"description": "Op voorraad. Voor 23:59 uur besteld, morgen in huis",
"arrives_by": "[redacted-phone]",
"release_date": null,
"shipping_cost": null,
"within_48h": true
},
"low_stock": false,
"promotions": null,
"offer_id": "078ae9e4-dfa4-47db-a7ac-a2a18ed33611",
"rating": 4.5,
"review_count": 403,
"image": "https://media.s-bol.com/qxxrK46A8Ko7/539x840.jpg",
"category": "Koptelefoons",
"category_path": [
"Elektronica",
"Audio & Hifi",
"Audio voor jou"
],
"ean": null,
"highlights": [
"On-ear",
"Niet draadloos",
"Geen microfoon"
],
"pack_quantity": null,
"energy_label": null,
"sponsored": false,
"offers_url": "https://www.bol.com/nl/nl/prijsoverzicht/sony-mdr-zx110-on-ear-koptelefoon-zwart/[redacted-phone]/?filter=new&sort=price&sortOrder=asc"
}
],
"count": 29,
"page": 1,
"total_results": 64330,
"total_results_is_estimate": false,
"total_pages": 10,
"categories": [
{
"id": "3136",
"name": "Elektronica",
"url": "https://www.bol.com/nl/nl/s/?searchtext=koptelefoon&N=3136"
},
{
"id": "33078",
"name": "Koptelefoons",
"url": "https://www.bol.com/nl/nl/s/?searchtext=koptelefoon&N=33078"
},
{
"id": "14035",
"name": "Wonen",
"url": "https://www.bol.com/nl/nl/s/?searchtext=koptelefoon&N=14035"
}
],
"query": "koptelefoon"
}
}What the bol.com API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| search | Search bol.com by keyword, paginated. Every row carries the product id and url, title, brand, the current price and bol's own 90-day reference price, the discount, the seller behind the buy-box (bol itself or a marketplace partner), the delivery promise, the rating and review count, the category path, the EAN when bol publishes one, and whether the row is a paid placement. The response also returns the ids of the categories the results fall into, which is what `category` takes. | Pricing teams call search to search bol.com by keyword, paginated. | query, page, sort, market, max_rotations |
| product/detail | Full bol.com product by id (or by its url): title, brand, EAN and MPN, the buy-box price with bol's 90-day reference price and the discount, the seller, the delivery promise, the full image gallery, the complete specification table, the description as plain text, the category breadcrumb, the average rating and review count, and how many competing offers exist. Review TEXT is not returned - bol serves it only to a signed-in session, and this endpoint never invents it. | Marketplace operators call product/detail to get full bol.com product by id (or by its url). | product_id, market, max_rotations |
| product/offers | Every seller offering one bol.com product, with that seller's price, condition, delivery promise, shipping cost and bol seller rating - bol is a marketplace and this is the competing-offer table behind the buy-box, on bol's own price-overview page. Cheap: about 45 KB per call against 290 KB for the product page. | Catalog enrichment teams call product/offers to get every seller offering one bol.com product, with that seller's price, condition, delivery prom…. | product_id, market, max_rotations |
| category | Browse a bol.com category by its id, paginated - the same rich product row `search` returns, plus the category bol resolved and its breadcrumb. Category listings page far deeper than search does and their result total is exact rather than estimated. | Retail analysts call category to get browse a bol.com category by its id, paginated - the same rich product row `search` returns,…. | category_id, page, sort, market, max_rotations |
Call search from your stack
curl -X POST https://api.reefapi.com/bol/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"koptelefoon"}'import requests
r = requests.post(
"https://api.reefapi.com/bol/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "koptelefoon"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/bol/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "koptelefoon"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.bol.search with {"query":"koptelefoon"}.Who uses this API and why
- Benelux price monitors call product/offers for the whole competing-seller table at a sixth of the cost of a product page.
- Marketplace sellers check where their own offer sits against every other seller on the same bol listing, with each seller's bol rating attached.
- Cross-border pricing teams run nl, be and be-fr on the same product id to see the real price gap between the Dutch and Belgian storefronts.
- Catalog teams call product/detail for the EAN, the MPN, the full spec groups and the sibling listings that differ only by colour or size.
Questions developers ask before integrating
What does one product/offers call return?
One row per seller offering that product, plus a breakdown of how many offers are new, refurbished and second-hand. A measured call on the Anker Soundcore Space One returned 6 sellers between 84.00 and 92.71 EUR - Anker Direct, ASD, gsmpunt, Eufy Official, bol itself and IT Distributie - each with its price, condition, shipping cost, the date it arrives, whether that is within 48 hours, and its bol seller rating out of 10. Across a 7-product chain check the action returned 1, 1, 1, 1, 2, 3 and 6 offers, so multi-seller products are common but not universal.
Why does bol's own row have no seller rating?
Because bol is not one of its own marketplace partners, so it has no partner rating to publish. That row comes back with the rating and top-seller flag null and an explicit is_bol flag set, rather than being filled in with a flattering default. Every third-party seller does carry a rating - 8.7, 8.4 and 9.3 on the measured rows - on bol's 1-to-10 scale.
Is be a language switch or a different market?
A different market. The same product measured 15.00 EUR on the Dutch storefront and 13.05 EUR on both Belgian ones within the same minute, so prices genuinely differ. be and be-fr are the Dutch and French storefronts of Belgium - a French search returns French titles and French category names for the same product ids. Both Belgian storefronts and the Dutch one were verified on all four actions.
What is the reference price, and is it a list price?
It is not an RRP and it is not returned as one. bol defines it as the most-shown price of the last 90 days excluding promotions, and it is returned under that name, on roughly the 40% of rows that have one. The discount is computed against that, so a discount here means "cheaper than bol has typically been showing it", not "cheaper than the manufacturer's list price" - which bol does not publish anywhere in the payload.
How does bol model colours and sizes?
As separate products with their own ids, which is bol's own model rather than a variant matrix. product/detail returns those siblings in a related-products list along with the feature that distinguishes them - Kleur for colour, Maat for size - so you can walk to them, but there is no size grid to read and none is invented. This is worth knowing before you build a variant table: on bol the price and the seller can differ per colour, because each colour is its own listing.
Does bol publish stock levels or review text?
Neither, and both are null. What bol publishes instead of a stock count is a delivery promise ("in stock, order before 23:59 and it arrives tomorrow"), a latest-arrival date and a scarcity flag - there is no number anywhere in the payload. Review text sits behind an authenticated call that returns 403 without a session, so only the average rating and the review total are returned. Seller legal details - address, chamber-of-commerce number, phone - live on a separate bol page this engine does not read.
Why is brand empty on some book rows?
Because a book has no brand on bol - it has an author, which bol publishes as a specification rather than as a brand. Measured: brand was filled on 5 of 30 rows in the Boeken department and on 28 of 28 in Films, where the field itself is clearly working ("Warner Home Video"). Ratings are similar: 6 of 28 Films rows had one, because most of those titles have no reviews yet. The gaps are bol's, and they come back null.
How fast and how reliable is it?
135 live calls across three separate runs came back 135 successful, with a median around 1.8 to 2.2 seconds. Per action on the shipped build: product/offers 1,224 ms, product/detail 1,550 ms, search 2,219 ms, category 2,694 ms. There is a real tail - one search in 135 took 17.2 seconds on a single slow exit - and an occasional block that is typed as retryable so the gateway re-leases and tries again. No storefront is meaningfully slower than another.
What is the bol.com API?
bol.com API is a ReefAPI endpoint group for dutch and belgian marketplace products, prices and competing sellers. It returns live JSON through POST requests under /bol/v1.
Is the bol.com API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. bol.com calls use the same shared credit balance as every other ReefAPI engine.
Do I need a bol.com login or account?
No login to bol.com is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the bol.com data?
The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the bol.com API use?
bol.com actions currently cost 1-2 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call bol.com from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call bol actions with the same key, credit pool and JSON envelope used by normal REST requests.