Methodology · Canadian Grocery Price Index
How the CGPI is built
A weekly grocery price index computed from a fixed 50-item basket priced across 22 retailer banners and 13 Canadian cities. The headline index is a chained weighted Jevons (methodology v2); the nine category sub-indices are direct fixed-base Jevons indices with the week of 2026-07-13 = 100. Documented end-to-end for the researchers, journalists, and analysts evaluating GroceryPulse as an alternative-data input.
The methodology, applied
4 weekly releases · through Aug 9, 2026Everything documented below produces this series: the national CGPI, recomputed from the full observation history under the current versioned code path and published weekly.
National CGPI, all groceries · published from July 2026 · methodology v2 (chained weighted Jevons) · down 1.3% since inception · explore the index →
How it works
Five automated stages run end-to-end every week, with no human in the loop between collection and publication.
- 01
Collect
Every Thursday at 08:00 UTC an automated pipeline reads publicly accessible retailer pages across 13 cities and 22 banners, extracting prices from server-rendered HTML. Any city below 90% store coverage is automatically re-scraped Friday.
- 02
Match
Each listing is matched to a canonical basket item by product type, then package size within ±10%, then variant. The cheapest valid match is kept.
- 03
Compute
Matched prices feed weighted Jevons (geometric-mean) indices, computed per city and banner and rolled up to a national figure. The headline index is chained: each week links to the previous week over the items priced in both. Category sub-indices are direct fixed-base: each week is compared with the base week of 2026-06-01 over the items present in both.
- 04
Audit
After every run a stratified sample is re-queried against the live sites; any mapping that fails two consecutive weeks is automatically retired from the index.
- 05
Publish
The index is recomputed and published the same week it is collected, giving a weekly read on advertised shelf prices.
Index formula
The headline (all-items) CGPI is a chained, matched-model weighted Jevons price index (methodology v2, adopted 2026-06-12): a geometric mean of week-over-week price relatives, the form recommended by the ILO/IMF Consumer Price Index Manual and used by Eurostat for elementary price aggregates. Each week is compared with the previous week over the items priced in both weeks, so week-over-week changes are same-composition by construction; the index level is the accumulated product of those weekly links. The previous fixed-base series (v1, base week 2026-03-30) was frozen at the 2026-06-12 cutover and remains available via the API with series=v1.
The nine category sub-indices are direct fixed-base matched-model Jevons indices (adopted 10 August 2026), with the week of 2026-06-01 (week ending Sunday 2026-06-07) equal to 100. Each week is compared directly with the base week over the items present in both, and every price relative is formed within a single store, never across stores or banners. Within each product, the top and bottom 10 percent of store-level relatives are trimmed before averaging, which removes residual slot-occupant and price-basis switches that survive the quarantine. Nothing accumulates between weeks, so re-running any week reproduces it, and every category level (national, city, or banner) reads as the change since the base week, which makes category levels comparable with each other. Week-over-week and month-over-month changes are the ratio of the two published levels (null after a missing week), so the tables reconcile with their own columns. Chaining each category over its 4 to 7 matched items had let sample churn accumulate into the published level; the direct comparison removes that failure at the cost that a fixed base ages, as documented in the revision log below.
Substitution-bias correction
The geometric mean implicitly accounts for consumers switching to cheaper alternatives when one product's price rises — the arithmetic mean of a Laspeyres index does not.
Symmetric price changes
A +50% increase and a −50% decrease offset each other exactly under Jevons. Under an arithmetic mean they do not, which biases the index upward.
Worked example
Compute a weekly Jevons link yourself
Change any price below and the index recomputes as you type. Four items from the real 50-item basket, at their real basket weights. Everything on this panel is arithmetic you can reproduce in a spreadsheet in about two minutes, which is the point.
Basket weight 0.030 · share of this example 26.1%
- Relative P2 / P1
- 1.0274
- Change
- +2.74%
- ln(relative)
- +0.02707
- w · ln(relative)
- +0.00706
Basket weight 0.040 · share of this example 34.8%
- Relative P2 / P1
- 1.0000
- Change
- 0.00%
- ln(relative)
- 0.00000
- w · ln(relative)
- 0.00000
Basket weight 0.020 · share of this example 17.4%
- Relative P2 / P1
- 1.0172
- Change
- +1.72%
- ln(relative)
- +0.01705
- w · ln(relative)
- +0.00296
Basket weight 0.025 · share of this example 21.7%
- Relative P2 / P1
- 1.0258
- Change
- +2.58%
- ln(relative)
- +0.02545
- w · ln(relative)
- +0.00553
The weekly link, with your numbers in it
link = exp( Σ wᵢ · ln(Pᵢ,2 / Pᵢ,1) ) = exp( 0.261 × ln(7.49 / 7.29) + 0.348 × ln(15.99 / 15.99) + 0.174 × ln(3.55 / 3.49) + 0.217 × ln(1.99 / 1.94) )
= exp( +0.015557 )
= 1.015679Why geometric, and not an arithmetic mean
Feed the identical prices and identical weights into a weighted arithmetic mean of the same relatives — the Laspeyres form — and it reads higher. It always does: the arithmetic mean of a set of positive numbers is never below their geometric mean, and the gap widens with dispersion. Try One item spikes and watch them separate.
exp( Σ wᵢ · ln rᵢ )
Σ wᵢ · rᵢ
Every week, compounding into the level.
The gap is substitution bias: an arithmetic mean implicitly assumes shoppers buy the same quantities whatever happens to relative prices, so an item that spikes drags the whole index with it, while a geometric mean allows for the shopper who buys less of what just got expensive.
Symmetry check: apply this week's changes, then reverse them exactly
A price that rises 50% and then falls 50% is back where it started, and an index that claims otherwise is inventing inflation. Chaining your week-2 relatives and then their exact inverses:
Jevons returns to exactly 100 for any set of price movements. The arithmetic mean lands above 100 whenever prices moved at all — a permanent upward drift manufactured by the formula rather than by the market. That is the whole reason the ILO/IMF CPI Manual and Eurostat recommend the geometric form for elementary aggregates, and the reason the CGPI uses it.
View the term-by-term arithmeticHide the term-by-term arithmetic
| Item | Weight | w (renorm.) | Week 1 | Week 2 | Relative | ln(rel) | w · ln(rel) |
|---|---|---|---|---|---|---|---|
| 2% milk | 0.030 | 0.2609 | 7.29 | 7.49 | 1.0274 | +0.02707 | +0.00706 |
| Chicken breast, boneless skinless | 0.040 | 0.3478 | 15.99 | 15.99 | 1.0000 | 0.00000 | 0.00000 |
| White bread | 0.020 | 0.1739 | 3.49 | 3.55 | 1.0172 | +0.01705 | +0.00296 |
| Bananas | 0.025 | 0.2174 | 1.94 | 1.99 | 1.0258 | +0.02545 | +0.00553 |
| Σ w · ln(rel) | +0.01556 | ||||||
| Index = 100 × exp( Σ ) | 101.57 | ||||||
This is an illustration, not published data. The prices above are plausible placeholders you can overwrite; no figure on this panel is a GroceryPulse observation. The published headline index runs the same link over the full matched item set at every city × banner cell, chains the weekly links into a level, and publishes a cell only where at least 60% of its basket slice (and at least two items) is priced in both weeks being linked. Because each headline series is rebased to 100 in its own entry week, headline levels are not comparable across cities or banners. The nine the headline and its category sub-indices are computed direct against the July 2026 base week (= 100), so category levels are comparable with each other.
The basket
50 products across 9 categories, weighted to approximate a typical Canadian household's grocery spend.
Weights approximate Statistics Canada's Food purchased from stores sub-basket. The category shares above are normalized and total 100%. The per-item weights in the full product list below are the raw ones, and those total 96.0% rather than 100%. That is deliberate, not a rounding gap: the raw weights are relative importances, and the index normalizes them before computing, then normalizes again inside each weekly link over the items actually priced in both weeks. An item missing at one banner is never treated as zero; its weight is redistributed across the items that are present. Bars are scaled to the largest category for legibility.
Full product listall 50 items with weights
| # | Product | Category | Weight |
|---|---|---|---|
| 1 | 2% Milk, 4L | Dairy & Eggs | 3.0% |
| 2 | Butter, Salted, 454g | Dairy & Eggs | 2.5% |
| 3 | Large Eggs, 12-pack | Dairy & Eggs | 3.0% |
| 4 | Cheddar Cheese, Medium, 400g | Dairy & Eggs | 2.5% |
| 5 | Greek Yogurt, Plain, 750g | Dairy & Eggs | 2.0% |
| 6 | Cream Cheese, 250g | Dairy & Eggs | 2.0% |
| 7 | Chicken Breast, Boneless Skinless, per kg | Meat & Protein | 4.0% |
| 8 | Ground Beef, Lean, per kg | Meat & Protein | 3.5% |
| 9 | Pork Loin Chops, per kg | Meat & Protein | 2.5% |
| 10 | Bacon, 375g | Meat & Protein | 2.0% |
| 11 | Salmon Fillet, Atlantic, per kg | Meat & Protein | 3.0% |
| 12 | Canned Tuna, Chunk Light, 170g | Meat & Protein | 1.5% |
| 13 | White Bread, 675g | Bread & Bakery | 2.0% |
| 14 | Whole Wheat Bread, 675g | Bread & Bakery | 2.0% |
| 15 | Bagels, ~6-pack | Bread & Bakery | 1.3% |
| 16 | English Muffins, 6-pack | Bread & Bakery | 1.3% |
| 17 | Hamburger Buns, 8-pack | Bread & Bakery | 1.4% |
| 18 | Bananas, per kg | Produce — Fruits | 2.5% |
| 19 | Apples, ~2kg Bag | Produce — Fruits | 2.0% |
| 20 | Strawberries, 454g | Produce — Fruits | 2.0% |
| 21 | Oranges, per kg | Produce — Fruits | 1.8% |
| 22 | Blueberries, Fresh 454g (1lb) | Produce — Fruits | 1.8% |
| 23 | Potatoes, 10lb Bag | Produce — Vegetables | 2.0% |
| 24 | Onions, 3lb Bag | Produce — Vegetables | 1.5% |
| 25 | Carrots, 2lb Bag | Produce — Vegetables | 1.5% |
| 26 | Broccoli, per Head or Crown | Produce — Vegetables | 1.8% |
| 27 | Tomatoes, per kg | Produce — Vegetables | 1.8% |
| 28 | Romaine Lettuce, per Head | Produce — Vegetables | 1.5% |
| 29 | White Rice (Long Grain), 2kg | Pantry Staples | 2.0% |
| 30 | Pasta, Spaghetti, 900g | Pantry Staples | 1.7% |
| 31 | Canned Tomatoes, Diced, 796mL | Pantry Staples | 1.3% |
| 32 | Peanut Butter, 1kg | Pantry Staples | 1.5% |
| 33 | Canola Oil, 946mL | Pantry Staples | 1.5% |
| 34 | Granulated Sugar, 2kg | Pantry Staples | 1.0% |
| 35 | All-Purpose Flour, 2.5kg | Pantry Staples | 1.0% |
| 36 | Frozen Pizza, Pepperoni | Frozen | 2.0% |
| 37 | Frozen Mixed Vegetables, 750g | Frozen | 2.0% |
| 38 | Ice Cream, 1L | Frozen | 2.0% |
| 39 | Frozen Chicken Nuggets, 700g | Frozen | 2.0% |
| 40 | Orange Juice, 2.63L | Beverages | 1.8% |
| 41 | Ground Coffee, 300g | Beverages | 1.8% |
| 42 | Tea Bags, ~72-pack | Beverages | 1.0% |
| 43 | Coca-Cola, 2L | Beverages | 1.3% |
| 44 | Bottled Water, 24-pack 500mL | Beverages | 1.3% |
| 45 | Paper Towels, ~6-roll | Household Essentials | 2.0% |
| 46 | Toilet Paper, 12-roll | Household Essentials | 2.0% |
| 47 | Dish Soap, 535mL | Household Essentials | 1.5% |
| 48 | Laundry Detergent, 1.47L | Household Essentials | 2.5% |
| 49 | Garbage Bags, 40-count | Household Essentials | 1.0% |
| 50 | Chicken Thighs, Bone-In, per kg | Meat & Protein | 2.5% |
Coverage & collection
22 active banners across the three largest Canadian grocery parents plus two regional players — spanning discount and full-service formats in every province. Super C is pipeline-supported but currently has no active stores, so it is listed below for completeness without counting toward the 22.
Loblaw Companies
10 banners- Loblaws — Ontario full-service flagship
- No Frills — national discount banner
- Real Canadian Superstore — large-format (Western Canada and Ontario)
- Atlantic Superstore — large-format (Atlantic provinces)
- Dominion — Newfoundland full-service
- Maxi — Quebec discount banner
- Provigo — Quebec full-service
- Fortinos — Ontario full-service
- Wholesale Club — bulk / small-format
- Your Independent Grocer — smaller-market full-service
Empire Company (Sobeys)
8 banners- Sobeys — national full-service
- FreshCo — national discount banner
- IGA — Quebec full-service
- Safeway — Western Canada full-service
- Foodland — smaller-market full-service
- Thrifty Foods — BC full-service
- Farm Boy — Ontario specialty (via Voilà delivery)
- Voilà — Sobeys online grocery delivery
Metro Inc.
3 banners- Metro — Ontario / Quebec full-service
- Food Basics — Ontario discount banner
- Super C — Quebec discount banner (pipeline-supported; no active stores currently tracked)
Independents / regional
2 banners- Save-On-Foods — Jim Pattison Group, Western Canada
- Giant Tiger — discount / general merchandise with grocery
Source
Prices come from publicly accessible retailer websites — the same product pages any consumer can view. Price data is extracted from the server-rendered HTML.
Product matching
- 1. Type — category must match (milk, butter, chicken breast…).
- 2. Size — package size within ±10% of the target.
- 3. Variant — specifics checked (salted butter, whole-wheat bread).
The cheapest valid match is used, reflecting shoppers' tendency to choose store brands and sale items.
Schedule
Collected every Thursday at 08:00 UTC (04:00 Eastern), with an automatic Friday catch-up for any city below 90% store coverage. A complete run covers all 13 cities and 22 banners; runtime is typically 10–30 minutes depending on retailer response times and rate-limit backoffs. The index is recomputed after each run.
Quality assurance
After every weekly scrape, an automated audit re-runs the matching pipeline for a stratified sample against the live retailer sites — effectively re-scraping those items on a different day — and retires any mapping it can no longer match, so broken matches don't persist in the index. These re-scrape checks confirm reproducibility and mapping liveness, but they re-run the same pipeline — they are not independent evidence of accuracy. A second tier of independent, browser-based spot audits reads prices off the retailer page the way a shopper would; that sample is still small (70 audits to date), so we do not publish a headline accuracy percentage. Results from the stratified monthly audit program (target N ≥ 100) will be published here as they accumulate.
Re-run, don't re-read
The audit re-runs the same search-and-match pipeline the scrape uses, then checks whether a fresh run still finds a valid match for each sampled item — it reproduces the scrape rather than re-reading a single stored SKU.
Drift vs. defect
A re-run that finds the item at a different price is recorded as ordinary week-to-week price movement, not an error. Only a failure to find any spec-valid match flags a genuinely broken mapping.
Auto-retirement
A mapping is retired from the index only after a fresh scrape finds no valid match for it two weeks running — long enough to ride out a transient outage, quick enough to catch real breakage.
Known coverage gap
Voilà (and Farm Boy via Voilà delivery) cannot yet be re-checked by the automated audit — its consumer search interface requires an active postal-code session the pipeline cannot replay. Voilà observations are still collected in the normal scrape; they just don't receive the live re-check the other families do. Extending the audit loop to the full 22-banner panel is on the roadmap.
A formal validation study will be published once enough production history has accumulated. We make no claim of predictive power over any other price series in the meantime. Method notes are documented in the CGPI whitepaper.
What the CGPI measures
The CGPI measures advertised prices: the posted shelf price a shopper sees online, for a fixed basket, every week. Official consumer price statistics measure transaction prices, drawn largely from retailer scanner data, across a much broader basket, monthly. These are different measurements of different things. Neither substitutes for the other, and they should not be expected to move identically. The table below sets out where they differ.
| Feature | CGPI | Official food CPI |
|---|---|---|
| What is measured | Advertised price posted online (sale-aware and regular both captured) | Transaction price paid at the till |
| Price source | Public retailer listings, collected first-party | Largely retailer scanner data, supplemented by field and web collection |
| Update frequency | Weekly (Thursdays) | Monthly |
| Formula | Headline: chained weighted Jevons (geometric mean, methodology v2). Categories: direct fixed-base Jevons, base week of 2026-07-13 = 100 | Modified Laspeyres (arithmetic mean) |
| Basket size | 50 representative items | several hundred representative food products (list revised monthly; no fixed count published) |
| Geographic scope | 13 cities across all 10 provinces | National + provincial |
| Banner coverage | 22 retailer banners (incl. all three parents: Loblaw, Metro, Empire) | Not disclosed at banner level |
| Transparency | Full basket & methodology published | Basket weights published, individual prices not |
Revision log
Price observations are append-only: once written, an observation is never updated or deleted. Corrections are made at read time or as superseding rows, never as edits to history. Index values are recomputed under the current versioned code, and every published value stays retrievable as it was first published through the point-in-time vintage archive.
What happened. An institutional reader flagged on 8 August 2026 that the published category sub-indices did not reconcile with the headline index. Weighted together, the nine category series read roughly +0.5% since the week of 2026-06-01 while the headline read roughly −2.0% over the same window, and category paths contained moves no shelf price made: household down 15.8% in a single week, frozen up 36% in two months. Those were changes in the matched sample, not changes in prices.
Why. Each category sub-index was chained weekly over only 4 to 7 matched items, and that matched set churns week to week as items drop out and return, with sale-price bouncing amplifying the effect. Every substitution left a small permanent error in the accumulated level. The headline carries 44 to 47 of the roughly 50 basket items each week, so the same churn averages out; it was checked against a direct matched-pair computation and stands.
Effect. Published category levels and percentage changes were wrong by up to roughly five percentage points in either direction. The headline (all-items) index was unaffected.
Remedy. Every published category week was restated on 10 August 2026 as a direct fixed-base matched-model Jevons index: each week is compared directly with the base week of 2026-06-01 (= 100), every price relative is formed within a single store, and nothing accumulates between weeks, so re-running any week reproduces it. The headline methodology is unchanged, and the headline rows were machine-verified byte-identical before and after the revision. Pre-revision category values remain retrievable exactly as published through the point-in-time vintage archive (?asof= on /api/v1/index).
Still limited. A direct fixed-base comparison ages: coverage of the base week decays as the store panel changes. A category week publishes only when at least 60% of its slice matches the base week, a warning fires when national coverage falls below 75%, and if coverage erodes the series will be re-based with disclosure rather than patched. A store or banner onboarded after the base week contributes no price relatives, and a banner cell with no base-week data publishes no category series.
What happened. Our collection recorded the regular price at Sobeys, Safeway, FreshCo, Foodland, IGA, Thrifty Foods, Voilà and Farm Boy, and missed the promotional prices those chains were running. At the Metro banners and Save-On the recorded price was correct; only the sale label was missing.
Why. Two separate collection defects. The Empire banners publish the promotional price in a field our scraper never requested, and an earlier fix targeted a field that is never populated, so it had never taken effect. For Voilà the promotional price arrived in every response and was discarded before it reached the parser.
Effect. Prices at the affected chains read high in any week where an item was on promotion, measured at roughly 2.8% to 3.0% against the retailers' own published payloads. Because those chains sit at the expensive end of most cross-chain comparisons, spreads published for affected weeks are overstated. The direction of those findings is unchanged: discount chains are cheaper by a large double-digit margin.
Remedy. Corrected in collection on 3 August 2026. History has not been rewritten. Corrected figures are published alongside the originals rather than replacing them, and pre-revision index values remain available through the vintage archive.
Still limited. Save-On-Foods publishes its reduced price with no pre-promotion reference, so its promotions have a knowable window and an unknowable depth. Promotional depth remains available for the Loblaw banners, Giant Tiger, the Empire banners and Voilà only.
Limitations
Disclosed openly in the spirit of transparency. The index is designed to complement, not replace, official statistics.
Online prices only
In-store prices may differ, especially for produce and meat.
13 cities
Coverage spans major cities across Canada, but rural areas are not tracked.
No Costco or Walmart
Bot protection prevents reliable automated price collection from these retailers.
50-item basket
The official food basket tracks several hundred representative products (the list is revised monthly). A tighter basket may not capture every price movement, especially in less-frequently-purchased categories.
No quality adjustment
If a product is reformulated we track the price change but not the quality change. Shrinkflation (pack-size reduction) is tracked explicitly; other reformulations are not.
Commercial access
The full per-observation panel, weekly CSV pack, and point-in-time Parquet dumps are available via commercial license, with a 14-day evaluation trial.