# Refurbed API — live refurbished-electronics marketplace data from refurbed's 24 European storefronts (refurbed.de, .fr, .it, .es, .nl, .co.uk, .ch, .se, .pl, .cz and more). Search or browse the catalogue of refurbished iPhones, Android phones, MacBooks, laptops, tablets, smartwatches, consoles, audio and household appliances, then pull the full product record: price in that country's currency, the new-retail reference price, refurbed's own four appearance grades (Premium / Excellent / Very good / Good) with the price step between them, battery option, every colour and storage variant, full specs, images, rating and the stated warranty and return window. No login, no API key.

> 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.
> ReefAPI engine `refurbed` · 5 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/refurbed/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed calls are free except verified SHEIN NOT_FOUND on product/detail and price (4 credits).
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http). Send the key as `Authorization: Bearer <key>`, or put it in the URL (`?key=<key>`) when the client has no header field, as ChatGPT does.

## Endpoints

### POST https://api.reefapi.com/refurbed/v1/search — 3 credits
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.

**Parameters:**
- `query` (string, optional) — Free-text keyword across the storefront's catalogue (model name, brand, accessory). PASS EITHER `query` OR `category` — with neither you get no results and a MISSING_PARAM telling you so. Combining them is not supported upstream: a category page ignores a keyword, so `category` wins and `meta.warnings` says it.
- `category` (enum, optional) — Browse one refurbed category instead of searching. These are the storefront's own URL slugs; the live list with labels and parent/child nesting is the `categories` action, which is the right place to look if a slug here is missing. [one of: smartphones, iphones, samsung-phones, google-phones, xiaomi-phones, oneplus-phones, oppo-phones, honor-phones, huawei-phones, motorola-phones, sony-phones, nokia-phones, asus-phones, cat-phones, doro-phones, emporia-phones, microsoft-phones, smartphones-accessories, laptops, macbooks, dell-laptops, lenovo-laptops, laptop-accessories, desktops, apple-mac, dell-desktops, hp-desktops, lenovo-desktops, fujitsu-desktops, computer-accessories, a-computer-accessories, tablets, ipads, samsung-tablets, huawei-tablets, hp-tablets, acer-tablets, microsoft-tablets, sony-tablets, tablet-accessories, smartwatches, apple-watches, smartwatch-accessories, audio, headphones, speakers, consoles, playstation, xbox, nintendo, console-accessories, cameras, acoustic-guitars, electric-guitars, bass-guitars, pedals, household, kitchen, kitchen-appliances, large-domestic-appliances, floorcare, air-treatment, household-accessories-other, health-beauty, beverages, breakfast, garden, lawnmowers, powertools, high-pressure-cleaners, chargers-and-charging-cables]
- `country` (enum, optional, default "de") — Which refurbed storefront to read. refurbed runs one catalogue, price list and currency PER COUNTRY, so this changes the numbers you get back, not just the language. Every response echoes the country, locale and currency it actually read in `meta`. Each value in this list answered a live search in this release. [one of: de, at, fr, it, es, nl, be, ie, gb, ch, se, dk, pl, cz, bg, hr, fi, pt, sk, si, lt, lv, ee, lu]
- `brand` (array, optional) — One or more brands, as refurbed spells them (Apple, Samsung, Google, Xiaomi, Lenovo, HP, Dell, Bosch …). Several values are OR-ed. Measured on /c/smartphones/ 2026-10-01: unfiltered 506 lots, brand=Apple 43, brand=Apple,Samsung 172. A value this storefront does not have comes back as a real zero and `meta.warnings` says the storefront did not recognise it — the live value list per category is the `filters` action.
- `colour` (array, optional) — One or more colour buckets as refurbed groups them (Black, White, Blue, Green, Red, Grey, Silver, Gold, Pink, Purple, Beige, Brown, Orange, Turquoise, Yellow, Multi). Several values are OR-ed. Measured: /c/smartphones/ 506 -> colour=Black 313.
- `price_min` (number, optional) — Lowest price to include, in THAT storefront's currency (see `country`). Works on its own. Measured: /c/smartphones/ 506 -> price_min=800 70.
- `price_max` (number, optional) — Highest price to include, in that storefront's currency. Measured: /c/smartphones/ 506 -> price_min=200 + price_max=400 218.
- `sort` (enum, optional, default "popular") — Result order. Measured on /c/smartphones/ 2026-10-01: price_asc's first row was 35.66 EUR and price_desc's was 2844.60 EUR, which are exactly the category's own published lowPrice and highPrice. [one of: popular, price_asc, price_desc]
- `page` (integer, optional, default 1) — Result page, 1-based. refurbed serves 16 rows per page (measured on every page sampled on 2026-10-01) and that page size is the source's, not a parameter. `meta.total_results` is the storefront's own exact match count, and `meta.pagination.has_more` is its own has-more flag.

