- Pattison
- Web scraping
- Price data
- Apify
How to Scrape Save-On-Foods, PriceSmart and Urban Fare Prices
Save-On-Foods, PriceSmart Foods, Urban Fare and Quality Foods all belong to Pattison Food Group and all run on the same platform. They price independently, which is the whole reason to scrape them together.
Savvi4 min read
Start here
Paste this into the Input tab of the Save-On-Foods Scraper API, switch to JSON view, and hit Start.
{
"banner": "saveonfoods",
"search_terms": ["eggs", "milk", "chicken breast"],
"postal_code": "V6M 2P8"
}Same run over HTTP:
curl -X POST \
"https://api.apify.com/v2/acts/sunny_eternity~save-on-foods-pricesmart-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"banner": "saveonfoods", "search_terms": ["eggs", "milk"], "postal_code": "V6M 2P8"}'Change banner to pricesmart, urbanfare or qualityfoods to switch chains, or swap search_terms for categories to walk a whole department.
What comes back
{ "store": "Save-On-Foods", "name": "Organic Whole Milk 2L", "price": "5.99", "unit_price": "$0.30/100ml", "location_name": "Save-On-Foods Marine Drive", // named store, not just an id "selling_type": "by_unit", // by_unit or by_weight "comparable_unit_price": 0.3, // normalized $/100ml "is_on_sale": false, … 8 more fields}
All four banners share categories and output fields, since they run on one platform. A dataset from Urban Fare concatenates onto one from Save-On-Foods with no mapping layer.
Four chains, four prices
Same group doesn’t mean same price. The same 3.25% milk can be $7.99 at Save-On-Foods and $8.49 at Urban Fare, same city, same day. Urban Fare sits at the premium end and PriceSmart at the value end, and the gap between them is the thing worth measuring.
Because the input shape is identical across banners, a four-chain sweep is a loop:
from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
rows = []
for banner in ["saveonfoods", "pricesmart", "urbanfare", "qualityfoods"]:
run = client.actor("sunny_eternity/save-on-foods-pricesmart-scraper").call(run_input={
"banner": banner,
"search_terms": ["whole milk 2l"],
"postal_code": "V6M 2P8",
})
rows += list(client.dataset(run["defaultDatasetId"]).iterate_items())
for r in sorted(rows, key=lambda r: float(r["price"])):
print(r["store"], r["name"], r["price"])pip install apify-client first. The JavaScript client does the same thing:
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const banners = ['saveonfoods', 'pricesmart', 'urbanfare', 'qualityfoods'];
const rows = [];
for (const banner of banners) {
const run = await client.actor('sunny_eternity/save-on-foods-pricesmart-scraper').call({
banner,
search_terms: ['whole milk 2l'],
postal_code: 'V6M 2P8',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
rows.push(...items);
}Stores, and the rsid
A postal code is usually enough. The Actor reads the chain’s live store directory at runtime rather than a bundled table, so Nanaimo, Kelowna, Prince George, Calgary, Saskatoon and Winnipeg resolve as reliably as downtown Vancouver. Every row names the store and its rsid.
To pin one store, pass locationId. You can read a store’s rsid straight out of the URL on the chain’s own site, the number in /sm/planning/rsid/1982/:
| Store | rsid |
|---|---|
| Save-On-Foods Vancouver Marine Drive | 1982 |
| Save-On-Foods Calgary Mount Royal | 6638 |
| PriceSmart Burnaby Station Square | 2281 |
| Urban Fare Vancouver Yaletown | 7614 |
| Quality Foods Nanaimo Harewood | 4714 |
Two things to know
- Coverage stops at four provinces. BC, Alberta, Saskatchewan and Manitoba. A postal code outside the chain’s footprint returns an error naming the provinces that banner actually serves.
comparable_unit_priceis null when a size won’t parse. You get null rather than a guessed number, so filter those rows before sorting on price per 100 ml.
To put these four against the Loblaw banners and T&T in one run instead of four, the comparison API does the matching for you.
Frequently asked questions
Is there an official Save-On-Foods or PriceSmart API?
No. None of the four chains publishes a public product or pricing API.
Does it cover Urban Fare and Quality Foods?
Yes, with banner set to urbanfare or qualityfoods. All four run on the same platform, so categories and output fields are identical across them.
How do I find a store’s rsid?
Usually postal_code is enough. Otherwise browse to your store on the chain’s site and read the rsid out of the URL, the number in /sm/planning/rsid/1982/. 1982 is Save-On-Foods Vancouver Marine Drive, 2281 is PriceSmart Burnaby Station Square, 7614 is Urban Fare Yaletown.
Does it work outside the big cities?
Yes. The Actor reads the chain’s live store directory at runtime rather than a bundled table, so Nanaimo, Kelowna, Prince George, Saskatoon and Winnipeg resolve as reliably as downtown Vancouver.
Do the four chains charge the same prices?
No, and that’s the useful part. They price independently, so the same 3.25% milk can be $7.99 at Save-On-Foods and $8.49 at Urban Fare on the same day in the same city.
Which provinces are covered?
BC, Alberta, Saskatchewan and Manitoba.
How do I track prices over time?
Schedule the Actor and diff price by product_id per store. Store matters here: the four chains price independently and each row names its rsid, so a history keyed on product alone blurs four price lines together.
Is scraping Save-On-Foods legal?
Public product and price information is generally legal to scrape. Each of the four chains has its own terms of service, and your local law applies regardless, so treat a four-banner sweep as four times the request volume and pace it accordingly.
Try it on your own data
The Save-On-Foods Scraper API runs on Apify. Paste an input, hit Start, and see what comes back before you write any code against it.
Get the Save-On-Foods Scraper APIOther retailers
- Canadian Grocery Price Comparison API16 chains matched, scored and ranked in a single call.
- Loblaws, No Frills & Superstore API12 Loblaw banners with PC Optimum offers and multi-buy deals.
- 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 T&T Supermarket PricesThe catalogue the mainstream Canadian scrapers skip. Also the three limitations I’d want to know before building anything on it.