# Decathlon API — search the decathlon.com (US) sporting-goods catalogue and pull a full product with every size/colour variant, its EAN-13 barcode, shipping weight in grams, live stock count, price and sale price, options, images and video — no API key, account or cookie required

> Search or browse the decathlon.com (US) catalogue. Give a free-text `query` ('hiking boots', 'bike helmet', 'kiprun'), a `collection` handle to browse (`running`, `womens`, `mens-hiking-boots`, `all`), or both to search inside one collection. Every result carries Decathlon's own product id and handle, the product title, house brand, product type, tags, image, price range, sale price, stock flag and — in catalogue and collection mode — the variant count, SKU list and shipping-weight range. Narrow with product_type, vendor, tag, min_price, max_price and available_only; sort by price, name or publish date; page through the whole match set. Live facet vocabularies with counts come back with every catalogue-mode response, so the filter values are never a guess. Feed a result's `handle` straight into `product_detail` for barcodes and per-variant weights.
> ReefAPI engine `decathlon` · 2 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/decathlon/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 or blocked calls are free.
- **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 /decathlon/v1/search — 1 credit
Search or browse the decathlon.com (US) catalogue. Give a free-text `query` ('hiking boots', 'bike helmet', 'kiprun'), a `collection` handle to browse (`running`, `womens`, `mens-hiking-boots`, `all`), or both to search inside one collection. Every result carries Decathlon's own product id and handle, the product title, house brand, product type, tags, image, price range, sale price, stock flag and — in catalogue and collection mode — the variant count, SKU list and shipping-weight range. Narrow with product_type, vendor, tag, min_price, max_price and available_only; sort by price, name or publish date; page through the whole match set. Live facet vocabularies with counts come back with every catalogue-mode response, so the filter values are never a guess. Feed a result's `handle` straight into `product_detail` for barcodes and per-variant weights.

**Parameters:**
- `query` (string, optional) — What to look for in the decathlon.com (US) catalogue — a product name, category word or one of Decathlon's own brands ('hiking boots', 'kiprun running shoes', 'bike helmet', 'quechua tent'). Provide `query` OR `collection` (or both, to search inside one collection). NOTE decathlon.com is the **US** store and its catalogue is small (~500 products): a term the US store does not carry returns HTTP 200 with zero results, which is an honest empty and not an error. decathlon.com is a narrow US store (about 500 products, heavy in hiking, camping, cycling, running and apparel) — it really does not stock tennis rackets, swim goggles, helmets or dumbbells, and this API will tell you 0 rather than pad the answer. (decathlon.com's own search will happily answer 'racket' with 58 jackets; this API will not.)
- `collection` (string, optional) — Browse one Decathlon collection instead of (or as well as) keyword searching. Use the handle exactly as it appears in a decathlon.com/collections/<handle> URL — e.g. `running`, `womens`, `mens-hiking-boots`, `womens-jackets`, `hiking-camp-accessories`. `all` browses the whole store. A handle that does not exist returns NOT_FOUND; a real collection the US store currently carries nothing in returns an honest empty result set.
- `mode` (enum, optional, default "catalog") — Which route answers. Leave this alone unless you are building a type-ahead box. `catalog` (the default) searches Decathlon's complete US catalogue and returns every match with a real total, pagination, filters, sorting and live facet counts. `suggest` calls Shopify's autocomplete index instead: one small fast request, but it is a type-ahead index, not search — it is hard-capped at 10 results, publishes no total, cannot page, filter or sort, and it UNDER-REPORTS (measured: 0 results for 'tennis' where the store has 1, and 4 for 'swim' where the store has 7). `meta.search_mode` and `meta.ranking` always report what ran. [one of: catalog, suggest]
- `max_results` (integer, optional, default 20) — How many products to return (1-250). In `suggest` mode Shopify's own cap of 10 applies whatever you ask for.
- `page` (integer, optional, default 1) — Which page of the matched result set to return (1-based, `max_results` per page). Paging is a catalogue-mode feature — Shopify's autocomplete index has a single page. Page until `meta.pagination.has_more` is false.
- `sort` (enum, optional) — Result ordering (catalogue and collection mode). Omit for relevance, which in collection browsing means Decathlon's own collection order. Any explicit sort forces catalogue mode so that the ordering covers the whole match set rather than one page. [one of: relevance, price_asc, price_desc, title_asc, title_desc, newest, oldest]
- `product_type` (string, optional) — Keep only products whose Shopify product type contains this text (case-insensitive). The values that actually exist for your query come back in `facets.product_type` with counts, so you never have to guess.
- `vendor` (string, optional) — Keep only products from this Decathlon house brand (Kiprun, Quechua, Domyos, Rockrider, Forclaz, Nabaiji, Btwin…). Case-insensitive, substring match. Live values with counts are in `facets.vendor`.
- `tag` (string, optional) — Keep only products carrying this Shopify tag (exact, case-insensitive). Decathlon's tag list mixes merchandising tags ('hiking_boots', 'FW25') with internal bookkeeping tags ('FAMILY: 25126', stock flags) — they are returned verbatim, and `facets.tag` shows which ones your result set actually has.
- `min_price` (number, optional) — Keep only products with at least one variant at or above this price, in USD.
- `max_price` (number, optional) — Keep only products with at least one variant at or below this price, in USD.
- `available_only` (boolean, optional, default false) — Keep only products with at least one buyable variant. Works in every mode — in suggest mode it is passed to Shopify as its own `unavailable_products=hide` option (measured: 10 results become 7 on a query with sold-out items).

