# Whoppah — design & vintage furniture resale marketplace (NL + EU)

> Search whoppah's 222,006 second-hand design and vintage listings — furniture, lighting, art, decoration, tableware and designer bags, from sellers in 25 countries — with the source's own filters: free text, category, brand, designer, artist, style/period, material, colour, shape, the item's country, condition, wear grade, delivery method, free-shipping destination, showroom models, bidding, trade discount, price, width / height / depth in cm, lot size, minimum discount and seller. `status=sold` turns it into a sold-comparables search: 36,108 sold listings with their final sale price. Every row carries its own price in EUR, previous price, discount, minimum bid, wear grade, dimensions, photos and the seller's id.
> ReefAPI engine `whoppah` · 6 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/whoppah/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/whoppah/v1/search — 2 credits
Search whoppah's 222,006 second-hand design and vintage listings — furniture, lighting, art, decoration, tableware and designer bags, from sellers in 25 countries — with the source's own filters: free text, category, brand, designer, artist, style/period, material, colour, shape, the item's country, condition, wear grade, delivery method, free-shipping destination, showroom models, bidding, trade discount, price, width / height / depth in cm, lot size, minimum discount and seller. `status=sold` turns it into a sold-comparables search: 36,108 sold listings with their final sale price. Every row carries its own price in EUR, previous price, discount, minimum bid, wear grade, dimensions, photos and the seller's id.

