# Coles API scraper — Coles Supermarkets (Australia) online grocery: search products, read the full product record with shelf price, was-price, half-price and multi-buy specials, unit price, barcode, nutrition panel and country of origin, browse categories and every special. Prices in AUD. No account, no browser.

> Search coles.com.au by keyword. Each product comes with its shelf price, the was-price and saving when on special, the half-price / multi-buy / online-only flags, the unit price per kg, litre or 100 g, availability, brand, size and category path. Sort by price or unit price and filter by brand, special type, dietary and free-from facets. Paid placements are removed unless you ask for them.
> ReefAPI engine `coles` · 5 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/coles/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 /coles/v1/search — 2 credits
Search coles.com.au by keyword. Each product comes with its shelf price, the was-price and saving when on special, the half-price / multi-buy / online-only flags, the unit price per kg, litre or 100 g, availability, brand, size and category path. Sort by price or unit price and filter by brand, special type, dietary and free-from facets. Paid placements are removed unless you ask for them.

**Parameters:**
- `query` (string, required) — What to look for, as a shopper would type it on coles.com.au ('milk', 'tim tam', 'laundry liquid'). Brand names work too.
- `page` (integer, optional, default 1) — Result page, 1-based. Coles returns 48 products per page; read `count`, `total_results` and `page_count` rather than assuming a size.
- `sort` (enum, optional) — Result order. Leave empty for Coles's own order (relevance for search, best sellers for categories, recommended for specials). Only orders Coles actually applies are offered; `sort_applied` is Coles's own confirmation. [one of: price_asc, price_desc, unit_price_asc]
- `brand_ids` (array, optional) — Only these brands — Coles brand ids from `filters_available.brands` or a row's `brand.id`. Several ids are OR-ed.
- `special` (enum, optional) — Only products on this kind of special. [one of: half_price, multi_buy, online_only]
- `dietary_ids` (array, optional) — Dietary facets (vegan, vegetarian, halal, GMO free …) — ids from `filters_available.dietary`. Several ids are OR-ed.
- `allergen_ids` (array, optional) — Free-from facets (gluten free, lactose free, nut free …) — ids from `filters_available.allergens`.
- `include_sponsored` (boolean, optional, default false) — false (default) drops paid placements. Coles injects them into the first page (they also repeat products that appear further down); true keeps them, flagged `sponsored: true`.
- `include_alternate_results` (boolean, optional, default false) — When nothing matches, Coles shows unrelated fallback products instead. false (default) drops them — `results` stays empty and `alternate_results_dropped` counts them; true returns them separately in `alternate_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), title (brand + name), name, brand{id,name}, size, category{id,name,level}, breadcrumb[]{id,name,level} (Coles's first listing), category_paths[][] (every category the product is shelved in), url, image, price (number — the shelf price for one item; null when the product is unavailable), price_display, currency=AUD, original_price (the was-price when Coles shows one, else null), original_price_display, discount_percent, save_amount, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (null — Coles publishes no member or Flybuys price), unit_price, unit_price_unit ('1L', '100g', '1kg'), unit_price_display ('$1.65/ 1L'), sold_by ('piece' | 'weight'), price_per ('piece'), price_is_estimate (true on weighed produce: the price is for one item of approximate weight), promotion_type ('special' | 'everyday' | 'new' | 'bonuscollectable' | null), special_type ('percent_off' | 'multi_save' | null), price_description ('1/2 Price', '40% Off', '2 for $9.40'), half_price, online_only_special, multi_buy_offer, multi_buy{min_quantity, unit_price, unit_price_display, unit_measure_price_display}, badges[]{type,text}, availability ('in_stock' | 'unavailable'), in_stock, available_quantity, max_order_quantity, max_promotional_quantity, age_restricted, rating/review_count/rating_scale (null), sponsored}, count, total_results, page, page_size, page_count, sponsored_dropped, sort_applied (Coles's own echo: relevance | best_selling | recommended | price_asc | price_desc | unit_price_asc), alternate_result (true = nothing matched), alternate_results_dropped (Coles's unrelated fallback products, dropped), alternate_results[] (empty unless include_alternate_results=true), did_you_mean[], filters_available{brands[], allergens[], dietary[], specials[]}{id,name,count}, categories[]{id,name,level,slug,count}, query

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

### POST /coles/v1/product/detail — 2 credits
The full Coles product record by `product_id` (or product URL): title, brand, size, barcode (GTIN), every image, shelf price, was-price and saving, special and multi-buy details, unit price, availability and order limits, the label (ingredients, allergens, storage and usage instructions), the nutrition panel per serving and per 100 g/ml, country of origin, and the other sizes and varieties of the same product.

**Parameters:**
- `product_id` (string, required) — Coles product id (the number every search row returns as `product_id`), or a coles.com.au product URL.
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** product{product_id (string), title (brand + name), name, brand{id,name}, size, category{id,name,level}, breadcrumb[]{id,name,level} (Coles's first listing), category_paths[][] (every category the product is shelved in), url, image, price (number — the shelf price for one item; null when the product is unavailable), price_display, currency=AUD, original_price (the was-price when Coles shows one, else null), original_price_display, discount_percent, save_amount, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (null — Coles publishes no member or Flybuys price), unit_price, unit_price_unit ('1L', '100g', '1kg'), unit_price_display ('$1.65/ 1L'), sold_by ('piece' | 'weight'), price_per ('piece'), price_is_estimate (true on weighed produce: the price is for one item of approximate weight), promotion_type ('special' | 'everyday' | 'new' | 'bonuscollectable' | null), special_type ('percent_off' | 'multi_save' | null), price_description ('1/2 Price', '40% Off', '2 for $9.40'), half_price, online_only_special, multi_buy_offer, multi_buy{min_quantity, unit_price, unit_price_display, unit_measure_price_display}, badges[]{type,text}, availability ('in_stock' | 'unavailable'), in_stock, available_quantity, max_order_quantity, max_promotional_quantity, age_restricted, rating/review_count/rating_scale (null), sponsored, barcode, images[], description, specs{label: value}, ingredients, allergen_warning, storage, usage, dimensions, origin, country_of_origin, nutrition{servings_per_package, serving_size, per_serving{}, per_100{}, panels[]}, nutritional_claims[], lifestyle[], minimum_life, variant_count, variants[]{product_id, variant_type ('size' | 'variety'), title, size, price, in_stock, url}, last_updated, store{store_id}}

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

### POST /coles/v1/deals — 2 credits
Coles's specials: every product on special, or only half-price, multi-buy or online-only specials, optionally within one category. Same rows, sorting and brand filter as search.

**Parameters:**
- `type` (enum, optional, default "all") — Which of Coles's specials to read. [one of: all, half_price, multi_buy, online_only]
- `category` (string, optional) — Optional: only specials in this category — a slug path from `categories` ('dairy-eggs-fridge' or 'dairy-eggs-fridge/milk') or a coles.com.au URL.
- `page` (integer, optional, default 1) — Result page, 1-based. Coles returns 48 products per page; read `count`, `total_results` and `page_count` rather than assuming a size.
- `sort` (enum, optional) — Result order. Leave empty for Coles's own order (relevance for search, best sellers for categories, recommended for specials). Only orders Coles actually applies are offered; `sort_applied` is Coles's own confirmation. [one of: price_asc, price_desc, unit_price_asc]
- `brand_ids` (array, optional) — Only these brands — Coles brand ids from `filters_available.brands` or a row's `brand.id`. Several ids are OR-ed.
- `include_sponsored` (boolean, optional, default false) — false (default) drops paid placements. Coles injects them into the first page (they also repeat products that appear further down); true keeps them, flagged `sponsored: true`.
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** results[]{product_id (string), title (brand + name), name, brand{id,name}, size, category{id,name,level}, breadcrumb[]{id,name,level} (Coles's first listing), category_paths[][] (every category the product is shelved in), url, image, price (number — the shelf price for one item; null when the product is unavailable), price_display, currency=AUD, original_price (the was-price when Coles shows one, else null), original_price_display, discount_percent, save_amount, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (null — Coles publishes no member or Flybuys price), unit_price, unit_price_unit ('1L', '100g', '1kg'), unit_price_display ('$1.65/ 1L'), sold_by ('piece' | 'weight'), price_per ('piece'), price_is_estimate (true on weighed produce: the price is for one item of approximate weight), promotion_type ('special' | 'everyday' | 'new' | 'bonuscollectable' | null), special_type ('percent_off' | 'multi_save' | null), price_description ('1/2 Price', '40% Off', '2 for $9.40'), half_price, online_only_special, multi_buy_offer, multi_buy{min_quantity, unit_price, unit_price_display, unit_measure_price_display}, badges[]{type,text}, availability ('in_stock' | 'unavailable'), in_stock, available_quantity, max_order_quantity, max_promotional_quantity, age_restricted, rating/review_count/rating_scale (null), sponsored}, count, total_results, page, page_size, page_count, sponsored_dropped, sort_applied (Coles's own echo: relevance | best_selling | recommended | price_asc | price_desc | unit_price_asc), alternate_result (true = nothing matched), alternate_results_dropped (Coles's unrelated fallback products, dropped), alternate_results[] (empty unless include_alternate_results=true), did_you_mean[], filters_available{brands[], allergens[], dietary[], specials[]}{id,name,count}, categories[]{id,name,level,slug,count}, type, category_slug

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

### POST /coles/v1/category — 2 credits
Every product in one Coles category (one to three levels deep), paged, with the same rows, sorting and filters as search. Category slugs come from `categories`.

**Parameters:**
- `category` (string, required) — A category slug path from `categories` (one to three levels, e.g. 'dairy-eggs-fridge' or 'dairy-eggs-fridge/milk') or a coles.com.au/browse URL.
- `page` (integer, optional, default 1) — Result page, 1-based. Coles returns 48 products per page; read `count`, `total_results` and `page_count` rather than assuming a size.
- `sort` (enum, optional) — Result order. Leave empty for Coles's own order (relevance for search, best sellers for categories, recommended for specials). Only orders Coles actually applies are offered; `sort_applied` is Coles's own confirmation. [one of: price_asc, price_desc, unit_price_asc]
- `brand_ids` (array, optional) — Only these brands — Coles brand ids from `filters_available.brands` or a row's `brand.id`. Several ids are OR-ed.
- `special` (enum, optional) — Only products on this kind of special. [one of: half_price, multi_buy, online_only]
- `dietary_ids` (array, optional) — Dietary facets (vegan, vegetarian, halal, GMO free …) — ids from `filters_available.dietary`. Several ids are OR-ed.
- `allergen_ids` (array, optional) — Free-from facets (gluten free, lactose free, nut free …) — ids from `filters_available.allergens`.
- `include_sponsored` (boolean, optional, default false) — false (default) drops paid placements. Coles injects them into the first page (they also repeat products that appear further down); true keeps them, flagged `sponsored: true`.
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** results[]{product_id (string), title (brand + name), name, brand{id,name}, size, category{id,name,level}, breadcrumb[]{id,name,level} (Coles's first listing), category_paths[][] (every category the product is shelved in), url, image, price (number — the shelf price for one item; null when the product is unavailable), price_display, currency=AUD, original_price (the was-price when Coles shows one, else null), original_price_display, discount_percent, save_amount, member_price / member_price_display / member_discount_percent / member_program / member_unit_price (null — Coles publishes no member or Flybuys price), unit_price, unit_price_unit ('1L', '100g', '1kg'), unit_price_display ('$1.65/ 1L'), sold_by ('piece' | 'weight'), price_per ('piece'), price_is_estimate (true on weighed produce: the price is for one item of approximate weight), promotion_type ('special' | 'everyday' | 'new' | 'bonuscollectable' | null), special_type ('percent_off' | 'multi_save' | null), price_description ('1/2 Price', '40% Off', '2 for $9.40'), half_price, online_only_special, multi_buy_offer, multi_buy{min_quantity, unit_price, unit_price_display, unit_measure_price_display}, badges[]{type,text}, availability ('in_stock' | 'unavailable'), in_stock, available_quantity, max_order_quantity, max_promotional_quantity, age_restricted, rating/review_count/rating_scale (null), sponsored}, count, total_results, page, page_size, page_count, sponsored_dropped, sort_applied (Coles's own echo: relevance | best_selling | recommended | price_asc | price_desc | unit_price_asc), alternate_result (true = nothing matched), alternate_results_dropped (Coles's unrelated fallback products, dropped), alternate_results[] (empty unless include_alternate_results=true), did_you_mean[], filters_available{brands[], allergens[], dietary[], specials[]}{id,name,count}, categories[]{id,name,level,slug,count}, category_slug

### POST /coles/v1/categories — 2 credits
Coles's full online category tree — every department, category and aisle with its id, slug path, URL and product count.

**Parameters:**
- `max_rotations` (integer, optional, default 3) — Advanced: how many times to retry a difficult request (1-6, default 3).

**Returns:** categories[]{id, name, level, slug, product_count, url, children[]}, count

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