**Returns:** products[]{product_id, model_id, name, url, slug, brand, department, category, subcategory, variant_label, grade, grade_letter, grade_code, price, price_display, price_feed, price_mismatch, price_new_reference, price_new_reference_display, currency, currency_symbol, grade_url_segment, rating, image, tags[], position} + meta{total_results, country, locale, currency, currency_symbol, page_size, filters_requested, filters_applied_by_source, pagination}}

**Example request body:**
```json
{
  "query": "iphone 13",
  "country": "de"
}
```

### POST https://api.reefapi.com/refurbed/v1/product — 3 credits
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.

**Parameters:**
- `product_id` (integer, required) — refurbed offer id — the digits in a product URL (refurbed.de/en-de/p/iphone-13/14162c/ -> 14162) and the `product_id` of every search row. An id that is not live in that storefront returns NOT_FOUND.
- `country` (enum, optional, default "de") — Which refurbed storefront to read. refurbed runs one catalogue, price list and currency PER COUNTRY, so this changes the numbers you get back, not just the language. Every response echoes the country, locale and currency it actually read in `meta`. Each value in this list answered a live search in this release. [one of: de, at, fr, it, es, nl, be, ie, gb, ch, se, dk, pl, cz, bg, hr, fi, pt, sk, si, lt, lv, ee, lu]
- `slug` (string, required) — The product slug, which refurbed requires alongside the id: it is the segment before the id in a product URL (/p/**iphone-13**/14162c/) and the `slug` of every search row. MEASURED 2026-10-01: refurbed answers HTTP 400 for an id with a slug that does not belong to it and has no id-only route, so this cannot be made optional without handing you a handle that does not work.
- `grade` (enum, optional) — Which appearance grade of the same device to open. These are refurbed's own four appearance categories, not a scale of ours. Omit it and you get the grade the id itself points at; `grade_options` in the response always lists every grade refurbed currently has in stock for that device with its own price difference. refurbed's OWN short forms are accepted too, so a search row's `grade_code` ('aa', '', 'b', 'c') or `grade_letter` ('AA', 'A', 'B', 'C') can be passed straight back to land on exactly the offer that row was about — without it you get refurbed's default grade for that device, which is usually a different price. [one of: premium, excellent, very_good, good]

**Returns:** product{product_id, model_id, name, slug, url, brand, description, breadcrumbs[], price, price_display, price_feed, price_mismatch, currency, price_new_reference, price_new_reference_display, price_valid_until, grade, grade_code, grade_url_segment, grade_options[]{label, selected, price_delta_label, product_id, grade_code, offer_id}, battery, battery_options[], option_groups{}, item_condition, availability, guarantees[], warranty_text, rating, rating_count, shipping_cost, shipping_currency, handling_days_min, handling_days_max, transit_days_min, transit_days_max, return_days, return_fees, specs{}, images[], variants[]{product_id, name, colour, size, price, currency, availability, grade_code, offer_id, url, image}, variant_count} + meta{country, locale, currency}

**Example request body:**
```json
{
  "product_id": 14162,
  "slug": "iphone-13",
  "country": "de"
}
```

### POST https://api.reefapi.com/refurbed/v1/categories — 3 credits
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.

**Parameters:**
- `country` (enum, optional, default "de") — Which refurbed storefront to read. refurbed runs one catalogue, price list and currency PER COUNTRY, so this changes the numbers you get back, not just the language. Every response echoes the country, locale and currency it actually read in `meta`. Each value in this list answered a live search in this release. [one of: de, at, fr, it, es, nl, be, ie, gb, ch, se, dk, pl, cz, bg, hr, fi, pt, sk, si, lt, lv, ee, lu]

**Returns:** categories[]{slug, label, url} + meta{country, locale}

**Example request body:**
```json
{
  "country": "de"
}
```