**Parameters:**
- `query` (string, optional) — Free text. Optional: without it you browse by filters (the whole catalogue is 222,006 listings).
- `language` (enum, optional, default "en") — Storefront language for titles, descriptions, category and attribute names, and the listing URL. Prices, filters and counts are identical in every language. Anything else is refused: the source would silently answer in English. [one of: en, nl, de, fr, es, it]
- `match` (enum, optional, default "keyword") — How a `query` is matched. Measured on 'eames': keyword 1,010 rows = its stated total; hybrid stated 1,011 but paged out 1,181 rows, 21 of them repeats. Ignored when there is no query. [one of: keyword, hybrid, semantic]
- `category` (array, optional) — Category slug(s), any level: 'furniture', 'lighting', 'dining-chairs', 'pendant-lamps', 'sofas', 'paintings-drawings' … (the facets action lists them with counts). An unknown slug is refused by name, never answered with zero rows.
- `brand` (array, optional) — Brand slug(s): 'vitra', 'leolux', 'artifort', 'cassina', 'fritz-hansen' …
- `designer` (array, optional) — Designer slug(s): 'eames', 'paulin', 'breuer', 'jacobsen', 'panton' …
- `artist` (array, optional) — Artist slug(s) for art listings: 'andy-warhol', 'corneille', 'herman-brood' …
- `style` (array, optional) — Style / period slug(s): 'vintage', 'mid-century-modern', 'scandinavian', 'art-deco', 'space-age', 'brutalist', '1970' … (52 values).
- `material` (array, optional) — Material slug(s): 'wood', 'metal', 'glass', 'leather', 'rattan', 'murano-glass', 'travertin' … (51 values).
- `color` (array, optional) — Colour slug(s): 'black', 'brown', 'white', 'silver', 'gold', 'beige', 'multi-color', 'blue' … (20 values).
- `country` (array, optional) — Country the item is located in (seller's country), ISO-2. [one of: NL, FR, BE, DE, IT, CZ, ES, PL, GB, HU, DK, AT, CH, PT, SI, SK, SE, SG, RO, IE, LU, LT, HR, MC, US]
- `condition` (array, optional) — Condition as the seller declared it. [one of: used, new, refurbished, openbox]
- `quality` (array, optional) — The source's 3-step wear grade: good · great ('Very good') · perfect ('Excellent'). [one of: good, great, perfect]
- `shipping_method` (array, optional) — Delivery the seller offers: pickup, courier (furniture courier), parcel (post). [one of: pickup, courier, parcel]
- `free_shipping_to` (array, optional) — Destination country (lower-case ISO-2) the seller ships to for free. [one of: nl, be, de, fr, it, es, at, cz, dk, hu, lu, mc, pl, pt, ro, se, si, sk, ch, gb]
- `shape` (array, optional) — Shape (tables, rugs, mirrors). [one of: rectangle, round, square, other, oval, organic, triangle]
- `art_size` (array, optional) — Size class for art. [one of: mini, small, medium, large, x_large]
- `art_orientation` (array, optional) — Orientation for art. [one of: portrait, landscape, square]
- `gender` (array, optional) — For bags and fashion. [one of: unisex, women, men]
- `showroom_model` (boolean, optional) — true = only ex-showroom pieces; false = exclude them.
- `bidding_allowed` (boolean, optional) — true = only listings that accept bids; false = buy-now only.
- `trade_discount` (boolean, optional) — true = only sellers offering the source's trade (pro buyer) discount.
- `min_price` (number, optional) — Lowest buy-now price, EUR.
- `max_price` (number, optional) — Highest buy-now price, EUR.
- `min_width` (number, optional) — Minimum width, cm.
- `max_width` (number, optional) — Maximum width, cm.
- `min_height` (number, optional) — Minimum height, cm.
- `max_height` (number, optional) — Maximum height, cm.
- `min_depth` (number, optional) — Minimum depth, cm.
- `max_depth` (number, optional) — Maximum depth, cm.
- `min_items` (number, optional) — Minimum lot size (a set of 6 chairs = 6).
- `max_items` (number, optional) — Maximum lot size.
- `min_discount` (number, optional) — Minimum discount the source shows, percent (0-100).
- `seller` (string, optional) — Only this seller's listings (the `seller_id` UUID a row returns).
- `status` (enum, optional, default "all") — Whoppah keeps sold listings searchable with their final sale price — 36,108 of them on 2026-10-14. 'sold' returns only those (sold comparables); 'available' only what can still be bought. [one of: all, available, sold]
- `sort` (enum, optional, default "relevance") — The site's own four orderings. Anything else is refused, because the source would silently fall back to relevance. [one of: relevance, newest, price_asc, price_desc]
- `page` (integer, optional, default 1) — Result page, 1-based.
- `page_size` (integer, optional, default 24) — Rows per page (1-100, default 24 as on the site). The source itself caps a page at 250.
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility. This engine returns what the source publishes (seller display name and business name); no masking is applied.

**Returns:** {total, total_is_exact, page, page_size, pages, has_more, language, match, sort, status, query, filters, listings[{id, identifier, slug, url, title, description, currency, price_eur, previous_price_eur, discount_pct, min_bid_eur, sold, sold_price_eur, on_hold_until, condition, quality, quality_label, number_of_items, dimensions_cm{width, height, depth}, dimension_warnings, shape, gender, is_art, is_curated, is_boosted, is_showroom_model, brand_id, seller_id, image, images[], created_at, updated_at, filter_mismatch?}]}

**Example request body:**
```json
{
  "query": "eames",
  "language": "en",
  "page_size": 24
}
```

### POST https://api.reefapi.com/whoppah/v1/detail — 1 credit
One listing in full from whoppah's own API: title and description in the requested language AND the seller's original wording, wear notes, status (for sale / sold, with the final sale price), every price the source keeps (buy-now, previous, new retail, minimum bid, bid count), condition and wear grade, width / height / depth / seat height in cm and weight in kg, lot size, category path, brand / designer / artist / colour / material / style records, the item's city and country, delivery options with the listing's own courier price per destination country, every photo, view and favourite counts, and the seller (name, business name, private or business, rating, reviews, listings and sales counts, member since).

**Parameters:**
- `id` (string, required) — The listing: the `id` (UUID) a search row returns, the listing's slug in any language, or its whoppah.com product URL.
- `language` (enum, optional, default "en") — Storefront language for titles, descriptions, category and attribute names, and the listing URL. Prices, filters and counts are identical in every language. Anything else is refused: the source would silently answer in English. [one of: en, nl, de, fr, es, it]
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility. This engine returns what the source publishes (seller display name and business name); no masking is applied.

**Returns:** {listing{id, identifier, slug, url, urls{en..it}, title, original_title, description, original_description, usage_signs, status, sold, price_eur, last_asking_price_eur, previous_price_eur, new_price_eur, min_bid_eur, sold_price_eur, bidding{...}, condition, quality, quality_label, number_of_items, dimensions_cm{width, height, depth, seat_height}, weight_kg, category{path, names, route}, brands[], designers[], artists[], colors[], materials[], styles[], labels[], location{city, country}, delivery{method, two_man_delivery, ground_floor_only, listed_courier_prices[{country, price_eur}], external_methods[]}, seller{id, display_name, name, business_name, kind, rating, reviews_count, listings_count, sales_count, member_since, countries}, images[], view_count, favorite_count, created_at, updated_at}}

**Example request body:**
```json
{
  "id": "2eee8387-a209-4fc5-ae19-45c4cf5f7cdd",
  "language": "en"
}
```

### POST https://api.reefapi.com/whoppah/v1/shipping — 1 credit
The live delivery quote whoppah's checkout gives for one listing to a destination YOU name — country, and optionally postcode, city and street for the address-priced courier — with each option's price, provider, delivery-day window and whether it is supported. The listing's own per-country courier grid is returned beside the quote with `grid_matches_quote`, because the two disagree for some destinations (measured: DE grid 278 vs quote 182, FR 309 vs 277; NL, BE, AT equal). The quote depends only on the address you pass, never on where the call comes from.

**Parameters:**
- `id` (string, required) — The listing: the `id` (UUID) a search row returns, the listing's slug in any language, or its whoppah.com product URL.
- `country` (string, required) — Destination country, ISO-2 (NL, DE, FR, BE, GB …).
- `postal_code` (string, optional) — Destination postcode. With city and street it prices the address-based courier option, which otherwise answers price_needs: full_address.
- `city` (string, optional) — Destination city.
- `street` (string, optional) — Destination street and number.
- `language` (enum, optional, default "en") — Storefront language for titles, descriptions, category and attribute names, and the listing URL. Prices, filters and counts are identical in every language. Anything else is refused: the source would silently answer in English. [one of: en, nl, de, fr, es, it]
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility. This engine returns what the source publishes (seller display name and business name); no masking is applied.

**Returns:** {listing_id, identifier, status, destination{country, postal_code, city}, options[{method, provider, price_eur, price_needs, delivery_type, delivery_days_min, delivery_days_max, supported}], listed_courier_price_eur, quoted_courier_price_eur, grid_matches_quote}

**Example request body:**
```json
{
  "id": "1e657853-67ca-48bc-b084-9d0e539150f2",
  "country": "DE"
}
```

### POST https://api.reefapi.com/whoppah/v1/similar — 2 credits
The listings whoppah itself shows as related to a given one (same maker, category or style), each with price, previous price, minimum bid, wear grade, sold flag and photo — the 'what else like this is on the market, and at what price' view.

**Parameters:**
- `id` (string, required) — The listing: the `id` (UUID) a search row returns, the listing's slug in any language, or its whoppah.com product URL.
- `limit` (integer, optional, default 12) — How many related listings (1-24).
- `language` (enum, optional, default "en") — Storefront language for titles, descriptions, category and attribute names, and the listing URL. Prices, filters and counts are identical in every language. Anything else is refused: the source would silently answer in English. [one of: en, nl, de, fr, es, it]
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility. This engine returns what the source publishes (seller display name and business name); no masking is applied.

**Returns:** {listing_id, count, listings[{id, identifier, slug, url, title, price_eur, previous_price_eur, min_bid_eur, sold, sold_price_eur, condition, quality, quality_label, is_designer_item, is_curated, category_path[], image}]}

**Example request body:**
```json
{
  "id": "2eee8387-a209-4fc5-ae19-45c4cf5f7cdd",
  "limit": 12
}
```

### POST https://api.reefapi.com/whoppah/v1/seller — 3 credits
A seller's public profile — display name, business name, private or business, expert-seller badge, rating, review count, live listings and completed sales counts, member since, countries — plus a page of that seller's listings with the same row shape as search (`status` lets you ask for their sold history).

**Parameters:**
- `id` (string, required) — The seller's id (the `seller_id` UUID a row returns).
- `include_listings` (boolean, optional, default true) — Also return a page of the seller's listings.
- `status` (enum, optional, default "all") — Which of the seller's listings. [one of: all, available, sold]
- `sort` (enum, optional, default "newest") — Order of the seller's listings. [one of: relevance, newest, price_asc, price_desc]
- `page` (integer, optional, default 1) — Result page, 1-based.
- `page_size` (integer, optional, default 24) — Rows per page (1-100, default 24 as on the site). The source itself caps a page at 250.
- `language` (enum, optional, default "en") — Storefront language for titles, descriptions, category and attribute names, and the listing URL. Prices, filters and counts are identical in every language. Anything else is refused: the source would silently answer in English. [one of: en, nl, de, fr, es, it]
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility. This engine returns what the source publishes (seller display name and business name); no masking is applied.

**Returns:** {seller{id, display_name, name, business_name, kind, type, expert_seller, rating, reviews_count, listings_count, sales_count, member_since, on_vacation_since, contact_disabled, avatar, countries[]}, listings_total, listings[...search row...]}

**Example request body:**
```json
{
  "id": "75c7bf38-aca0-426f-b6b8-5965fc4dd141",
  "page_size": 10
}
```

### POST https://api.reefapi.com/whoppah/v1/facets — 2 credits
The source's own filter values with its own live counts, for any scope you can search — the whole catalogue, a free-text query, or any combination of the search filters: categories, brands, designers, artists, styles, materials, colours, seller countries, conditions, wear grades, delivery methods, free-shipping destinations, and the price / width / height / depth ranges. This is where the slugs the search filters take come from. The source lists at most the top 100 values of a facet.

**Parameters:**
- `query` (string, optional) — Optional free text to scope the counts.
- `language` (enum, optional, default "en") — Storefront language for titles, descriptions, category and attribute names, and the listing URL. Prices, filters and counts are identical in every language. Anything else is refused: the source would silently answer in English. [one of: en, nl, de, fr, es, it]
- `match` (enum, optional, default "keyword") — How a `query` is matched (see search). [one of: keyword, hybrid, semantic]
- `category` (array, optional) — Category slug(s), any level: 'furniture', 'lighting', 'dining-chairs', 'pendant-lamps', 'sofas', 'paintings-drawings' … (the facets action lists them with counts). An unknown slug is refused by name, never answered with zero rows.
- `brand` (array, optional) — Brand slug(s): 'vitra', 'leolux', 'artifort', 'cassina', 'fritz-hansen' …
- `designer` (array, optional) — Designer slug(s): 'eames', 'paulin', 'breuer', 'jacobsen', 'panton' …
- `artist` (array, optional) — Artist slug(s) for art listings: 'andy-warhol', 'corneille', 'herman-brood' …
- `style` (array, optional) — Style / period slug(s): 'vintage', 'mid-century-modern', 'scandinavian', 'art-deco', 'space-age', 'brutalist', '1970' … (52 values).
- `material` (array, optional) — Material slug(s): 'wood', 'metal', 'glass', 'leather', 'rattan', 'murano-glass', 'travertin' … (51 values).
- `color` (array, optional) — Colour slug(s): 'black', 'brown', 'white', 'silver', 'gold', 'beige', 'multi-color', 'blue' … (20 values).
- `country` (array, optional) — Country the item is located in (seller's country), ISO-2. [one of: NL, FR, BE, DE, IT, CZ, ES, PL, GB, HU, DK, AT, CH, PT, SI, SK, SE, SG, RO, IE, LU, LT, HR, MC, US]
- `condition` (array, optional) — Condition as the seller declared it. [one of: used, new, refurbished, openbox]
- `quality` (array, optional) — The source's 3-step wear grade: good · great ('Very good') · perfect ('Excellent'). [one of: good, great, perfect]
- `shipping_method` (array, optional) — Delivery the seller offers: pickup, courier (furniture courier), parcel (post). [one of: pickup, courier, parcel]
- `free_shipping_to` (array, optional) — Destination country (lower-case ISO-2) the seller ships to for free. [one of: nl, be, de, fr, it, es, at, cz, dk, hu, lu, mc, pl, pt, ro, se, si, sk, ch, gb]
- `shape` (array, optional) — Shape (tables, rugs, mirrors). [one of: rectangle, round, square, other, oval, organic, triangle]
- `art_size` (array, optional) — Size class for art. [one of: mini, small, medium, large, x_large]
- `art_orientation` (array, optional) — Orientation for art. [one of: portrait, landscape, square]
- `gender` (array, optional) — For bags and fashion. [one of: unisex, women, men]
- `showroom_model` (boolean, optional) — true = only ex-showroom pieces; false = exclude them.
- `bidding_allowed` (boolean, optional) — true = only listings that accept bids; false = buy-now only.
- `trade_discount` (boolean, optional) — true = only sellers offering the source's trade (pro buyer) discount.
- `min_price` (number, optional) — Lowest buy-now price, EUR.
- `max_price` (number, optional) — Highest buy-now price, EUR.
- `min_width` (number, optional) — Minimum width, cm.
- `max_width` (number, optional) — Maximum width, cm.
- `min_height` (number, optional) — Minimum height, cm.
- `max_height` (number, optional) — Maximum height, cm.
- `min_depth` (number, optional) — Minimum depth, cm.
- `max_depth` (number, optional) — Maximum depth, cm.
- `min_items` (number, optional) — Minimum lot size (a set of 6 chairs = 6).
- `max_items` (number, optional) — Maximum lot size.
- `min_discount` (number, optional) — Minimum discount the source shows, percent (0-100).
- `seller` (string, optional) — Only this seller's listings (the `seller_id` UUID a row returns).
- `status` (enum, optional, default "all") — Whoppah keeps sold listings searchable with their final sale price — 36,108 of them on 2026-10-14. 'sold' returns only those (sold comparables); 'available' only what can still be bought. [one of: all, available, sold]
- `facet` (array, optional) — Return only these facets (keys as the response names them: category, brand, designer, artist, style, material, color, country, condition, quality, shipping_method, free_shipping, price, width, height, depth, …). Omit for all.
- `include_pii` (boolean, optional, default false) — Accepted for gateway compatibility. This engine returns what the source publishes (seller display name and business name); no masking is applied.

**Returns:** {total, query, language, facets{<key>: {type, values[{value, label, id, count, selected}] | min, max, unit}}}

**Example request body:**
```json
{
  "category": [
    "lighting"
  ],
  "facet": [
    "brand",
    "designer",
    "price"
  ],
  "language": "en"
}
```

## 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=whoppah
- Human docs page: https://reefapi.com/docs/whoppah
- Overview page: https://reefapi.com/whoppah-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
