# Mercadona API scraper — Spain's largest supermarket online shop (tienda.mercadona.es): search products, read the full product record with shelf price, price drops, unit price, VAT, EAN, ingredients and allergens, browse every category and the price-drop list, priced for any Spanish postal code. Prices in EUR. No account, no browser.

> Search Mercadona's online shop by keyword, priced for a Spanish postal code. Each product comes with its shelf price, the previous price when it just dropped, the unit price per kg / litre / unit, the pack size, VAT rate, EAN, brand, category path and availability. Filter by brand or category and sort by price, unit price or price drop. Mercadona returns at most 60 products per query.
> ReefAPI engine `mercadona` · 6 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/mercadona/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 /mercadona/v1/search — 1 credit
Search Mercadona's online shop by keyword, priced for a Spanish postal code. Each product comes with its shelf price, the previous price when it just dropped, the unit price per kg / litre / unit, the pack size, VAT rate, EAN, brand, category path and availability. Filter by brand or category and sort by price, unit price or price drop. Mercadona returns at most 60 products per query.

**Parameters:**
- `query` (string, required) — What to look for, as a shopper would type it on tienda.mercadona.es ('leche', 'aceite de oliva', 'Hacendado'). Search in the language you pass in `lang` ('milk' with lang=en). Typos are tolerated.
- `postal_code` (string, optional) — Spanish postal code of the delivery address. Prices and assortment depend on the warehouse that serves it (Canary Islands prices differ). A postal code Mercadona does not deliver to is rejected. Takes precedence over `warehouse`. Default: Mercadona's own default area (Valencia, warehouse vlc1).
- `warehouse` (string, optional) — Advanced: a Mercadona warehouse id ('vlc1', 'mad3', 'bcn1', '4701') instead of a postal code. Every answer reports the warehouse used; `warehouse/lookup` maps a postal code to it.
- `lang` (enum, optional, default "es") — Language of product names and categories. [one of: es, en, ca, vai, eu]
- `page` (integer, optional, default 1) — Result page, 1-based.
- `page_size` (integer, optional, default 50) — Products per page (1-200, default 50).
- `sort` (enum, optional) — Order of the rows. Leave empty for Mercadona's own order (relevance for search, shelf order for categories). Applied over the complete list Mercadona returns. [one of: price_asc, price_desc, unit_price_asc, unit_price_desc, discount_desc, name_asc]
- `brands` (array, optional) — Only these brands (exact names, case-insensitive; see `filters_available.brands`). Several brands are OR-ed. Comma-separated text works too.
- `category_id` (string, optional) — Only products inside this Mercadona category (any level; ids from `categories` or `filters_available.categories`).
- `include_fallback_results` (boolean, optional, default false) — For things Mercadona does not sell, its search returns loosely related products (e.g. perfumes for 'zapatillas nike'). false (default) drops them: `results` stays empty and `fallback_results_dropped` counts them; true returns them separately in `fallback_results`, never in `results`.
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** results[]{product_id (string; can contain a dot, e.g. '81661.2'), title, brand (null on category/deals rows — Mercadona omits it there), ean (search rows and detail), packaging, main_feature, category{id,name,level}, breadcrumb[], category_paths[][], url, slug, image, price (number, EUR, the shelf price; for weighed counter items the price of the minimum quantity), price_display ('5,04 €'), currency, original_price (the previous price when Mercadona shows a price drop, else null), original_price_display, discount_percent, save_amount, price_dropped, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (always null — Mercadona has no loyalty price), unit_price, unit_price_unit ('L' | 'kg' | '100 g' | '100 ml' | 'ud' | 'dz' | 'dc' | 'lv' | 'm'), unit_price_measure ('litre', 'kg', 'unit', 'dozen', 'wash', 'metre' …), unit_price_display ('0,84 €/L'), sold_by ('unit' | 'weight'), price_per_quantity + price_per_quantity_unit (weighed items: 0.15 kg), order_increment, price_is_estimate (true = piece of approximate weight), size ('6 briks x 1 L'), size_value, size_unit, pack_units, pack_unit_name, drained_weight, tax_percent (VAT/IGIC included in the price), is_new_price, availability ('in_stock' | 'out_of_stock' | 'unavailable_in_warehouse' | 'unavailable'), in_stock, status, unavailable_from, unavailable_weekdays[], max_order_quantity, age_restricted, is_water, is_new_arrival, rating/review_count/rating_scale (null — not published), sponsored (always false)}, count, total_results, page, page_size, page_count, filters_available{brands[]{name,count}, categories[]{id,name,level,count}}, sort_applied, area{postal_code, warehouse, source ('postal_code' | 'warehouse' | 'default')}, lang, query, search_provider ('mercadona' | 'algolia'), fallback_results_dropped, fallback_results[], keyword_index_hits (set only when no row contained a query word: the shop's keyword index was asked whether the rows are genuine synonyms)

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

### POST /mercadona/v1/product/detail — 1 credit
The full Mercadona product record by `product_id` (or product URL), priced for a postal code: title, brand, EAN, every image, shelf price, previous price and saving, unit price, pack size, VAT rate, availability and order limit, legal name, ingredients, allergens, storage and usage instructions, origin, suppliers, alcohol by volume, hazard statements and colour variants.

**Parameters:**
- `product_id` (string, required) — Mercadona product id (the value every search row returns as `product_id`, e.g. '10381' or '81661.2'), or a tienda.mercadona.es/product/… URL.
- `postal_code` (string, optional) — Spanish postal code of the delivery address. Prices and assortment depend on the warehouse that serves it (Canary Islands prices differ). A postal code Mercadona does not deliver to is rejected. Takes precedence over `warehouse`. Default: Mercadona's own default area (Valencia, warehouse vlc1).
- `warehouse` (string, optional) — Advanced: a Mercadona warehouse id ('vlc1', 'mad3', 'bcn1', '4701') instead of a postal code. Every answer reports the warehouse used; `warehouse/lookup` maps a postal code to it.
- `lang` (enum, optional, default "es") — Language of product names and categories. [one of: es, en, ca, vai, eu]
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** product{product_id (string; can contain a dot, e.g. '81661.2'), title, brand (null on category/deals rows — Mercadona omits it there), ean (search rows and detail), packaging, main_feature, category{id,name,level}, breadcrumb[], category_paths[][], url, slug, image, price (number, EUR, the shelf price; for weighed counter items the price of the minimum quantity), price_display ('5,04 €'), currency, original_price (the previous price when Mercadona shows a price drop, else null), original_price_display, discount_percent, save_amount, price_dropped, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (always null — Mercadona has no loyalty price), unit_price, unit_price_unit ('L' | 'kg' | '100 g' | '100 ml' | 'ud' | 'dz' | 'dc' | 'lv' | 'm'), unit_price_measure ('litre', 'kg', 'unit', 'dozen', 'wash', 'metre' …), unit_price_display ('0,84 €/L'), sold_by ('unit' | 'weight'), price_per_quantity + price_per_quantity_unit (weighed items: 0.15 kg), order_increment, price_is_estimate (true = piece of approximate weight), size ('6 briks x 1 L'), size_value, size_unit, pack_units, pack_unit_name, drained_weight, tax_percent (VAT/IGIC included in the price), is_new_price, availability ('in_stock' | 'out_of_stock' | 'unavailable_in_warehouse' | 'unavailable'), in_stock, status, unavailable_from, unavailable_weekdays[], max_order_quantity, age_restricted, is_water, is_new_arrival, rating/review_count/rating_scale (null — not published), sponsored (always false), images[], image_set[]{zoom,regular,thumbnail,perspective}, legal_name, description, origin, suppliers[], ingredients, allergens, storage, usage, mandatory_mentions, danger_mentions, counter_info, production_variant, alcohol_by_volume, alcohol_by_volume_text, prepared_by_mercadona, extra_info[], is_bulk, is_variable_weight, variant_count, variants[]{product_id, variant_type ('color'), name, color_hex, status, in_stock, url}, nutrition (null — Mercadona publishes no nutrition table)}, area{postal_code, warehouse, source ('postal_code' | 'warehouse' | 'default')}, lang

**Example request body:**
```json
{
  "product_id": "10381"
}
```

### POST /mercadona/v1/category — 1 credit
Every product of one Mercadona category (ids from `categories`), with its sub-categories, priced for a postal code. Same rows as search; filter by sub-category, sort by price or unit price.

**Parameters:**
- `category_id` (string, required) — A category id from `categories` (the second level, e.g. 112 = Aceite, vinagre y sal), or a tienda.mercadona.es/categories/… URL.
- `subcategory_id` (string, optional) — Optional: only this sub-category of the category (ids in `subcategories`).
- `postal_code` (string, optional) — Spanish postal code of the delivery address. Prices and assortment depend on the warehouse that serves it (Canary Islands prices differ). A postal code Mercadona does not deliver to is rejected. Takes precedence over `warehouse`. Default: Mercadona's own default area (Valencia, warehouse vlc1).
- `warehouse` (string, optional) — Advanced: a Mercadona warehouse id ('vlc1', 'mad3', 'bcn1', '4701') instead of a postal code. Every answer reports the warehouse used; `warehouse/lookup` maps a postal code to it.
- `lang` (enum, optional, default "es") — Language of product names and categories. [one of: es, en, ca, vai, eu]
- `page` (integer, optional, default 1) — Result page, 1-based.
- `page_size` (integer, optional, default 50) — Products per page (1-200, default 50).
- `sort` (enum, optional) — Order of the rows. Leave empty for Mercadona's own order (relevance for search, shelf order for categories). Applied over the complete list Mercadona returns. [one of: price_asc, price_desc, unit_price_asc, unit_price_desc, discount_desc, name_asc]
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** results[]{product_id (string; can contain a dot, e.g. '81661.2'), title, brand (null on category/deals rows — Mercadona omits it there), ean (search rows and detail), packaging, main_feature, category{id,name,level}, breadcrumb[], category_paths[][], url, slug, image, price (number, EUR, the shelf price; for weighed counter items the price of the minimum quantity), price_display ('5,04 €'), currency, original_price (the previous price when Mercadona shows a price drop, else null), original_price_display, discount_percent, save_amount, price_dropped, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (always null — Mercadona has no loyalty price), unit_price, unit_price_unit ('L' | 'kg' | '100 g' | '100 ml' | 'ud' | 'dz' | 'dc' | 'lv' | 'm'), unit_price_measure ('litre', 'kg', 'unit', 'dozen', 'wash', 'metre' …), unit_price_display ('0,84 €/L'), sold_by ('unit' | 'weight'), price_per_quantity + price_per_quantity_unit (weighed items: 0.15 kg), order_increment, price_is_estimate (true = piece of approximate weight), size ('6 briks x 1 L'), size_value, size_unit, pack_units, pack_unit_name, drained_weight, tax_percent (VAT/IGIC included in the price), is_new_price, availability ('in_stock' | 'out_of_stock' | 'unavailable_in_warehouse' | 'unavailable'), in_stock, status, unavailable_from, unavailable_weekdays[], max_order_quantity, age_restricted, is_water, is_new_arrival, rating/review_count/rating_scale (null — not published), sponsored (always false)}, count, total_results, page, page_size, page_count, filters_available{brands[]{name,count}, categories[]{id,name,level,count}}, sort_applied, area{postal_code, warehouse, source ('postal_code' | 'warehouse' | 'default')}, lang, category{id,name}, subcategories[]{id,name,count}, next_category{id,name}

### POST /mercadona/v1/categories — 1 credit
Mercadona's full online category tree — every department and its categories with ids to pass to `category`.

**Parameters:**
- `postal_code` (string, optional) — Spanish postal code of the delivery address. Prices and assortment depend on the warehouse that serves it (Canary Islands prices differ). A postal code Mercadona does not deliver to is rejected. Takes precedence over `warehouse`. Default: Mercadona's own default area (Valencia, warehouse vlc1).
- `warehouse` (string, optional) — Advanced: a Mercadona warehouse id ('vlc1', 'mad3', 'bcn1', '4701') instead of a postal code. Every answer reports the warehouse used; `warehouse/lookup` maps a postal code to it.
- `lang` (enum, optional, default "es") — Language of product names and categories. [one of: es, en, ca, vai, eu]
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** categories[]{id, name, level (0), children[]{id, name, level (1), url}}, count, category_count, area{postal_code, warehouse, source ('postal_code' | 'warehouse' | 'default')}, lang

### POST /mercadona/v1/deals — 1 credit
Mercadona's complete price-drop list ('Bajadas de precio': every product whose price just went down, with the previous price) or its new-products list ('Novedades'), for a postal code. Filter by department, sort by price or by the size of the drop.

**Parameters:**
- `type` (enum, optional, default "price_drops") — Which of Mercadona's lists to read. [one of: price_drops, new_arrivals]
- `category_id` (string, optional) — Only products of this department (a first-level id from `categories`, e.g. 19 = Bodega). Mercadona's list rows name only the department.
- `postal_code` (string, optional) — Spanish postal code of the delivery address. Prices and assortment depend on the warehouse that serves it (Canary Islands prices differ). A postal code Mercadona does not deliver to is rejected. Takes precedence over `warehouse`. Default: Mercadona's own default area (Valencia, warehouse vlc1).
- `warehouse` (string, optional) — Advanced: a Mercadona warehouse id ('vlc1', 'mad3', 'bcn1', '4701') instead of a postal code. Every answer reports the warehouse used; `warehouse/lookup` maps a postal code to it.
- `lang` (enum, optional, default "es") — Language of product names and categories. [one of: es, en, ca, vai, eu]
- `page` (integer, optional, default 1) — Result page, 1-based.
- `page_size` (integer, optional, default 50) — Products per page (1-200, default 50).
- `sort` (enum, optional) — Order of the rows. Leave empty for Mercadona's own order (relevance for search, shelf order for categories). Applied over the complete list Mercadona returns. [one of: price_asc, price_desc, unit_price_asc, unit_price_desc, discount_desc, name_asc]
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** results[]{product_id (string; can contain a dot, e.g. '81661.2'), title, brand (null on category/deals rows — Mercadona omits it there), ean (search rows and detail), packaging, main_feature, category{id,name,level}, breadcrumb[], category_paths[][], url, slug, image, price (number, EUR, the shelf price; for weighed counter items the price of the minimum quantity), price_display ('5,04 €'), currency, original_price (the previous price when Mercadona shows a price drop, else null), original_price_display, discount_percent, save_amount, price_dropped, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (always null — Mercadona has no loyalty price), unit_price, unit_price_unit ('L' | 'kg' | '100 g' | '100 ml' | 'ud' | 'dz' | 'dc' | 'lv' | 'm'), unit_price_measure ('litre', 'kg', 'unit', 'dozen', 'wash', 'metre' …), unit_price_display ('0,84 €/L'), sold_by ('unit' | 'weight'), price_per_quantity + price_per_quantity_unit (weighed items: 0.15 kg), order_increment, price_is_estimate (true = piece of approximate weight), size ('6 briks x 1 L'), size_value, size_unit, pack_units, pack_unit_name, drained_weight, tax_percent (VAT/IGIC included in the price), is_new_price, availability ('in_stock' | 'out_of_stock' | 'unavailable_in_warehouse' | 'unavailable'), in_stock, status, unavailable_from, unavailable_weekdays[], max_order_quantity, age_restricted, is_water, is_new_arrival, rating/review_count/rating_scale (null — not published), sponsored (always false)}, count, total_results, page, page_size, page_count, filters_available{brands[]{name,count}, categories[]{id,name,level,count}}, sort_applied, area{postal_code, warehouse, source ('postal_code' | 'warehouse' | 'default')}, lang, type, title

**Example request body:**
```json
{
  "type": "price_drops"
}
```

### POST /mercadona/v1/warehouse/lookup — 1 credit
Check whether Mercadona's online shop delivers to a Spanish postal code and which warehouse (price area) serves it.

**Parameters:**
- `postal_code` (string, required) — Spanish 5-digit postal code.
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** postal_code, delivers (bool), warehouse (null when not served), message

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