# ManoMano — European DIY & home-improvement marketplace

> Keyword search across a ManoMano storefront. Returns the product rows with price (with and without VAT), reference price, seller, EAN, rating, delivery flags and the technical attribute ids.
> ReefAPI engine `manomano` · 8 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/manomano/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 /manomano/v1/search — 1 credit
Keyword search across a ManoMano storefront. Returns the product rows with price (with and without VAT), reference price, seller, EAN, rating, delivery flags and the technical attribute ids.

**Parameters:**
- `query` (string, required) — Search terms, in the storefront's own language.
- `country` (enum, optional, default "fr") — Which storefront to search. They are different shops: different catalogue, sellers, prices and currency. [one of: fr, de, es, it, gb]
- `page` (integer, optional, default 1) — 1-based page number.
- `limit` (integer, optional, default 24) — Rows per page (max 100).
- `sort` (enum, optional, default "relevance") — Sort order. Non-relevance sorts use the source's own replica indexes and report their own totals. [one of: relevance, price_asc, price_desc, rating_desc]
- `brand` (string, optional) — Exact brand name as the source spells it.
- `seller` (string, optional) — Exact merchant display name.
- `category_id` (integer, optional) — Restrict to a category id at any depth (ids from category/tree).
- `min_price` (number, optional) — Minimum price, VAT included, storefront currency.
- `max_price` (number, optional) — Maximum price, VAT included.
- `min_rating` (number, optional) — Minimum customer rating. Note ~46 % of rows carry a rating at all, and that varies sharply by category.
- `free_delivery` (boolean, optional) — Only offers with free delivery.
- `relay_delivery` (boolean, optional) — Only offers deliverable to a pickup point.
- `manomano_fulfilment` (boolean, optional) — Only offers shipped by ManoMano Fulfilment.
- `eco_responsible` (boolean, optional) — Only offers the source flags as eco-responsible.
- `in_promo` (boolean, optional) — Only offers currently running a promotion.
- `attributes` (array, optional) — Technical attribute filters as '<attribute_id>:<value>'. Ids and their labels come from search/filters.
- `resolve_attributes` (boolean, optional, default false) — Attach human-readable names to each row's attribute ids. Costs one extra upstream request.

**Returns:** {results: [...], total_estimated, total_is_exact, page, per_page, query, country, currency}

**Example request body:**
```json
{
  "query": "perceuse",
  "country": "fr"
}
```

### POST /manomano/v1/product/detail — 2 credits
Full product record: price with and without VAT, reference price, eco-participation, EAN, brand, seller, live stock, delivery promise, the technical attribute table, images, documents and the Q&A. Pass model_id to pin the exact variant a search row referred to.

**Parameters:**
- `model_id` (string, optional) — The variant key, and the one to prefer: search returns it as model_id and it identifies exactly one variant.
- `product_id` (string, optional) — Offer/product id from search. Resolved upstream to its variant; never pasted into a URL.
- `article_id` (integer, optional) — The article id a product URL ends with. An article can group several variants (up to 7 seen), so on its own it returns the article's DEFAULT variant — check variants_count and pass model_id to pin one.
- `url` (string, optional) — A ManoMano product URL; the trailing id is used.
- `country` (enum, optional, default "fr") [one of: fr, de, es, it, gb]
- `postal_code` (string, optional) — Compute the delivery promise for this postcode. Without it the source uses its own default.
- `with_delivery` (boolean, optional, default true) — Include the delivery promise (carriers and date windows). One extra upstream request.

**Returns:** {product: {...}} — a single product record

**Example request body:**
```json
{
  "article_id": 91537520,
  "country": "fr"
}
```

### POST /manomano/v1/product/offers — 1 credit
Every competing merchant selling one product, with that merchant's own price, VAT split, eco-participation, live stock, minimum quantity and delivery window. This is the marketplace view.

**Parameters:**
- `product_id` (string, optional) — Product id from search (`product_id`).
- `article_id` (integer, optional) — Article id; resolved to the product id upstream.
- `model_id` (string, optional) — Model id; resolved to the product id upstream.
- `url` (string, optional) — A ManoMano product URL.
- `country` (enum, optional, default "fr") [one of: fr, de, es, it, gb]

**Returns:** {product_id, competing_offers_count, offers: [...]} sorted cheapest first

**Example request body:**
```json
{
  "product_id": "165183914",
  "country": "fr"
}
```

### POST /manomano/v1/product/reviews — 1 credit
Customer reviews for a product: rating, text, date, reviewer display name and country, votes and photos.

**Parameters:**
- `article_id` (integer, optional) — Article id (from search or product/detail).
- `url` (string, optional) — A ManoMano product URL; the trailing id is used.
- `country` (enum, optional, default "fr") [one of: fr, de, es, it, gb]
- `page` (integer, optional, default 1)
- `limit` (integer, optional, default 20)
- `sort` (enum, optional, default "newest") [one of: newest, oldest, rating_desc, rating_asc, most_voted]
- `b2b_only` (boolean, optional, default false) — Only reviews left by trade customers.

**Returns:** {reviews: [...], total, offset, limit, has_next_page}

### POST /manomano/v1/category/browse — 2 credits
Browse a category's product grid as the storefront renders it, with price, seller, rating and stock per row. Paid placements are dropped and counted unless you ask for them.

**Parameters:**
- `category_id` (integer, required) — Category id from category/tree or a search row.
- `category_slug` (string, optional) — Optional slug; the id is what resolves.
- `country` (enum, optional, default "fr") [one of: fr, de, es, it, gb]
- `page` (integer, optional, default 1) — 1-based page. The source caps a category at 6 000 products (100 pages of 56).
- `include_sponsored` (boolean, optional, default false) — Return paid placements too, each flagged is_sponsored.

**Returns:** {category, results: [...], page, per_page, total_estimated, total_reachable, sponsored_dropped}

### POST /manomano/v1/category/tree — 1 credit
The storefront's category tree with ids and slugs, up to three levels.

**Parameters:**
- `country` (enum, optional, default "fr") [one of: fr, de, es, it, gb]
- `depth` (integer, optional, default 2) — How many levels to return.

**Returns:** {categories: [{id, name, slug, children: [...]}]}

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

### POST /manomano/v1/search/filters — 1 credit
The filters the storefront itself offers for a query, including the technical attribute id -> name table (with units) that turns a search row's numeric attribute ids into readable specs.

**Parameters:**
- `query` (string, required) — The query whose filter set you want.
- `country` (enum, optional, default "fr") [one of: fr, de, es, it, gb]
- `limit` (integer, optional, default 40) — How many attribute filters to return.

**Returns:** {attribute_filters: [...], general_filters: [...]}

**Example request body:**
```json
{
  "query": "perceuse"
}
```

### POST /manomano/v1/search/suggest — 1 credit
Autocomplete straight from the storefront: query suggestions, brands, categories and advice articles for a prefix.

**Parameters:**
- `query` (string, required) — A prefix, as typed in the search box.
- `country` (enum, optional, default "fr") [one of: fr, de, es, it, gb]
- `limit` (integer, optional, default 8)

**Returns:** {suggestions: {query: [...], brand: [...], category: [...], ...}}

**Example request body:**
```json
{
  "query": "perceuse"
}
```

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