Save-On-Foods, PriceSmart & Urban Fare Scraper API - Canada
Pricing
from $1.00 / 1,000 results
Save-On-Foods, PriceSmart & Urban Fare Scraper API - Canada
Scrape grocery prices from Pattison Food Group stores — Save-On-Foods, PriceSmart Foods, Urban Fare and Quality Foods. Product names, prices, sale prices, package sizes, normalized unit pricing and images, per store by postal code. Export JSON, CSV or Excel.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
JChaw
Maintained by CommunityActor stats
2
Bookmarked
11
Total users
2
Monthly active users
10 days ago
Last modified
Categories
Share
Western Canada grocery price data from the four Pattison Food Group chains, priced at the individual store by postal code.
The Save-On-Foods Scraper API returns live shelf prices, sale prices, unit prices and weekly deals from Save-On-Foods, PriceSmart Foods, Urban Fare and Quality Foods, with no API key and no browser.
- Chains: Save-On-Foods, PriceSmart Foods, Urban Fare, Quality Foods
- Coverage: BC, Alberta, Saskatchewan, Manitoba
- Every row: name, price, was-price, sale flag, unit price, normalized unit price, package size, image, product URL, product ID, store details, multi-buy deals
- Setup: none. No API key, no account, no proxy.
Each chain prices independently. 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.
Run it
Paste this into the Input tab (switch to JSON view) and hit Start:
{"banner": "saveonfoods","search_terms": ["eggs", "milk", "chicken breast"],"postal_code": "V6M 2P8"}
Rows stream into Output as they arrive. Download from Storage → Dataset as JSON, CSV or Excel.
Three things to change from there:
- Scrape a whole department by swapping
search_termsforcategories, e.g.["fruits-vegetables", "dairy-eggs"] - Price a different store by changing
postal_codeto yours - Switch chains with
banner:pricesmart,urbanfareorqualityfoods
From the API
Grab a token at console.apify.com/settings/integrations:
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", "chicken breast"], "postal_code": "V6M 2P8"}'
run-sync-get-dataset-itemssuits small runs and can time out on large ones- For scheduled or full-store scrapes, POST to
/runsand read the returneddefaultDatasetId - Official JavaScript and Python clients work too
What you get back
{"store": "Save-On-Foods","name": "Organic Whole Milk 2L","price": "5.99","unit_price": "$0.30/100ml","comparable_unit_price": 0.30,"package_size": "2000ml","selling_type": "by_unit","was_price": null,"is_on_sale": false,"multi_buy_deal": null,"image_url": "https://assets.shop.saveonfoods.com/products/...","product_url": "https://www.saveonfoods.com/sm/planning/rsid/1982/product/...","product_id": "00000000123456","category": "dairy-eggs/milk-creams","location": "1982","location_name": "Save-On-Foods Marine Drive"}
| Field | What it holds |
|---|---|
store | Chain name: Save-On-Foods, PriceSmart Foods, Urban Fare or Quality Foods |
name, price | Product name and current price, no $ symbol |
unit_price | Raw unit price string from the store, e.g. "$0.30/100ml" |
comparable_unit_price | Normalized $/100 g or $/100 ml, null when the size will not parse |
parsed_unit_price, parsed_package_size, normalized_package_size | Structured value, quantity and unit forms |
package_size, selling_type | Size string, plus by_weight or by_unit |
was_price, is_on_sale, multi_buy_deal | Pre-sale price, sale flag, promotion text |
image_url, product_url, product_id | Media and identifiers |
category | Category slug, or search:<term> for dedicated search results |
location, location_name, location_postal_code, location_coordinates | The store the prices came from |
Export as JSON, CSV or Excel, pull it through the Apify API, schedule recurring runs, or push it into Zapier, Make, Google Sheets and Airbyte.
Why this Actor
- 705 runs across the suite in the last 30 days, none ended in a failed state. Run stats are public on the Apify API; figures from August 2026.
- Covers four banners from one input. All four run on one platform, so categories and output fields are identical across them. Urban Fare and Quality Foods are the two most competing scrapers leave out.
- Prices down to the individual store. Postal codes resolve against the chain's own live store directory, and every row names the store, its rsid, its postal code and its coordinates.
- Works outside the metros. Because the directory is read at runtime, Nanaimo, Kelowna, Prince George, Calgary, Edmonton, Saskatoon and Winnipeg resolve as reliably as downtown Vancouver.
- Runs without a browser. The only runtime dependency is the Apify SDK, reading the same JSON API the storefront reads, so you pay for no headless Chrome and hit no CAPTCHAs.
- Needs no credentials at all: no API key, no account, no third-party geocoder, no proxy configuration.
- Normalizes unit prices to $/100 g or $/100 ml, and leaves
comparable_unit_priceasnullwhere a package size will not parse, which keeps a bad number out of your comparison. - Refuses out-of-footprint input. A postal code a chain does not serve returns an error naming the provinces it does, so an Ontario query never quietly comes back with a BC store.
What people build with it
- Grocery price comparison apps covering BC, Alberta, Saskatchewan and Manitoba
- Food inflation research, by rerunning a fixed basket weekly and diffing
priceover time - CPG competitor monitoring across all four Pattison banners at once
- Deal sites, pulling
is_on_sale,was_priceandmulti_buy_deal - AI shopping agents that need grounded Western Canada prices
- Regional price studies, comparing Vancouver against Nanaimo, Kelowna, Prince George or Saskatoon
Input
| Field | Type | Required | Description |
|---|---|---|---|
banner | string | yes | saveonfoods, pricesmart, urbanfare or qualityfoods |
categories | string[] | one of | Departments or subcategories; 125 entries (14 departments, 111 subcategories) |
categoryUrls | string[] | one of | Raw category IDs for subcategories missing from the dropdown |
search_terms | string[] | one of | Keyword queries; combined with categories they filter by name instead |
postal_code | string | one of | Canadian postal code; resolves the nearest store. Default V6M 2P8 |
locationId | string | one of | Explicit store rsid, takes priority over postal_code |
- At least one of
categories,categoryUrlsorsearch_termsis required - At least one of
postal_codeorlocationIdis required, so the Actor knows which store to query - Selecting a department queues every subcategory beneath it
- Setting
categoriesandsearch_termstogether filters the category results by product name, with no separate search call, andcategoryholds the category slug - Used on its own, each search term goes to the store's search API and its rows are tagged
category: "search:<term>"
Examples
One subcategory:
{ "banner": "saveonfoods", "categories": ["bakery/breads"] }
Departments and subcategories mixed:
{ "banner": "saveonfoods", "categories": ["meat-seafood", "dairy-eggs/cheese", "frozen/frozen-pizza"] }
A specific store by rsid:
{ "banner": "pricesmart", "locationId": "2274", "categories": ["fruits-vegetables/fresh-fruit", "bakery/breads"] }
Keyword search:
{ "banner": "saveonfoods", "locationId": "1982", "search_terms": ["oat milk", "sourdough bread"] }
Category results filtered by keyword:
{ "banner": "saveonfoods", "postal_code": "V6M 2P8", "categories": ["dairy-eggs/cheese"], "search_terms": ["lactose free"] }
Supported stores
| Banner | Store | Where |
|---|---|---|
saveonfoods | Save-On-Foods | BC, AB, SK, MB |
pricesmart | PriceSmart Foods | Metro Vancouver |
urbanfare | Urban Fare | Vancouver, Kelowna |
qualityfoods | Quality Foods | Vancouver Island, Sunshine Coast |
All four run on one Pattison Food Group platform, so the Actor behaves identically for each.
FAQ
How do I scrape Save-On-Foods prices? Set "banner": "saveonfoods" and give either search_terms or categories, plus a postal_code or locationId. The Run it input above is a working example.
Can I get grocery prices for a specific postal code? Yes. Pass any Canadian postal_code inside the chain's footprint and the Actor resolves the nearest store, then reports its name and rsid on every row.
Does it cover Urban Fare and Quality Foods? Yes, with "banner": "urbanfare" or "qualityfoods". Both run on the same platform as Save-On-Foods, so categories and output fields are identical.
Is there an official Save-On-Foods or PriceSmart API? No. None of the four chains publishes a public product or pricing API.
How do I find a store's rsid? In most cases postal_code is enough. Otherwise browse to your store on the chain's site and read the rsid out of the URL (/sm/planning/rsid/1982/):
- Save-On-Foods:
1982Vancouver Marine Drive,2236Vancouver False Creek,6638Calgary Mount Royal - PriceSmart Foods:
2281Burnaby Station Square - Urban Fare:
7614Vancouver Yaletown - Quality Foods:
4714Nanaimo Harewood
Does it work outside the big cities? Yes. The Actor reads the chain's live store directory rather than a bundled table, so Nanaimo, Kelowna, Prince George, Saskatoon and Winnipeg resolve as reliably as downtown Vancouver.
How do I track prices over time? Schedule the Actor daily or weekly in Apify, keep each run's dataset, and compare price per product_id per store. The Actor returns current prices and stores no history itself.
Is scraping Save-On-Foods legal? Scraping publicly available product and price information is generally legal. Complying with each retailer's terms of service and the law in your jurisdiction remains your responsibility, so keep request volume reasonable.
Canadian grocery Actors
| Actor | Chains | Coverage |
|---|---|---|
| Canadian Grocery Price Comparison API | 16, matched and ranked in one run | Nationwide |
| Loblaws, No Frills, Superstore & Maxi Scraper API - Canada | 12 Loblaw banners | Every province, YT, NT |
| Save-On-Foods, PriceSmart & Urban Fare Scraper API - Canada (this Actor) | 4 Pattison Food Group banners | BC, AB, SK, MB |
| T&T Asian Supermarket Grocery Price Scraper API - Canada | T&T | BC, AB, ON, QC |
| Costco Scraper API - Warehouse Prices & Deals | Costco warehouses | Canada and US |
All four retailer Actors emit the same 21 core fields, among them name, price, was_price, is_on_sale, unit_price, comparable_unit_price, package_size, selling_type and the location set. Their datasets concatenate without a mapping layer. Chain-specific extras such as pc_optimum_offer sit alongside those fields.
Feedback
Found a bug or have technical feedback? Open an issue on the Actor's Issues tab.