Scrappa's GET /api/bol/search returns bol.com products for the Netherlands and Belgium as structured JSON, with sort, category, brand and price filters and paging.
Credits
One successful request with results costs 9 credits. An empty result, a validation error and every failure cost nothing.
What you get
One request returns one page (about 30 cards) with bol.com's own match count in total, max_page, the requested page and the facets for the next call. Each product has id, title, url, price (a number) and currency (EUR), original_price and discount (amount and percent) when bol.com shows a reference price, select_price, seller, condition, delivery_text, delivery_date (ISO 8601), image_url, rating, review_count, brand, categories, is_sponsored and offers_url. The id goes unchanged into the product, offers and reviews endpoints.
Storefronts
country=nl (default) searches bol.com Netherlands. country=be searches Belgium in Dutch (language=nl, default) or French (language=fr). A search for an EAN returns the product with that EAN.
Filters are honoured
category takes a category id from facets with type category. facet_ids takes filter value ids from facets (for example a brand), comma-separated. min_price and max_price filter by price in EUR; cards whose shown price is outside the range are removed from the page. applied_filters echoes what was sent. Unknown parameters and values return 422.
Sorting and paging
sort is relevance (default), price_asc, price_desc, rating or popularity. bol.com lists at most 10 pages. A later page returns an empty products list with the requested page, never page 1 data.
Not available
Rating and delivery filters are not offered. A bare brand name such as a brand without a product word returns 422.
Run this endpoint
Endpoint
Parameters
Start with the required fields, then add optional filters only when your use case needs them.
Runnable path
1 required parameter needed before sending a request.
8 optional filters available.
string
Required
Search term or EAN, for example airfryer.
airfryer
string
Optional
nl (default) or be.
nl
string
Optional
nl (default) or fr. fr needs country=be.
en
integer
Optional
Result page, 1 to 10. Later pages are empty.
1
string
Optional
relevance (default), price_asc, price_desc, rating or popularity.
price_asc
string
Optional
Category id from the facets of an earlier search, for example 18336.
example
string
Optional
Filter value ids from the facets, such as a brand. Comma-separated.
example
number
Optional
Minimum price in EUR.
100
number
Optional
Maximum price in EUR.
150
Request Examples
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: YOUR_API_KEY_HERE"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
<?php
use Illuminate\Support\Facades\Http;
$response = Http::timeout(30)
->withHeaders(['x-api-key' => 'YOUR_API_KEY_HERE'])
->get('https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150');
if ($response->successful()) {
echo $response->body();
} else {
echo "Error: " . $response->status();
}
const options = {
method: 'GET',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE'
}
};
fetch('https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150', options)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
const axios = require('axios');
const options = {
method: 'GET',
url: 'https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150',
headers: {
x-api-key: 'YOUR_API_KEY_HERE',
}
};
try {
const response = await axios(options);
console.log(response.data);
} catch (error) {
console.error('Error:', error.message);
}
require 'net/http'
require 'uri'
uri = URI.parse("https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri.request_uri)
request['x-api-key'] = 'YOUR_API_KEY_HERE'
begin
response = http.request(request)
puts response.body
rescue => e
puts "Error: #{e.message}"
end
import http.client
import json
conn = http.client.HTTPSConnection("scrappa.co")
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
conn.request("GET", "/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
except Exception as e:
print(f"Error: {e}")
finally:
conn.close()
import requests
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
response = requests.get('https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150', headers=headers)
response.raise_for_status()
print(response.text)
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class ApiExample {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150")
.addHeader("x-api-key", "YOUR_API_KEY_HERE")
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
System.out.println(response.body().string());
} else {
System.out.println("Error: " + response.code());
}
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("x-api-key", "YOUR_API_KEY_HERE")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println(string(body))
}
#!/bin/bash
curl -X GET \
-H "x-api-key: YOUR_API_KEY_HERE" \
"https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150"
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY_HERE");
try
{
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150"));
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
import axios from 'axios';
async function run(): Promise<void> {
try {
const response = await axios({
method: 'GET',
url: 'https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE',
},
});
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
}
void run();
use reqwest::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let response = client
.get("https://scrappa.co/api/bol/search?q=airfryer&country=nl&sort=price_asc&min_price=100&max_price=150")
.header("x-api-key", "YOUR_API_KEY_HERE")
.send()
.await?;
println!("{}", response.text().await?);
Ok(())
}
Response Schema
Example response fields are illustrative; inspect the JSON before integrating.
Example response fields
Scan these fields before integrating.
success
query
locale
page
max_page
total
returned
applied_filters
+3 more
Common products fields
id
title
url
price
{
"success": true,
"query": "airfryer",
"locale": "nl/nl",
"page": 1,
"max_page": 10,
"total": 704,
"returned": 28,
"applied_filters": {
"sort": "price_asc",
"min_price": 100,
"max_price": 150
},
"products": [
{
"id": "9300000171550609",
"title": "Philips 3000 Series Dual Airfryer - NA351/00 - Dubbele Mand",
"url": "https://www.bol.com/nl/nl/p/philips-3000-series-dual-airfryer-na351-00/9300000171550609/",
"price": 109,
"currency": "EUR",
"original_price": 199.99,
"discount": {
"amount": 90.99,
"percent": 45
},
"select_price": null,
"is_select": false,
"seller": {
"id": "0",
"name": "bol",
"is_bol": true,
"rating": null,
"rating_percent": null,
"top_seller": null
},
"condition": "new",
"delivery_text": "Op voorraad. Voor 23:59 uur besteld, dinsdag in huis",
"delivery_date": "2026-11-10",
"image_url": "https://media.s-bol.com/Qr0jNQGR24Q0/zp9Z668/1200x989.jpg",
"rating": 4.6,
"review_count": 200,
"brand": {
"id": "4978545",
"name": "Philips"
},
"categories": [
[
{
"id": "3136",
"name": "Elektronica"
},
{
"id": "18336",
"name": "Airfryers"
}
]
],
"is_sponsored": false,
"offers_url": "https://www.bol.com/nl/nl/prijsoverzicht/philips-3000-series-dual-airfryer-na351-00/9300000171550609/",
"locale": "nl/nl"
}
],
"facets": [
{
"type": "category",
"id": "18336",
"name": "Airfryers",
"count": 2868,
"applied": false
},
{
"type": "range",
"id": "12194",
"name": "Prijs (\u20ac)",
"min": 0.89,
"max": 2203,
"unit": "\u20ac"
},
{
"type": "filter",
"id": "4842",
"name": "Merk",
"items": [
{
"id": "4294884369",
"name": "Philips",
"count": 72,
"applied": false
}
]
}
],
"meta": {
"endpoint_family": "search",
"billable": true,
"credits": 9,
"attempts": 1
}
}
Errors
Handle these documented responses before retrying or showing customer-facing failures.
Query Not Searchable
bol.com answers a bare brand name with a brand page instead of a product list (`unsearchable_query`). Add a product word. Non-billable.
{
"meta": {
"attempts": 1,
"billable": false,
"retryable": false,
"endpoint_family": "search"
},
"error": {
"code": "unsearchable_query",
"message": "bol.com answers this query with a brand or category page, not a product list. Search for a product term instead."
},
"success": false
}Validation Error
A query parameter is missing, unknown or unusable. Unknown parameters and unknown values are rejected, never ignored. Non-billable.
{
"errors": {
"q": [
"The q field is required."
]
},
"message": "The q field is required."
}Upstream Unavailable
bol.com could not be reached after retries, answered with an error or changed its format (`upstream_unavailable`, `upstream_blocked`, `parser_drift`). Retryable and never billed. Clients are never rate limited.
{
"meta": {
"attempts": 4,
"billable": false,
"retryable": true,
"endpoint_family": "search"
},
"error": {
"code": "upstream_unavailable",
"message": "bol.com is temporarily unavailable. Please retry."
},
"success": false
}Generate Code with AI
Copy a ready-made prompt with all the endpoint details, parameters, and example responses. Paste it into ChatGPT, Claude, or any AI assistant to instantly generate working code.
Try It Live
Test this endpoint in our interactive playground with real data.