### POST https://api.reefapi.com/refurbed/v1/filters — 3 credits
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`.

**Parameters:**
- `query` (string, optional) — Free-text keyword across the storefront's catalogue (model name, brand, accessory). PASS EITHER `query` OR `category` — with neither you get no results and a MISSING_PARAM telling you so. Combining them is not supported upstream: a category page ignores a keyword, so `category` wins and `meta.warnings` says it.
- `category` (enum, optional) — Browse one refurbed category instead of searching. These are the storefront's own URL slugs; the live list with labels and parent/child nesting is the `categories` action, which is the right place to look if a slug here is missing. [one of: smartphones, iphones, samsung-phones, google-phones, xiaomi-phones, oneplus-phones, oppo-phones, honor-phones, huawei-phones, motorola-phones, sony-phones, nokia-phones, asus-phones, cat-phones, doro-phones, emporia-phones, microsoft-phones, smartphones-accessories, laptops, macbooks, dell-laptops, lenovo-laptops, laptop-accessories, desktops, apple-mac, dell-desktops, hp-desktops, lenovo-desktops, fujitsu-desktops, computer-accessories, a-computer-accessories, tablets, ipads, samsung-tablets, huawei-tablets, hp-tablets, acer-tablets, microsoft-tablets, sony-tablets, tablet-accessories, smartwatches, apple-watches, smartwatch-accessories, audio, headphones, speakers, consoles, playstation, xbox, nintendo, console-accessories, cameras, acoustic-guitars, electric-guitars, bass-guitars, pedals, household, kitchen, kitchen-appliances, large-domestic-appliances, floorcare, air-treatment, household-accessories-other, health-beauty, beverages, breakfast, garden, lawnmowers, powertools, high-pressure-cleaners, chargers-and-charging-cables]
- `country` (enum, optional, default "de") — Which refurbed storefront to read. refurbed runs one catalogue, price list and currency PER COUNTRY, so this changes the numbers you get back, not just the language. Every response echoes the country, locale and currency it actually read in `meta`. Each value in this list answered a live search in this release. [one of: de, at, fr, it, es, nl, be, ie, gb, ch, se, dk, pl, cz, bg, hr, fi, pt, sk, si, lt, lv, ee, lu]

**Returns:** price_min, price_max, currency_symbol, attributes[]{attribute_id, name, name_localised, type, values[], min, max} + meta{total_results, country}

**Example request body:**
```json
{
  "category": "smartphones",
  "country": "de"
}
```

### POST https://api.reefapi.com/refurbed/v1/compare_countries — 5 credits
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.

**Parameters:**
- `query` (string, optional) — Free-text keyword across the storefront's catalogue (model name, brand, accessory). PASS EITHER `query` OR `category` — with neither you get no results and a MISSING_PARAM telling you so. Combining them is not supported upstream: a category page ignores a keyword, so `category` wins and `meta.warnings` says it.
- `category` (enum, optional) — Browse one refurbed category instead of searching. These are the storefront's own URL slugs; the live list with labels and parent/child nesting is the `categories` action, which is the right place to look if a slug here is missing. [one of: smartphones, iphones, samsung-phones, google-phones, xiaomi-phones, oneplus-phones, oppo-phones, honor-phones, huawei-phones, motorola-phones, sony-phones, nokia-phones, asus-phones, cat-phones, doro-phones, emporia-phones, microsoft-phones, smartphones-accessories, laptops, macbooks, dell-laptops, lenovo-laptops, laptop-accessories, desktops, apple-mac, dell-desktops, hp-desktops, lenovo-desktops, fujitsu-desktops, computer-accessories, a-computer-accessories, tablets, ipads, samsung-tablets, huawei-tablets, hp-tablets, acer-tablets, microsoft-tablets, sony-tablets, tablet-accessories, smartwatches, apple-watches, smartwatch-accessories, audio, headphones, speakers, consoles, playstation, xbox, nintendo, console-accessories, cameras, acoustic-guitars, electric-guitars, bass-guitars, pedals, household, kitchen, kitchen-appliances, large-domestic-appliances, floorcare, air-treatment, household-accessories-other, health-beauty, beverages, breakfast, garden, lawnmowers, powertools, high-pressure-cleaners, chargers-and-charging-cables]
- `countries` (array, required) — 2 to 6 refurbed country codes, e.g. `de,fr,it`. Same values as the `country` enum of the other actions. More than 6 is rejected so one call cannot outgrow its time budget.

**Returns:** comparisons[]{country, locale, currency, currency_symbol, total_results, cheapest{product_id, name, url, price, price_display, grade_letter, grade_code}} + meta{countries_requested, countries_answered, countries_failed[]}

**Example request body:**
```json
{
  "countries": "de,fr,it"
}
```

## At scale
- **Volume:** 5M+ requests a day, measured at 60 requests a second across the fleet with no
  central bottleneck. Per-key limits are raised for high-volume accounts; volume pricing on request.
- **Missing a source:** tell us a site we do not cover and it becomes an engine. A customer asked
  for bestprice.gr on 21 Sep 2026 and it was in the catalog on 22 Sep.
- **Support:** 2 minute median time from a question in the live chat to the first answer. Setup
  help included, no support tier to buy.
- **One key, one credit pool** across every API. No per-site plans, no separate subscriptions.

## More
- Try it live, no code: https://reefapi.com/playground?engine=refurbed
- Human docs page: https://reefapi.com/docs/refurbed
- Overview page: https://reefapi.com/refurbed-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
