# Loblaws API — Canadian grocery prices per store: Loblaws, Real Canadian Superstore, No Frills, Zehrs, Fortinos, Maxi and more — price, unit price, sale and multi-buy deals, member price, PC Optimum offers, stock and full product data

> Search one Loblaw-banner store's shelf: price, was-price, unit price, member price, sale / multi-buy deal, PC Optimum offer flag, stock level, brand, pack size and a sponsored flag per row, plus the brand / category / deal facets. Prices are the chosen store's (`store_id`; without it the banner's default store, flagged).
> ReefAPI engine `loblaws` · 5 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/loblaws/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). Send the key as `Authorization: Bearer <key>`, or put it in the URL (`?key=<key>`) when the client has no header field, as ChatGPT does.

## Endpoints

### POST https://api.reefapi.com/loblaws/v1/search — 2 credits
Search one Loblaw-banner store's shelf: price, was-price, unit price, member price, sale / multi-buy deal, PC Optimum offer flag, stock level, brand, pack size and a sponsored flag per row, plus the brand / category / deal facets. Prices are the chosen store's (`store_id`; without it the banner's default store, flagged).

**Parameters:**
- `query` (string, required) — What to search for, as typed in the site's search box. The site's search is semantic: a word it does not know still returns the closest products.
- `banner` (enum, optional, default "loblaw") — Which Loblaw grocery banner (all share one catalogue platform; prices and assortment are the chosen store's). Default loblaw (loblaws.ca). [one of: loblaw, superstore, nofrills, zehrs, fortinos, maxi, independent, rass, wholesaleclub, valumart, dominion]
- `store_id` (string, optional) — The store whose shelf you read (prices, deals, stock and assortment differ store by store). Get ids from the `stores` action. Without it the banner's default store is used and the answer says so (store_defaulted: true): loblaw 1032 Markham ON, superstore 1562, nofrills 3683, zehrs 0550, fortinos 0065, maxi 8049, independent 1891, rass 0311, wholesaleclub 0215, valumart 5374, dominion 0927.
- `page` (integer, optional, default 1) — Result page, 1-50. Past the last page the answer is an honest empty.
- `page_size` (integer, optional, default 48) — Organic rows per page (the site uses 48). Sponsored rows come on top of these on page 1.
- `sort` (enum, optional, default "relevance") — Result order (the site's own five sorts). [one of: relevance, price_asc, price_desc, name_asc, newest]
- `brand` (string, optional) — Only this brand, by the brand CODE the `facets.brands` list of any search answer gives (e.g. BDIA = Blue Diamond). One code per call.
- `deal` (enum, optional) — Only products on this kind of deal at the chosen store. [one of: price_reduction, multi_buy, pc_points, dollar_deals, flyer]
- `category_id` (string, optional) — A category id (numeric) — from a product's breadcrumbs, `facets.categories`, or the site's own /c/<id> URLs (28224 = Milk & Cream, 28003 = Dairy & Eggs, 27985 = Food).

**Returns:** query, banner, store_id, store_name, store_defaulted, page, page_size, total (the source's own count), returned, organic_rows, sponsored_rows, has_next, results[]{product_id (-> product.product_id), article_number, title, brand, package_size, package_sizing_text, price, price_display, price_is_estimate (true for items sold by weight: 'about $x' for one average piece), currency (CAD), original_price (the was-price this row prints), discount_amount, unit_price, unit_price_per (e.g. '100ml', '1kg'), unit_prices[]{price, per_quantity, per_unit}, sold_by ('piece' | 'weight'), sold_by_source, member_price (PC Optimum member-only price), member_price_display, deal{type ('sale' | 'multi' | 'limit' | …), text, expires, points}, multi_buy{price, min_quantity, text}, pc_optimum_offer (bool), pc_optimum_offer_text, stock_status ('in_stock' | 'low_stock' | 'out_of_stock'), max_order_quantity, environmental_fee, badge, sponsored, repeated_in_results, image, url, rank}, facets{categories[], brands[], deals[], flyers[] ({code, name, count})}

**Example request body:**
```json
{
  "query": "milk",
  "banner": "loblaw",
  "store_id": "1032"
}
```

### POST https://api.reefapi.com/loblaws/v1/category — 2 credits
Every product in one category (department, aisle or shelf) at one store, paged, with the same rows, sorts and filters as search.

**Parameters:**
- `category_id` (string, required) — A category id (numeric) — from a product's breadcrumbs, `facets.categories`, or the site's own /c/<id> URLs (28224 = Milk & Cream, 28003 = Dairy & Eggs, 27985 = Food).
- `banner` (enum, optional, default "loblaw") — Which Loblaw grocery banner (all share one catalogue platform; prices and assortment are the chosen store's). Default loblaw (loblaws.ca). [one of: loblaw, superstore, nofrills, zehrs, fortinos, maxi, independent, rass, wholesaleclub, valumart, dominion]
- `store_id` (string, optional) — The store whose shelf you read (prices, deals, stock and assortment differ store by store). Get ids from the `stores` action. Without it the banner's default store is used and the answer says so (store_defaulted: true): loblaw 1032 Markham ON, superstore 1562, nofrills 3683, zehrs 0550, fortinos 0065, maxi 8049, independent 1891, rass 0311, wholesaleclub 0215, valumart 5374, dominion 0927.
- `page` (integer, optional, default 1) — Result page, 1-50. Past the last page the answer is an honest empty.
- `page_size` (integer, optional, default 48) — Organic rows per page (the site uses 48). Sponsored rows come on top of these on page 1.
- `sort` (enum, optional, default "relevance") — Result order (the site's own five sorts). [one of: relevance, price_asc, price_desc, name_asc, newest]
- `brand` (string, optional) — Only this brand, by the brand CODE the `facets.brands` list of any search answer gives (e.g. BDIA = Blue Diamond). One code per call.
- `deal` (enum, optional) — Only products on this kind of deal at the chosen store. [one of: price_reduction, multi_buy, pc_points, dollar_deals, flyer]

**Returns:** category_id, banner, store_id, store_name, store_defaulted, page, page_size, total (the source's own count), returned, organic_rows, sponsored_rows, has_next, results[]{product_id (-> product.product_id), article_number, title, brand, package_size, package_sizing_text, price, price_display, price_is_estimate (true for items sold by weight: 'about $x' for one average piece), currency (CAD), original_price (the was-price this row prints), discount_amount, unit_price, unit_price_per (e.g. '100ml', '1kg'), unit_prices[]{price, per_quantity, per_unit}, sold_by ('piece' | 'weight'), sold_by_source, member_price (PC Optimum member-only price), member_price_display, deal{type ('sale' | 'multi' | 'limit' | …), text, expires, points}, multi_buy{price, min_quantity, text}, pc_optimum_offer (bool), pc_optimum_offer_text, stock_status ('in_stock' | 'low_stock' | 'out_of_stock'), max_order_quantity, environmental_fee, badge, sponsored, repeated_in_results, image, url, rank}, facets{categories[], brands[], deals[], flyers[] ({code, name, count})}

**Example request body:**
```json
{
  "category_id": "28224",
  "store_id": "1032"
}
```

### POST https://api.reefapi.com/loblaws/v1/deals — 2 credits
Everything on one kind of deal at one store right now — sale prices, multi-buys, PC Optimum points offers, $1-$5 deals or this week's flyer — optionally inside one category.

**Parameters:**
- `deal` (enum, optional, default "price_reduction") — Which deal list (default price_reduction = items on sale). [one of: price_reduction, multi_buy, pc_points, dollar_deals, flyer]
- `banner` (enum, optional, default "loblaw") — Which Loblaw grocery banner (all share one catalogue platform; prices and assortment are the chosen store's). Default loblaw (loblaws.ca). [one of: loblaw, superstore, nofrills, zehrs, fortinos, maxi, independent, rass, wholesaleclub, valumart, dominion]
- `store_id` (string, optional) — The store whose shelf you read (prices, deals, stock and assortment differ store by store). Get ids from the `stores` action. Without it the banner's default store is used and the answer says so (store_defaulted: true): loblaw 1032 Markham ON, superstore 1562, nofrills 3683, zehrs 0550, fortinos 0065, maxi 8049, independent 1891, rass 0311, wholesaleclub 0215, valumart 5374, dominion 0927.
- `category_id` (string, optional) — A category id (numeric) — from a product's breadcrumbs, `facets.categories`, or the site's own /c/<id> URLs (28224 = Milk & Cream, 28003 = Dairy & Eggs, 27985 = Food).
- `page` (integer, optional, default 1) — Result page, 1-50. Past the last page the answer is an honest empty.
- `page_size` (integer, optional, default 48) — Organic rows per page (the site uses 48). Sponsored rows come on top of these on page 1.
- `sort` (enum, optional, default "relevance") — Result order (the site's own five sorts). [one of: relevance, price_asc, price_desc, name_asc, newest]

**Returns:** deal, banner, store_id, store_name, store_defaulted, page, page_size, total (the source's own count), returned, organic_rows, sponsored_rows, has_next, results[]{product_id (-> product.product_id), article_number, title, brand, package_size, package_sizing_text, price, price_display, price_is_estimate (true for items sold by weight: 'about $x' for one average piece), currency (CAD), original_price (the was-price this row prints), discount_amount, unit_price, unit_price_per (e.g. '100ml', '1kg'), unit_prices[]{price, per_quantity, per_unit}, sold_by ('piece' | 'weight'), sold_by_source, member_price (PC Optimum member-only price), member_price_display, deal{type ('sale' | 'multi' | 'limit' | …), text, expires, points}, multi_buy{price, min_quantity, text}, pc_optimum_offer (bool), pc_optimum_offer_text, stock_status ('in_stock' | 'low_stock' | 'out_of_stock'), max_order_quantity, environmental_fee, badge, sponsored, repeated_in_results, image, url, rank}, facets{categories[], brands[], deals[], flyers[] ({code, name, count})}

**Example request body:**
```json
{
  "deal": "multi_buy",
  "store_id": "1032"
}
```

### POST https://api.reefapi.com/loblaws/v1/product — 1 credit
One product at one store by the `product_id` a search row returns (or its product URL): price and price type (regular / special) with its end date, was-price, every unit price, member price, deal and multi-buy price, PC Optimum points offer, all promotions, stock, taxes, deposit, GTIN, description, ingredients, nutrition table, breadcrumb, variants and images. `date` reads the price for a coming day (the store's flyer changes on Thursdays).

**Parameters:**
- `product_id` (string, required) — The `product_id` exactly as a search row returns it (e.g. 20896887_EA, 20175355001_KG) or a product URL ending /p/<id>.
- `banner` (enum, optional, default "loblaw") — Which Loblaw grocery banner (all share one catalogue platform; prices and assortment are the chosen store's). Default loblaw (loblaws.ca). [one of: loblaw, superstore, nofrills, zehrs, fortinos, maxi, independent, rass, wholesaleclub, valumart, dominion]
- `store_id` (string, optional) — The store whose shelf you read (prices, deals, stock and assortment differ store by store). Get ids from the `stores` action. Without it the banner's default store is used and the answer says so (store_defaulted: true): loblaw 1032 Markham ON, superstore 1562, nofrills 3683, zehrs 0550, fortinos 0065, maxi 8049, independent 1891, rass 0311, wholesaleclub 0215, valumart 5374, dominion 0927.
- `date` (string, optional) — The day whose price you want, YYYY-MM-DD, from today (Toronto) to 6 days ahead. Default today in Toronto.
- `language` (enum, optional, default "en") — Language of the product texts. [one of: en, fr]

**Returns:** banner, store_id, store_name, store_defaulted, price_date, product{product_id, article_number, gtin, title, brand, description, package_size, price, price_type ('regular' | 'special'), price_expires, currency, original_price, discount_amount, unit_prices[]{price, per_quantity, per_unit}, sold_by, sold_by_source, average_weight, member_price, member_price_expires, deal{type, text, expires}, deal_price, multi_buy{price, min_quantity, text}, pc_optimum_points, pc_optimum_offer_text, pc_optimum_offer_expires, promotions[]{type, title, text, reward, expires, promo_code}, member_promotions[], stock_status, shoppable, max_order_quantity, taxes{gst, hst, pst}, deposit, environmental_fee, badges[], breadcrumbs[]{category_id, name}, ingredients, nutrition{serving_size, household_serving, nutrients[]{name, amount, daily_value, sub[]}, micronutrients[], disclaimer}, variants[]{product_id, title}, images[], url}

**Example request body:**
```json
{
  "product_id": "20896887_EA",
  "store_id": "1032"
}
```

### POST https://api.reefapi.com/loblaws/v1/stores — 2 credits
The stores of one banner with their store_id (for every other action): name, address, postal code, coordinates, phone, today's hours and open-now, minimum online order, departments. Narrow by `postal_code` prefix, `city`, or sort by distance from `lat`+`lon`.

**Parameters:**
- `banner` (enum, optional, default "loblaw") — Which Loblaw grocery banner (all share one catalogue platform; prices and assortment are the chosen store's). Default loblaw (loblaws.ca). [one of: loblaw, superstore, nofrills, zehrs, fortinos, maxi, independent, rass, wholesaleclub, valumart, dominion]
- `postal_code` (string, optional) — Canadian postal code or its first 3 characters (FSA): stores whose postal code starts with it.
- `city` (string, optional) — Stores in this city (exact name, case-insensitive).
- `lat` (number, optional) — Latitude; with `lon`, stores are sorted by distance and carry distance_km.
- `lon` (number, optional) — Longitude, with `lat`.
- `sells_online` (boolean, optional, default true) — Only stores that sell online on the site (default true; the others answer no prices).
- `limit` (integer, optional, default 50) — At most this many stores.

**Returns:** banner, total (stores of the banner), matched, returned, stores[]{store_id, name, banner, banner_name, location_type, pickup_type, sells_online, address, city, province, postal_code, lat, lon, distance_km, phone, store_manager, time_zone, open_now, hours_today, hours_date, min_order_value, departments[]}

**Example request body:**
```json
{
  "banner": "loblaw",
  "city": "Toronto"
}
```

## At scale
- **Volume:** 5M+ requests a day, measured at 60 requests a second across the fleet with no
  central bottleneck. Per-key limits are raised for high-volume accounts; volume pricing on request.
- **Missing a source:** tell us a site we do not cover and it becomes an engine. A customer asked
  for bestprice.gr on 21 Sep 2026 and it was in the catalog on 22 Sep.
- **Support:** 2 minute median time from a question in the live chat to the first answer. Setup
  help included, no support tier to buy.
- **One key, one credit pool** across every API. No per-site plans, no separate subscriptions.

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