- Loblaw
- Web scraping
- Price data
- Apify
How to Scrape Loblaws, Superstore and No Frills Prices
Loblaw runs twelve grocery banners in Canada on one platform. That means one input shape and one output shape covers Loblaws, Superstore, No Frills, Maxi, Provigo and seven more, priced at the individual store.
Savvi5 min read
Start here
Pick a banner, name what you want, give a postal code. Paste this into the Input tab of the Loblaws Scraper API on Apify, switch to JSON view, and hit Start.
{
"banner": "superstore",
"search_terms": ["eggs", "milk", "chicken breast"],
"postal_code": "V5X 0C4"
}Same run over HTTP:
curl -X POST \
"https://api.apify.com/v2/acts/sunny_eternity~loblaws-grocery-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"banner": "superstore", "search_terms": ["eggs", "milk"], "postal_code": "V5X 0C4"}'Swap search_terms for categories to walk a department instead, like ["fruits-vegetables", "dairy-eggs"], or ["all"] for the whole store.
What comes back
{ "store": "Real Canadian Superstore", "name": "Bananas", "price": "0.69", "unit_price": "454 g, $0.15/100g", "comparable_unit_price": 0.15, // normalized $/100g "was_price": 0.89, // pre-sale price "is_on_sale": true, // live sale flag "multi_buy_deal": "2 for $1.00", // parsed promo text "pc_optimum_offer": "1000 points", // loyalty points - Loblaw only … 8 more fields}
Two of those fields are Loblaw-only. pc_optimum_offer carries loyalty points offers like "1000 points", and multi_buy_deal carries promo text like "2 for $1.00". If you’re modelling the real cost of a basket rather than the shelf price, those two are where the difference hides.
Picking the right banner
banner is the input that matters most, because it decides both the catalogue and the region. Get it wrong and you’ll scrape a chain that doesn’t operate where you’re asking about.
| Region | Banners that serve it |
|---|---|
| Quebec | maxi, provigo. valumart and wholesaleclub also have Quebec locations. |
| Atlantic Canada | rass (Atlantic Superstore — the banner code is rass, not atlantic), plus nofrills and independent. |
| Ontario | The widest choice: loblaw, zehrs, fortinos, valumart, independent, independentcitymarket, nofrills, wholesaleclub. |
| Western Canada | superstore, nofrills, independent, wholesaleclub. |
Postal codes resolve against Loblaw’s own store directory, the same endpoint the storefronts call to fill their store pickers, so coverage is nationwide and there’s no bundled lookup table to go stale. Every row names the store, its postal code and its coordinates. To pin one store, pass locationId instead: 1517 is Superstore Vancouver Marine Drive, 1000 is Loblaws Toronto Queen St, 7234 is Maxi Montréal Mt-Royal Ouest.
From Python
pip install apify-client, then:
from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("sunny_eternity/loblaws-grocery-scraper").call(run_input={
"banner": "superstore",
"search_terms": ["eggs", "milk", "chicken breast"],
"postal_code": "V5X 0C4",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["name"], item["price"], item["comparable_unit_price"], item["is_on_sale"])From JavaScript
npm i apify-client, then:
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('sunny_eternity/loblaws-grocery-scraper').call({
banner: 'superstore',
search_terms: ['eggs', 'milk', 'chicken breast'],
postal_code: 'V5X 0C4',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const onSale = items.filter((i) => i.is_on_sale);Comparing across banners
product_id is shared across the Loblaw catalogue, which is the useful bit. Run the same search_terms and region once per banner, then join on product_id and you have the same SKU priced at Superstore, No Frills and Loblaws side by side. That’s the cheapest way to see a chain’s internal price laddering.
for banner in ["superstore", "nofrills", "loblaw"]:
run = client.actor("sunny_eternity/loblaws-grocery-scraper").call(run_input={
"banner": banner,
"search_terms": ["butter"],
"postal_code": "M5H 2N2",
})
# join the rows on product_id afterwardsIf you want that join done for you, and across Pattison and T&T as well, the comparison API handles the fan-out and the matching in a single run.
Two things that catch people
- Atlantic Superstore’s code is
rass. Every other banner reads like its name. This one doesn’t, and Dominion in Newfoundland isn’t supported at all. comparable_unit_priceis null when a size won’t parse. That’s deliberate, so a guessed number stays out of your comparison. Filter those rows out before you sort on price per 100 g.
Frequently asked questions
Is there an official Loblaws API?
No. Loblaw publishes no public product or pricing API, so the Actor reads the same JSON endpoints the storefronts themselves call.
Which banner covers my region?
Quebec is maxi and provigo. Atlantic Canada is rass (Atlantic Superstore), nofrills and independent. Ontario has the widest choice, with all eight Ontario banners. Western Canada is superstore, nofrills, independent and wholesaleclub.
Can I get prices for a specific postal code?
Yes. Pass any Canadian postal_code and the Actor resolves the nearest store of that banner, then reports the store name, its postal code and its coordinates on every row.
How do I pin one specific store?
Pass locationId instead of postal_code. 1517 is Superstore Vancouver Marine Drive, 1000 is Loblaws Toronto Queen St, 7234 is Maxi Montréal Mt-Royal Ouest.
Can I compare the same product across banners?
Yes. Run once per banner with the same search_terms and region, then join on product_id, which is shared across the Loblaw catalogue. For a ready-made cross-chain comparison, use the comparison Actor instead.
What are pc_optimum_offer and multi_buy_deal?
pc_optimum_offer carries loyalty points offers like "1000 points" and multi_buy_deal carries promo text like "2 for $1.00". Both are Loblaw-specific; the other retailer Actors return multi_buy_deal but never PC Optimum.
How many products can one run return?
A single subcategory is usually tens to hundreds. categories: ["all"] walks the entire store. Rows stream to the dataset as they go, so partial results are available while the run continues.
Is scraping Loblaws legal?
Public product and price information is generally legal to scrape. Loblaw’s terms of service and your local law are still your responsibility. Twelve banners on one platform makes it easy to fan out wider than you meant to, so cap your banner list and your run frequency.
Try it on your own data
The Loblaws Scraper API runs on Apify. Paste an input, hit Start, and see what comes back before you write any code against it.
Get the Loblaws Scraper APIOther retailers
- Canadian Grocery Price Comparison API16 chains matched, scored and ranked in a single call.
- Save-On-Foods & PriceSmart APIFour Pattison Food Group chains across BC and the prairies.
- T&T Supermarket APICanada’s largest Asian grocery chain, in BC, AB, ON and QC.
- Costco Scraper APIWarehouse prices, deals and item numbers from costco.ca and costco.com.
They all share the same field names. See them side by side on the grocery data API page, with a sample record for each.
Keep reading
- How to Compare Grocery Prices Across 16 Canadian Chains in One CallThis one isn’t a scraper, it’s a matcher. How the confidence scoring works, how to filter on it, and how to get basket totals per store.
- How to Scrape Save-On-Foods, PriceSmart and Urban Fare PricesSame group, same platform, different prices. The same milk runs $7.99 at Save-On-Foods and $8.49 at Urban Fare on the same day in the same city.