**Returns:** products[]{product_id, handle, url, title, vendor, product_type, tags[], description, description_html, image, image_count, available, price_min, price_max, compare_at_price_min, compare_at_price_max, currency, on_sale, variant_count, in_stock_variant_count, grams_min, grams_max, skus[], options[], published_at, created_at, updated_at} + collection{handle, collection_id, title, description, products_count, products_published, url} (only when you browsed a collection; `products_count` is Decathlon's own declared figure and `products_published` is how many the collection actually serves — they disagree on about half of collections, so both are returned rather than one being quietly preferred) + facets{product_type[]{value,count}, vendor[]{value,count}, tag[]{value,count}, price{min,max}} (catalogue and collection mode). meta.search_mode says which route answered (`suggest` | `catalog` | `collection`), meta.ranking says whose ordering you are looking at (`shopify_suggest` | `engine_relevance` | `collection_order`), and meta.pagination{page, per_page, total_results, returned, has_more, next_page} carries the counts. meta.matching{all_terms, any_term} says how the result set was found: `all_terms` is the normal case (every search term matched the product), and `any_term` is a rescue that only ever runs when matching every term found nothing at all — a thin result set is never padded out with weaker matches. HONEST LIMITS: search cards never carry `barcode`, `weight` or per-variant stock counts, because Decathlon's list feeds do not publish them at all — `product_detail` is where those live. decathlon.com is the US store only, it lists about 500 products, and a term it does not stock returns 0 products with ok:true rather than a padded answer. `suggest` mode is a type-ahead index: max 10, one page, `total_results` null, and it misses products catalogue mode finds.

**Example request body:**
```json
{
  "query": "hiking boots"
}
```

### POST /decathlon/v1/product_detail — 1 credit
Get one Decathlon product in full, by handle, product id or decathlon.com URL — title, house brand, product type, tags, the marketing description in both plain text and its original HTML, every image with dimensions and the variants it belongs to, product video and 3D media, the option axes with their values, and one row per size x colour variant. Each variant row carries Decathlon's own variant id, the retail SKU, the **EAN-13 barcode**, the **shipping weight in grams** (plus the merchant's own weight + unit pair), the live **inventory quantity** as a real integer, the buyable flag, price, compare-at price, tax and shipping flags, and any volume-pricing rules. Built for shipping-cost and catalogue-matching work: barcode and grams are the two fields this endpoint exists for.

**Parameters:**
- `handle` (string, optional) — The product's Decathlon handle — the last path segment of a decathlon.com/products/<handle> URL, and the `handle` returned by `search`. This is the fast path: it is the only key Decathlon's own product route resolves, so it costs exactly one round trip. Provide `handle`, `product_id` or `url`.
- `product_id` (string, optional) — Shopify's numeric product id, exactly as `search` returns it. Accepted for convenience, but be aware decathlon.com does NOT serve a product by id (/products/<id>.json is a 404), so the id is resolved to its handle through this API's cached catalogue index — the first such call after the cache expires also pays for the catalogue read. Pass `handle` when you have it.
- `url` (string, optional) — A full decathlon.com product URL — an alternative to `handle`. A `?variant=<id>` suffix is accepted and ignored (every variant is returned anyway).

**Returns:** product{product_id, handle, url, title, vendor, product_type, tags[], description, description_html, specifications[]{name, value}, available, price_min, price_max, price_varies, compare_at_price_min, compare_at_price_max, currency, on_sale, options[]{name, position, values[]}, images[]{image_id, src, alt, position, width, height, variant_ids[]}, image, media[]{media_id, media_type, position, alt, src, external_id, host}, variant_count, in_stock_variant_count, grams_min, grams_max, weight_units_seen[], weight_unit_varies, published_at, created_at, updated_at, variants[]{variant_id, sku, barcode, title, options{}, option1, option2, option3, position, price, compare_at_price, currency, available, inventory_quantity, inventory_policy, inventory_management, grams, weight, weight_unit, requires_shipping, taxable, quantity_rule, quantity_price_breaks, image, url}}. meta.coverage counts how many of this product's variants actually carry a barcode, a weight and a stock number, so you can see the completeness of the row you were given rather than trusting an average. FIELD NOTES: `grams` is the canonical shipping weight and is always in grams; `weight`+`weight_unit` are the merchant's own entry and Decathlon genuinely mixes kg and lb BETWEEN variants of the same product, which is why `weight_unit_varies` is flagged and no conversion is invented. `inventory_quantity` is null when the merchant does not track stock for that variant (`inventory_management` is then null too) — null means 'not published', never 'zero'. `description_html` is the raw HTML Decathlon publishes; `description` is the same copy with markup stripped and entities decoded. Decathlon publishes NO ratings, reviews, dimensions, colour swatches or per-store stock on these routes.

**Example request body:**
```json
{
  "handle": "decathlon-gift-card"
}
```

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