Refurbed API & Scraper
The Refurbed API returns refurbed, the Vienna-based refurbished-electronics marketplace, as clean JSON across every one of its 24 European storefronts, in five actions: search, product, categories, filters and compare_countries.
🤖 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.
refurbed runs a separate catalogue, price list and currency per country, so every action takes an explicit country and every response echoes the country, the locale and the currency it actually read — a row from this API is never 'some country's price'. Verified live on 2026-10-01: all 24 storefronts answered, with the currency taken from each storefront's own structured data — EUR on 19 of them, GBP on refurbed.co.uk, CHF on refurbed.ch, SEK on refurbed.se, DKK on refurbed.dk, PLN on refurbed.pl and CZK on refurbed.cz. The same /c/smartphones/ shelf held 506 offers in Germany, 488 in the Netherlands, 480 in France and Belgium, 474 in Austria and Italy, 473 in Spain, 463 in Poland, 456 in Czechia, 452 in Sweden, 136 in Switzerland and 105 in the United Kingdom. What makes a refurbished marketplace different from a shop is the condition ladder, and this API carries refurbed's own four appearance grades verbatim — Premium, Excellent, Very good and Good, which refurbed also labels AA, A, B and C — never remapped onto a scale of ours. The product action returns the whole ladder for one device with refurbed's own price difference per grade: on the iPhone 13 we sampled, Good was 264.99 EUR, Very good +13.01, Excellent +35.01 and Premium +74.01, plus the independent battery option (Optimal or New, +10.02 there). search narrows a storefront by free-text keyword or by category slug, then by brand, colour and a price range in that storefront's own currency, sorted by price up or down, with refurbed's own exact match count in meta.total_results so you can see a filter bite. Measured in one run against an unfiltered 506 German smartphone offers: brand=Apple 43, brand=Apple,Samsung 172, colour=Black 313, price_min=800 70, price_max=200 224, price 200-400 218, Apple under 300 EUR 21 — all seven narrowed the set. Every product row carries the offer id, the model id, the product URL, brand, refurbed's own three-level category path, the variant label, the grade letter and code, the live price, the price string the storefront printed next to it, the separate new-retail reference price, the currency and its symbol, the rating and the image. product adds the full spec sheet, every gallery image, all colour and storage variants with their own prices and offer ids, the product rating with its review count, the stated warranty and return window, shipping cost and the handling and transit day ranges. No refurbed account and no API key on their side — one ReefAPI key and the standard { ok, data, meta, error } envelope.
The 24 storefronts, their currency and their live smartphone shelf on 2026-10-01
Every row was fetched live. The currency is the ISO code that storefront publishes in its own structured data, not an assumption from the domain — and not the code in refurbed's analytics payload, which says EUR on all 24 including the British, Swiss, Swedish, Danish, Polish and Czech ones. Offers move daily; every search response carries that storefront's own exact total for your query.
| country | storefront | currency | smartphone offers |
|---|---|---|---|
| de | refurbed.de | EUR | 506 |
| nl | refurbed.nl | EUR | 488 |
| fr | refurbed.fr | EUR | 480 |
| be | refurbed.be | EUR | 480 |
| at | refurbed.at | EUR | 474 |
| it | refurbed.it | EUR | 474 |
| es | refurbed.es | EUR | 473 |
| dk | refurbed.dk | DKK | 472 |
| lu | refurbed.lu | EUR | 466 |
| pl | refurbed.pl | PLN | 463 |
| cz | refurbed.cz | CZK | 456 |
| se | refurbed.se | SEK | 452 |
| fi | refurbed.fi | EUR | 449 |
| pt | refurbed.pt | EUR | 447 |
| bg | refurbed.bg | EUR | 440 |
| ie | refurbed.ie | EUR | 439 |
| ee | refurbed.ee | EUR | 438 |
| sk | refurbed.sk | EUR | 434 |
| hr | refurbed.hr | EUR | 433 |
| si | refurbed.si | EUR | 433 |
| lt | refurbed.lt | EUR | 433 |
| lv | refurbed.lv | EUR | 412 |
| ch | refurbed.ch | CHF | 136 |
| gb | refurbed.co.uk | GBP | 105 |
Switzerland and the United Kingdom are genuinely smaller shelves, not a parsing problem — both answered with 16 rows and a full price string, and their own category pages reported the same 136 and 105. Other shelves on refurbed.de the same day: laptops 1,204, smartphones 506, kitchen appliances 210, smartwatches 97, consoles 74. Results come 16 per page on every page we measured, which is refurbed's own page size and not a parameter; meta.pagination.has_more is refurbed's own has-more flag.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/refurbed/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "iphone 13",
"country": "de"
}
}{
"ok": true,
"meta": {
"api": "refurbed",
"endpoint": "search",
"mode": "live",
"latency_ms": 458,
"record_count": 16,
"bytes": 700773,
"cache_hit": false,
"completeness_pct": 6.61,
"stop_reason": "limit_reached",
"country": "de",
"locale": "en-de",
"currency": "EUR",
"page_size": 16,
"rows": 16,
"price_mismatch_rows": 5,
"currency_symbol": "€",
"page_type": "search",
"pagination": {
"page": 1,
"has_more": true,
"next_page": 2
},
"upstream_requests": 1,
"total_results": 242,
"charged_credits": 1,
"version": "1.0.0",
"request_id": "383e9d916eca4fdc",
"queue_ms": 1.2
},
"data": {
"products": [
{
"product_id": 14165,
"model_id": 2506,
"name": "iPhone 13",
"url": "https://www.refurbed.de/en-de/p/iphone-13/14165c/",
"slug": "iphone-13",
"brand": "Apple",
"department": "electronics",
"category": "Phones & Smartphones",
"subcategory": "iPhones",
"variant_label": "128 GB | Dual-SIM (eSIM, Nano-SIM) | Starlight | Grade C (14165)",
"grade": "Good",
"grade_letter": "C",
"grade_code": "c",
"grade_url_segment": "c",
"price": 249.99,
"price_display": "249,99 €",
"price_feed": 240.99,
"price_mismatch": true,
"price_new_reference": 760.45,
"price_new_reference_display": "760,45 € (New)",
"currency": "EUR",
"currency_symbol": "€",
"rating": 4.8,
"image": "https://files.refurbed.com/pi/iphone-13-1647245047.jpg?t=resize&h=300&w=300",
"tags": [
"Just a few left"
],
"position": 0
},
{
"product_id": 14184,
"model_id": 2508,
"name": "iPhone 13 Mini",
"url": "https://www.refurbed.de/en-de/p/iphone-13-mini/14184c/",
"slug": "iphone-13-mini",
"brand": "Apple",
"department": "electronics",
"category": "Phones & Smartphones",
"subcategory": "iPhones",
"variant_label": "128 GB | Dual-SIM (eSIM, Nano-SIM) | Midnight | Grade C (14184)",
"grade": "Good",
"grade_letter": "C",
"grade_code": "c",
"grade_url_segment": "c",
"price": 236.99,
"price_display": "236,99 €",
"price_feed": 236.99,
"price_mismatch": false,
"price_new_reference": 449.9,
"price_new_reference_display": "449,90 € (New)",
"currency": "EUR",
"currency_symbol": "€",
"rating": 4.76,
"image": "https://files.refurbed.com/pi/iphone-13-mini-1647245009.jpg?t=resize&h=300&w=300",
"tags": [
"Just a few left"
],
"position": 1
},
{
"product_id": 14171,
"model_id": 2507,
"name": "iPhone 13 Pro",
"url": "https://www.refurbed.de/en-de/p/iphone-13-pro/14171/",
"slug": "iphone-13-pro",
"brand": "Apple",
"department": "electronics",
"category": "Phones & Smartphones",
"subcategory": "iPhones",
"variant_label": "128 GB | Dual-SIM (eSIM, Nano-SIM) | blue | Grade A (14171)",
"grade": "Excellent",
"grade_letter": "A",
"grade_code": "a",
"grade_url_segment": "",
"price": 338.69,
"price_display": "338,69 €",
"price_feed": 338.69,
"price_mismatch": false,
"price_new_reference": null,
"price_new_reference_display": null,
"currency": "EUR",
"currency_symbol": "€",
"rating": 4.86,
"image": "https://files.refurbed.com/pi/iphone-13-pro-1647245103.jpg?t=resize&h=300&w=300",
"tags": null,
"position": 2
}
]
}
}What the Refurbed API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| search | Search or browse one refurbed storefront. PASS EITHER `query` (free-text keyword) OR `category` (a refurbed category slug) — one of the two is required, and calling with neither returns MISSING_PARAM rather than the whole catalogue. `country` selects the storefront and therefore the price list and the currency. `brand`, `colour`, `price_min`/`price_max` and `sort` are the storefront's own filters and every one of them was measured to change the result total. Each row carries refurbed's own appearance grade letter, the live price, the printed price string the storefront rendered beside it, and the separate new-retail reference price. `meta.total_results` is refurbed's own exact match count for the request, so you can tell a bitten filter from an ignored one. 🔴 refurbed's keyword matching is FUZZY and never says 'no match': the nonsense keyword 'zzqqxxnotathingqq' returned 300 rows on refurbed.de on 2026-10-01. A keyword result is a relevance list, not a containment filter — scope with `category` + `brand` + `price_min`/`price_max` when you need exactness. | Pricing teams call search to search or browse one refurbed storefront. | query, category, country, brand, colour, ... |
| product | The full record for one refurbed offer by `product_id`, in one `country`. Adds everything a result row cannot carry: refurbed's four appearance grades with the exact price step between them and the offer id of each, the battery option, every colour/storage variant with its own price, the complete spec sheet, every image, the product rating with its review count, the stated warranty and return window, shipping cost and the handling/transit day ranges. Optional `grade` opens the same device in another appearance grade. An id that is not live in that storefront answers NOT_FOUND — never an empty success. Pass a search row's `grade_code` as `grade` to land on that exact offer; without it refurbed serves its default grade for the device, which is a different price. | Marketplace operators call product to get the full record for one refurbed offer by `product_id`, in one `country`. | product_id, slug, country, grade |
| categories | The live category tree of one storefront, read off its own navigation: every category slug with the label that storefront prints for it. Feed a returned `slug` straight into search's `category`. One request, no product bodies transferred. | Catalog enrichment teams call categories to get the live category tree of one storefront, read off its own navigation. | country |
| filters | The live filter taxonomy for one search or category: refurbed's own attribute ids and labels (Brand, Storage, Colour, Screen Size, RAM, Operating System, Year of release …), the exact value list of every enum attribute and the min/max of every numeric one, plus the price range and currency symbol of that result set. This is how you discover which values exist in a storefront instead of guessing them. Same required-params rule as search: `query` or `category`. | Retail analysts call filters to get the live filter taxonomy for one search or category. | query, category, country |
| compare_countries | The same device's live price in several refurbed storefronts in one call — the question a single-country endpoint cannot answer. Give a `query` (or a `category`) and up to 6 `countries`; the engine reads each storefront's own search page sequentially, inside one wall-clock budget, and returns that storefront's cheapest matching row with its own currency. Storefronts that did not finish inside the budget are COUNTED in `meta.countries_failed` with the reason, never dropped silently. | Pricing teams call compare_countries to get the same device's live price in several refurbed storefronts in one call. | countries, query, category, countries |
Call search from your stack
curl -X POST https://api.reefapi.com/refurbed/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"iphone 13","country":"de"}'import requests
r = requests.post(
"https://api.reefapi.com/refurbed/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "iphone 13",
"country": "de"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/refurbed/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "iphone 13",
"country": "de"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.refurbed.search with {"query":"iphone 13","country":"de"}.Who uses this API and why
- Run cross-border price intelligence on refurbished hardware: one compare_countries call gives the same model's cheapest live offer in up to six storefronts, each in its own currency, so arbitrage and local-pricing questions get a measured answer instead of a guess.
- Build a condition-aware buying tool: pull one device's full appearance-grade ladder with refurbed's own price step per grade and its battery option, and show a buyer what Very good actually saves over Excellent on that exact unit.
- Monitor a competitor's or your own refurbished range across 24 European markets: browse a category per country, filter by brand and price band, and track the exact offer count and price distribution each storefront publishes for itself.
- Feed a sustainability or circular-economy dashboard with real secondary-market supply: 1,204 live laptop offers and 506 smartphone offers on refurbed.de alone on the day this was measured, broken down by brand, colour and price band.
- Enrich a marketplace or comparison site with refurbished alternatives: resolve a model to a refurbed offer, then return the live price, the grade, the stated minimum warranty, the 30-day return window and the free-shipping flag alongside the new-product listing.
Questions developers ask before integrating
Do I get refurbed's condition grades, or your interpretation of them?
refurbed's own, word for word. It grades device appearance in four categories and the product page prints them as Premium, Excellent, Very good and Good, while its data layer labels the same four AA, A, B and C. Both are returned: grade is the display name, grade_letter is refurbed's letter and grade_code is the lowercase short form. The pairing was checked on 32 live rows and agreed 32 of 32, with no exceptions. Nothing is translated into a scale of ours. A result card prints the letter rather than the name, so on a search row the name is resolved through refurbed's own letter-to-name table, the same one its product page prints; that pairing agreed on 32 of 32 live rows, so the name repeats refurbed rather than inferring anything.
How much does the grade change the price?
That is the question the product action is built to answer. It returns grade_options for the device you asked about: every appearance grade refurbed currently has in stock, each with refurbed's own price difference and its own offer id. On the iPhone 13 we sampled on refurbed.de, Good was 264.99 EUR and refurbed printed Very good as +13.01, Excellent as +35.01 and Premium as +74.01 against it. Battery is a separate ladder on phones — Optimal or New, +10.02 there — and comes back as battery_options. When refurbed has only one grade of a device left there is no ladder to return, so grade_options is null and grade still tells you which one it is.
Which country's price am I getting?
The one you asked for, and the response says so. country is required in practice — it defaults to de — and every response echoes meta.country, meta.locale, meta.currency and meta.currency_symbol. The currency is read from the storefront's own structured data, which matters: refurbed's analytics payload reports EUR on all 24 storefronts including refurbed.co.uk, so taking the obvious field would have published British prices labelled EUR. Across the 24 storefronts the live values are EUR on 19, GBP, CHF, SEK, DKK, PLN and CZK on the other five. If a storefront ever changes currency, the engine returns the live value and says so in meta.warnings instead of staying wrong quietly.
Can I compare the same device across countries in one call?
Yes, that is compare_countries. Give it a keyword or a category and 2 to 6 country codes and it reads each storefront's own search page inside one time budget, returning each one's cheapest matching offer with that country's currency and its own total. Six is the cap so a single call cannot outgrow its budget, and any storefront that did not finish is counted in meta.countries_failed with the reason rather than dropped silently.
Why do you return two prices, and why is one sometimes higher than what I expect?
price is what the storefront prints to a buyer, and price_new_reference is the brand-new retail reference refurbed shows struck through next to it. The second one is not a refurbed price and we do not call it one: refurbed's own tooltip describes it as the device's new retail price averaged from an independent price-comparison portal and recalculated daily. It is null whenever the storefront prints no strikethrough. There is also price_feed, which is the number in the storefront's own data layer for the same row — and it disagrees with the printed price on roughly a fifth of rows, always by a round 10 or 20 units and always lower. We publish the printed price, keep the data-layer number beside it, flag the row with price_mismatch and count the flagged rows in meta, because that disagreement is real and hiding it would mean quoting people prices that are too low.
Does a keyword search only return things that match my keyword?
No, and this is worth knowing before you build on it. refurbed's keyword search is fuzzy and it never reports a miss: the nonsense keyword zzqqxxnotathingqq still returned 300 rows on refurbed.de on 2026-10-01. A keyword result is a relevance list, not a containment filter. When you need an exact scope, browse a category slug and add brand, colour and a price range — those are real filters and every one of them was measured to change the total.
How do I know a filter actually did something?
Every search response reports meta.total_results, refurbed's own exact match count, plus meta.filters_requested and meta.filters_applied_by_source, which is the storefront echoing back which filters it recognised. In one run against an unfiltered 506 German smartphone offers: brand=Apple 43, brand=Apple,Samsung 172, colour=Black 313, price_min=800 70, price_max=200 224, price 200 to 400 218, and Apple under 300 EUR 21. Sorting is verifiable the same way — sort=price_asc returned 35.66 EUR as its first row and sort=price_desc returned 2,844.60, which are exactly the lowest and highest price that category publishes for itself. A brand the storefront does not stock comes back as a real zero and meta.warnings says the storefront did not recognise the value, so you can tell that apart from an empty shelf.
Do you tell me which merchant is selling the item?
No, because refurbed does not publish it. refurbed is a managed marketplace and its public product page names no merchant, no shop and no per-merchant rating anywhere — we probed for it rather than assuming. What the page does publish, and what you get, is the storefront entity (refurbed Deutschland, refurbed UK, and so on), the product rating with its review count, the stated warranty, the return window and the shipping and delivery-time ranges. Rather than fill a seller field with nulls, the API leaves it out.
What do I need to pull one product?
The offer id and the slug, both of which every search row gives you, and both of which are in any product URL: refurbed.de/en-de/p/iphone-13/14162c/ is slug iphone-13, id 14162 and grade code c. The slug is mandatory because refurbed has no id-only product route and answers HTTP 400 for an id with the wrong slug — we measured that rather than offering you a handle that does not work. Pass the row's grade_code as grade and you land on exactly that offer; leave it out and you get refurbed's default grade for that device, which is usually a different price.
What does product add over a search row?
The full spec sheet as refurbed labels it (24 rows on the iPhone 13: battery capacity, camera, connectivity, connectors, dimensions, display type, operating system, processor, weight and so on), every gallery image in display order, all colour and storage variants with their own price and offer id (18 on that iPhone 13), the appearance-grade ladder and the battery option, the product rating with its review count, the storefront's stated guarantees including the minimum warranty, the return window in days and whether returns are free, the shipping cost and the handling and transit day ranges, and the breadcrumb path. Everything in the search row is in there too.
How do I find out which categories and filter values exist?
Two cheap actions. categories returns that storefront's live category tree read off its own navigation — 60 slugs with labels on refurbed.de, 58 on refurbed.co.uk — and a returned slug goes straight into search's category. filters returns the live filter taxonomy for one search or category: refurbed's own attribute ids and labels with the exact value list of every enum and the min and max of every numeric one, plus that result set's price range and currency symbol. Smartphones exposed 10 attributes, laptops 15 — the taxonomy is per category, which is exactly why you should read it rather than guess it.
What happens if I ask for something that is not there?
You get a reason, never an empty success to interpret. An offer id that is not live in that storefront is NOT_FOUND. A slug that does not belong to the id is INVALID_PARAM with an explanation, because that is our URL being wrong and not the site refusing us. A country, category, grade or sort value refurbed does not have is rejected with the allowed list. Calling search with neither query nor category is MISSING_PARAM that says so, rather than dumping the whole catalogue on you.
What is the Refurbed API?
Refurbed API is a ReefAPI endpoint group for europe's refurbished-electronics marketplace as json: 24 country storefronts, each with its own price list and currency, refurbed's own four appearance grades and the price step between them. It returns live JSON through POST requests under /refurbed/v1.
Is the Refurbed API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Refurbed calls use the same shared credit balance as every other ReefAPI engine.