BestPrice.gr API & Scraper
The BestPrice.gr API turns Greece's second large price-comparison site into JSON in three actions.
🤖 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.
search takes Greek or English keywords and returns the product grid: product_id, title, url, the price BestPrice prints on the card, the category id and the product image, with the shop count where the card carries one. category walks one category's grid without a keyword, and takes an optional keyword to narrow it. product is the action the source exists for: the shop-by-shop offer table for one product, each row carrying the shop's name, its price in euros, its availability and its link, plus the product's brand, GTIN, the Greek category path, the customer rating and the market's price band. Two counts come back on purpose and they are not the same number: offer_count is how many shops the market has for that product and offers_listed is how many this response carries, because BestPrice lists the first thirty on the page itself. Measured on 2026-09-23 through the live gateway: on 103 search rows across six queries and a category, product_id, title, url, price, image and category id were filled on every single row and the shop count on 74 percent of them; on eight products the offer table ran from 3 to 30 rows, and where the market had thirty or fewer shops every one of them came back. The price is a number in euros on every row, never a string and never the site's raw integer. This is a second Greek market source beside Skroutz, useful for the same product from two comparison sites. One ReefAPI key, the standard { ok, data, meta, error } envelope, and no BestPrice account of any kind.
Which BestPrice number is which
Four of these fields look interchangeable and are not. Measured through the live gateway on 2026-09-23.
| Field | What it is | Measured example |
|---|---|---|
| price_eur (search row) | The price BestPrice prints on the grid card, in euros | 610.00 on the iPhone 15 128GB card |
| price_low_eur / price_high_eur | The band across the WHOLE market for that product, as the product page states it | 610.00 to 999.00 while the market had 74 shops |
| offer_count | How many shops the market has for the product | 74, 87 and 71 on three measured products |
| offers_listed | How many offers THIS response carries. The product page lists the first thirty | 30 of 74; but 23 of 23 and 3 of 3 where the market is smaller |
| offers[].price_eur | One shop's own price. The lowest of these equals price_low_eur | 701.33 lowest, 885.00 highest listed, on a product whose band reaches 964.26 |
| shop_count (search row) | The shop count printed on the grid card, when the card carries one | filled on 76 of 103 measured rows |
| offers[].availability | The stock line the shop declares, per offer | InStock on 28 rows and BackOrder on 2 of the same product |
The band and the offer list answer different questions: price_high_eur describes the whole market, while the offers array stops at the thirty the product page lists, so the highest listed offer can sit below price_high_eur. Compare like with like by reading offer_count beside offers_listed.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/bestprice/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "iphone"
}
}{
"ok": true,
"meta": {
"api": "bestprice",
"endpoint": "search",
"mode": "live",
"latency_ms": 14804.6,
"record_count": 14,
"bytes": 121937,
"cache_hit": false,
"stop_reason": "complete",
"query": "iphone",
"charged_credits": 2,
"version": "1.0.0"
},
"data": {
"products": [
{
"product_id": "2162499899",
"url": "https://www.bestprice.gr/item/2162499899/apple-iphone-17-256gb.html",
"title": "Apple iPhone 17 256GB",
"price_eur": 860,
"currency": "EUR",
"category_id": "806",
"shop_count": 59,
"image": "https://bbpcdn.pstatic.gr/bpimg59/2mlD0D/1VPsWP_SX250Y333/1770649991/apple-iphone-17-256gb-mg6j4qla-mg6m4qla-mg6l4qla-mg6n4qla-mg6k4qla.webp"
},
{
"product_id": "2162499901",
"url": "https://www.bestprice.gr/item/2162499901/apple-iphone-17-pro-max-256gb.html",
"title": "Apple iPhone 17 Pro Max 256GB",
"price_eur": 1243,
"currency": "EUR",
"category_id": "806",
"shop_count": 66,
"image": "https://bbpcdn.pstatic.gr/bpimg61/2mlD0F/1VPsYV_SX250Y333/1770650121/apple-iphone-17-pro-max-256gb-mfyn4qla-mfyp4qla-mfym4qla.webp"
},
{
"product_id": "2162499900",
"url": "https://www.bestprice.gr/item/2162499900/apple-iphone-17-pro-256gb.html",
"title": "Apple iPhone 17 Pro 256GB",
"price_eur": 1132.88,
"currency": "EUR",
"category_id": "806",
"shop_count": 65,
"image": "https://bbpcdn.pstatic.gr/bpimg60/2mlD0E/1VFXeJ_SX250Y333/1768383141/apple-iphone-17-pro-256gb-mg8h4qla-mg8j4qla-mg8g4qla.webp"
}
],
"page": 1,
"limit": 24,
"query": "iphone"
}
}What the BestPrice.gr API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| search | Search BestPrice by keyword. Returns the product grid: title, the price BestPrice shows (the lowest across shops), how many shops carry it, and the product id you feed to `product`. | Pricing teams call search to search BestPrice by keyword. | query, page, limit |
| barcode | Find a product by its BARCODE (EAN-13, EAN-8, UPC-12 or GTIN-14) and get the BestPrice product id, so a catalogue can be matched on barcodes instead of names. Keyword `search` cannot do this: BestPrice's text index has no barcodes in it. Each row carries `match_type`: `barcode_exact` means BestPrice itself declared an exact barcode match (measured 25 of 25 correct, no false positive), `text_match` means it fell back to a fuzzy name match and MAY be a different product. By default only exact matches are returned. Coverage is partial and honest: 25 of 36 barcodes taken off BestPrice's own product pages resolved (3/3 in electronics, appliances, laptops, toys and tools; 1/3 in supermarket and beauty), so an empty answer means BestPrice's barcode index does not carry that code — not an error. Feed the returned `product_id` to `product` for every Greek shop's price. | Marketplace operators call barcode to find a product by its BARCODE (EAN-13, EAN-8, UPC-12 or GTIN-14) and get the BestPrice produc…. | barcode, strict, limit |
| product | One product with EVERY shop offer: the shop name, its price, availability and the price band across the market (lowest, highest, how many shops). This is the whole point of a price-comparison source. It also returns the product's BARCODE (`gtin`, with `gtin_type` naming the key it came from) — published on 36 of 36 pages measured across 12 categories — so a catalogue can be matched on barcodes even for the codes BestPrice's `barcode` lookup does not carry. | Catalog enrichment teams call product to get one product with EVERY shop offer. | product_id |
| category | Browse one category's grid without a keyword. Same rows as `search`. Take the category id from a search row's `category_id` or from a category URL. | Retail analysts call category to get browse one category's grid without a keyword. | category_id, page, limit, query |
Call search from your stack
curl -X POST https://api.reefapi.com/bestprice/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"iphone"}'import requests
r = requests.post(
"https://api.reefapi.com/bestprice/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "iphone"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/bestprice/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "iphone"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.bestprice.search with {"query":"iphone"}.Who uses this API and why
- Greek price monitoring: one product id, every listed shop's price and stock, plus the market's low and high in euros.
- Repricing for Greek e-shops: watch where your own shop sits in the offer table for the products you carry.
- Cross-checking a second comparison site beside Skroutz before publishing a price or a claim about the cheapest offer.
- Catalogue enrichment: brand, GTIN, the Greek category path and the product image from a keyword or a category walk.
- Market research on a Greek category: walk the grid by category id and read prices and shop counts across the shelf.
Questions developers ask before integrating
Do I get every Greek shop selling a product?
You get every offer the product page lists, which is the first thirty, and you are told how many the market has. Measured on eight products on 2026-09-23: 30 of 74, 30 of 87, 30 of 71, 25 of 26, and 23 of 23, 23 of 23, 23 of 23 and 3 of 3 where the market had thirty or fewer shops. So for most products you get all of them, and for the very crowded ones you get the cheapest end of the table plus an honest count of what is behind it. offers_listed and offer_count are both in the response so you never have to guess which you are holding.
Does it work with Greek keywords?
Yes, and that is the better way to search it. Greek queries were measured alongside English ones on 2026-09-23 and returned the same shape of rows, so a category phrase in Greek works as naturally as a brand and model in Latin letters. Titles, the category path and the stock lines come back in the site's own language, unmodified.
Is the price a number, and in which currency?
A number, in euros, on every row. The site's own markup carries the price as an integer, which is the trap a hand-written parser walks into and publishes a price a hundred times too large; the conversion happens once inside the engine, so price_eur is 610.0 and never 61000. There is one currency here because there is one market: bestprice.gr is Greece only, and there is no country parameter behind it.
How do I go from a search result to the shop comparison?
Every search and category row hands back product_id, and product takes that id or a full bestprice.gr product URL. A dead or wrong id is answered with NOT_FOUND rather than an empty-looking success, which matters here because the site answers an unknown product with a normal page rather than a 404.
What does this add if I already use Skroutz?
A second Greek comparison site for the same product. The two rank shops differently and do not carry identical merchant sets, so reading both is how a Greek pricing tool sees the market rather than one site's view of it. Both are separate engines here with their own actions and their own credit prices.
Which fields can be empty?
Measured across 103 rows on 2026-09-23: product_id, title, url, price, image and category id were filled on all 103, and shop_count on 76 of them, because BestPrice does not print a shop count on every grid card. On the product side, the rating and its count come back as null when the site publishes no rating for that product, which was the case on four of the eight measured products, rather than being dressed up as a zero.
What is the BestPrice.gr API?
BestPrice.gr API is a ReefAPI endpoint group for greek price comparison: every shop's price for one product, the market's price band and the shop count, in eur. It returns live JSON through POST requests under /bestprice/v1.
Is the BestPrice.gr API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. BestPrice.gr calls use the same shared credit balance as every other ReefAPI engine.
Do I need a BestPrice.gr login or account?
No login to BestPrice.gr 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 BestPrice.gr 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 BestPrice.gr API use?
BestPrice.gr actions currently cost 1-3 credits per successful call. Failed or blocked calls are free. All APIs draw from one credit pool.
Can I call BestPrice.gr from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call bestprice actions with the same key, credit pool and JSON envelope used by normal REST requests.
Is the BestPrice.gr API a BestPrice.gr scraper?
It is the managed alternative to a DIY BestPrice.gr scraper. Instead of building and maintaining your own scraper — proxies, headless browsers, captcha and constant breakage — you call one ReefAPI endpoint and get the same greek price comparison: every shop's price for one product, the market's price band and the shop count, in eur back as clean JSON.
Why does my BestPrice.gr scraper keep getting blocked?
Most BestPrice.gr scrapers break on anti-bot defenses, rate limits and IP bans that need rotating residential proxies and browser fingerprinting to clear. ReefAPI handles all of that for you — no proxies, no captchas, no maintenance — and returns live JSON. Blocked calls are free.