# Douglas API scraper — Europe's largest beauty & perfume retailer (douglas.*): search 13 countries, read the full product (variants/sizes, price per 100 ml, was-price, EU 30-day lowest price, EAN, ingredients, specs, rating, images), and autocomplete. No account, no browser.

> Search a Douglas storefront by keyword and/or category, with the site's own sorts and filters (brand, category, price range, on-sale). Each row: product + variant code, name, brand, the logged-out price, the struck was-price + discount, the EU 30-day lowest price, the per-100 ml/per-litre base price, rating + count, availability/stock, sale/new/exclusive flags, marketplace seller, images and images. EAN and ingredients are on product/detail. No-result searches return empty (the site's own 204); a keyword→category/brand redirect is reported and the real rows are kept.
> ReefAPI engine `douglas` · 3 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/douglas/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, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /douglas/v1/search — 1 credit
Search a Douglas storefront by keyword and/or category, with the site's own sorts and filters (brand, category, price range, on-sale). Each row: product + variant code, name, brand, the logged-out price, the struck was-price + discount, the EU 30-day lowest price, the per-100 ml/per-litre base price, rating + count, availability/stock, sale/new/exclusive flags, marketplace seller, images and images. EAN and ingredients are on product/detail. No-result searches return empty (the site's own 204); a keyword→category/brand redirect is reported and the real rows are kept.

**Parameters:**
- `country` (enum, optional, default "de") — Douglas storefront. All 13 run on one platform; currency + language follow the country. [one of: de, at, ch, nl, be, pl, it, es, cz, sk, hu, ro, lt]
- `query` (string, optional) — Keyword to search (brand, product or product-type words). Optional when `category` is given (category browse).
- `language` (string, optional) — Interface language for multi-language countries (ch: de/fr/it, be: nl/fr). Ignored elsewhere; defaults to the country's primary language.
- `sort` (enum, optional, default "relevance") — Result ordering. [one of: relevance, price_asc, price_desc, top_rated, newest, best_discount]
- `page` (integer, optional, default 1) — 1-based page; 48 products per page.
- `brand` (string, optional) — Brand facet code (from a search response's facets, e.g. 'b0102' for Hugo Boss).
- `category` (string, optional) — Category code (allCategories facet, e.g. '0301'). Browse it alone, or narrow a keyword with it.
- `price_min` (number, optional) — Minimum price in the country currency.
- `price_max` (number, optional) — Maximum price in the country currency.
- `on_sale` (boolean, optional, default false) — Only discounted products.
- `include_facets` (boolean, optional, default false) — Also return the filter facets (brands, categories, properties) with their codes.
- `max_rotations` (integer, optional, default 6) — Max proxy attempts on hardened storefronts (DE/PL/IT). Ignored elsewhere.

**Returns:** results[]{product_code, base_product_code, variant_name, product_name, brand, brand_code, brand_line, url, price, currency, was_price, discount_percent, lowest_price_30d, lowest_price_30d_discount_percent, store_price, member_price, base_price{value, per_amount, per_unit, content_amount, content_unit}, rating, rating_count, rating_scale, availability{code, message, stock_status}, product_type, is_new, on_sale, flags[{code,label}], seller{marketplace,name,partner_id,fulfilled_by_douglas}, image, images}, count, total_results, total_pages, page, page_size, sort, query, country, currency, redirect_to, did_you_mean, corrected_from, search_mode ('search'|'suggest_index'), partial, facets (with include_facets)

**Example request body:**
```json
{
  "country": "at",
  "query": "dior sauvage"
}
```

### POST /douglas/v1/product/detail — 1 credit
The full Douglas product by code or URL: title, brand, price with was-price/discount and the EU 30-day lowest price, per-100 ml base price, the Douglas coupon/deal price when one runs, availability, rating, images, EAN, ingredients, application, bullet points, the specification table, the category breadcrumb, marketplace seller, and every size/colour variant each with its own price and base price. (On DE, when the full route is Akamai-gated, a listing-mode record is returned with ean/ingredients/specs null and partial=true.)

**Parameters:**
- `country` (enum, optional, default "de") — Douglas storefront. All 13 run on one platform; currency + language follow the country. [one of: de, at, ch, nl, be, pl, it, es, cz, sk, hu, ro, lt]
- `product_id` (string, optional) — Douglas product code (a variant code like '1298545' or a base code like '5012091068').
- `url` (string, optional) — A douglas.* product URL (…/p/<code>) instead of product_id.
- `language` (string, optional) — Interface language for multi-language countries (ch: de/fr/it, be: nl/fr). Ignored elsewhere; defaults to the country's primary language.
- `max_rotations` (integer, optional, default 6) — Max proxy attempts on hardened storefronts (DE/PL/IT). Ignored elsewhere.

**Returns:** product{...all search-row fields..., ean, ingredients, application, bullet_points, specs[{group,name,value}], breadcrumb[{name,code,url}], coupon{code,price,base_price,discount_percent,starts_at,ends_at}, variant_display_type, variant_count, variants[{product_code,name,url,price,was_price,discount_percent,lowest_price_30d,store_price,base_price,availability,coupon,images}], max_order_quantity, mode, partial}

**Example request body:**
```json
{
  "country": "at",
  "product_id": "1078984"
}
```

### POST /douglas/v1/suggest — 1 credit
Douglas autocomplete for a partial query: product suggestions (code, variant name, product name, brand, url, image) and the ranked keyword/brand suggestions.

**Parameters:**
- `query` (string, optional) — Keyword to search (brand, product or product-type words). Optional when `category` is given (category browse).
- `country` (enum, optional, default "de") — Douglas storefront. All 13 run on one platform; currency + language follow the country. [one of: de, at, ch, nl, be, pl, it, es, cz, sk, hu, ro, lt]
- `language` (string, optional) — Interface language for multi-language countries (ch: de/fr/it, be: nl/fr). Ignored elsewhere; defaults to the country's primary language.
- `max_rotations` (integer, optional, default 6) — Max proxy attempts on hardened storefronts (DE/PL/IT). Ignored elsewhere.

**Returns:** suggestions[{product_code, variant_name, product_name, brand, url, image}], keyword_suggestions[{term, url}], count, country

**Example request body:**
```json
{
  "country": "at",
  "query": "chanel"
}
```

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