Project frozen · 2026-05-26 snapshot

Hong Kong grocery price intelligence

Two million grocery prices. One honest wall.

My wife noticed no single Hong Kong shop carries everything she wants. I noticed foodpanda often charges more than the shop's own website. So I read the publicly listed price of nearly everything, eleven retailers deep, and let the numbers talk.

Here's the pattern that fell out: foodpanda is genuinely convenient, and almost every shop quietly makes you pay for that convenience — a flat tax, a fake sale, a gouge on what you'll cave on, a pricier till. The only real variable is how honestly they show it. (One shop, M&S, just eats the cost.)

Then I parked it, on purpose. This whole dashboard runs inside your browser tab. No server. No database humming in a closet. Just SQL on Parquet, right here. Scroll down and poke it.

2,027,565
price observations
1,147
stores
11
retail sources
156,433
product families
110,508
barcodes
warming up…

The convenience tax

Walk, or let foodpanda walk for you?

Same product. Same shop. Two prices. One for your feet, one for your couch. For every product we could match between a chain's own website and its foodpanda delivery page, we measured the gap. That gap is the channel premiumChannel premium: how much more the same item costs on foodpanda delivery versus buying it direct from the retailer's own website. Measured per matched product, then summarised per chain..

Bars = the average markup and the painful 90th-percentile markup, per chain. The couch is rarely free.

Delivery markup by chain

foodpanda price vs chain-direct price, per matched product family

+20%
city'super adds a flat fifth on delivery. Same trolley, +20%, across ~3,007 matched items.
+0%
Marks & Spencer barely blinks. 97% of 2,660 items sit at exact parity online and delivered.
+13.2%
Wellcome's average. But the top tenth of items? +50%. Read the small print.

The shop scoreboard

Start here. Which chains lean on the couch tax, ranked by average markup. The shop is the story; the products are the evidence.

ShopAvg markupWorst 10%Items dearer on deliveryProducts compared
querying…

city'super and M&S bridge by exact product id (not name), so their rows are dead reliable: city'super tacks a flat fifth onto almost everything; M&S charges you the same whether you walk or wait.

…and the products that prove it

Biggest single-item gaps we found (direct shelf price vs foodpanda median, same retailer, matched products)

ProductChainCategoryDirectfoodpandaMarkup
querying…
The maths · channel premium

foodpanda price minus the shelf price, over the shelf price. That one percentage is the whole section. city'super charges 20% of it; M&S, basically nothing.

Why delivery costs more
Visual explainer slot — a plain-language illustration of this concept lands here.

The whole market, properly

So how much is the foodpanda tax, really?

We have two separate price worlds: the shops' own websites (the source of truth) and foodpanda's listings. Match every product we can across both — 10,244 of them — and ask the global question with proper, outlier-robust math. The honest answer is that there are several honest answers, and which one gets quoted is itself a choice.

+0%
the typical product (median) — most sit at parity
+6.8%
your whole basket (buy one of everything)
+9.6%
the average product (dragged up by the markups)
+232%
the worst single item (Dr. Groot, Mannings)

Across the matched market: 41% of products are dearer on foodpanda, 53% sit at exact parity, and 5% are actually cheaper. Trim the 5% tails off each end and the robust mean is +7.9%.

So is foodpanda “0% more,” “7% more,” or “232% more”? All three are true at once. The honest number for a real weekly basket is about +7%; the median hides it at zero, the tail screams +232. Quote whichever suits you and you can prove almost anything — which is exactly why every figure on this page leans on the median and a robust scale, never a mean someone can cherry-pick. The convenience tax is real, modest in aggregate, and living almost entirely in the tail.

The platform's cut

Who actually pays foodpanda's commission?

That delivery markup isn't always greed. foodpanda charges every merchant a commission on every order (restaurants are widely reported at 25–35%; grocery rates stay private). The real question isn't whether the cut exists — it's who decides to eat it. And the shape of each shop's markup gives the game away.

The tell: a markup with no variance is a rule, not a price

city'super delivery vs its own shelf · 2,996 matched items · 2026-05-26

×1.20
every item, on the nose
20.0–20.8%
the entire interquartile range
2,335 / 2,996
items land in the +20% bucket

That isn't a thousand pricing decisions. It's one: multiply the shelf by 1.20 and ship it. A flat, machine-clean markup with almost no spread is the fingerprint of a commission passed straight through. So city'super's “delivery price” is, almost certainly, foodpanda's cut — relabelled as the product price and handed to you. Which lets you do the thing the platform would rather you couldn't: read foodpanda's take-rate straight off the shelf, with no contract and no inside data.

Pass it on

city'super

Every item ×1.20. The shopper pays foodpanda's cut, not the shop. The premium logic writes itself: “we're city'super — why would we eat a fee for someone who couldn't be bothered to visit?”

Eat it

M&S · Watsons

Around 97% of items at exact parity. The shop swallows the commission to keep the order in-app. Delivery is a loyalty loss-leader, not a profit centre.

Pick their spots

Mannings · Wellcome

~62% at parity, then a hard-gouged tail (+50% to +232%). They absorb the cut on staples and turn convenience items into profit centres. The commission becomes a scalpel.

This is the question a CPG brand or foodpanda itself pays for — not “prices are high,” but who is stacking their own margin on top of the platform's commission, quietly suppressing the very orders the brand and the platform both want. Same two million public prices. A different, far more expensive question.

The hunt

Why you can't win the basket in one shop

It started with my wife. It's not that she can't find things in one place. It's that the cheapest version of each thing keeps landing in a different shop. I wanted to prove or kill that hunch. So I took six real items and checked the walk-in shelf price at two shops. Not delivery, not promotions. Just the price you'd pay standing in the aisle.

Watch the cheaper shop flip on every single line. And these two shops are owned by the same company.

One basket, two shops, the winner flips every row

real walk-in shelf prices · 2026-05-26 snapshot · Wellcome and Market Place by Jasons (both DFI)

ItemWellcomeMarket PlaceCheaper at
555 Fried Sardines 155g$9.0$17.5Wellcome
Cobs Sea Salt Popcorn 80g$45.0$25.0Market Place
Nestlé All-Purpose Cream 250ml$20.0$37.5Wellcome
Walkers Shortbread 150g$55.0$33.0Market Place
Oishi Crackers 90g$12.0$21.0Wellcome
French Cellars Cabernet 250ml$34.0$17.0Market Place
$175
whole basket at Wellcome
$151
whole basket at Market Place
$116
cheapest, if you split across both

Splitting the basket beats the cheaper single shop by 23%. And these are sister shops, same owner. Now picture doing it across real rivals — except they won't publish a barcode to let anyone compare (that's the wall at the bottom). So you can't optimise. You hunt by memory and luck. That isn't chaos in the data. That's the strategy working exactly as designed.

The maths · why splitting wins

Add up the cheapest version of each item and you beat the best single shop almost every time. Here: $116 split versus $151 at one shop. That gap is your whole reward for hunting — and why one trip never wins.

Today's bait

real markdowns pulled from the snapshot — the hooks that get you through the door

Black Thunder Mini Choc 139g$63$16
Campbell's Trolley Cart$399$50
Wah Yuen Butter Egg Rolls 18pc$129$35
Bonaqua Water 24×770ml$192$59
Black Thunder Almond & Hazelnut$63$16
Headache capsules 60pc (Mannings)$199$50

The evidence: every chain has a personality

x = price level vs market · y = how much prices wobble (CVCoefficient of variation: standard deviation ÷ mean. Low = steady, predictable prices. High = constant promotions and reversals, i.e. Hi-Lo.) · bubble = SKUs compared

CV 2.30
Mannings, the wildest swings in town. You genuinely cannot guess if today's price is a gift or a gouge. So you check. Every time.
CV 0.47
Wellcome keeps you guessing too — steady on the surface, spiky underneath.
flat
A handful of shops barely move. Boring is a feature: you can trust the price without a spreadsheet.
The basket you can never fill cheaply in one place
Visual explainer slot — a plain-language illustration of this concept lands here.

Even within one shop

The same Donki, two different tills

Don Don Donki has no website — foodpanda is its only shelf, spread across 17 storefronts. And those storefronts don't agree. Order the same item from a different Donki and it can cost a fifth more (a median swing of ~20% on items sold at three or more of them).

And it isn't random noise — it's a structured ladder. The small “Donki Beauty” shops sit cheapest (~3% over the floor); full stores and extended-delivery zones run dearer (~7–14%). The same mall can even host two Donki storefronts at two prices. (Caveat: we only see the foodpanda listings, so this is delivery pricing — the price you actually pay on the app — not a claim about the in-store shelf.)

Same product, different storefront, different price

cheapest vs dearest Donki storefront · 2026-05-26 snapshot

ItemStoresCheapestDearestSwing
Ichiran Tonkotsu Ramen 138g11$34.9$54.0+55%
Honeyce Honey Hair Mask 200g11$29.9$49.9+67%
US Angus Ribeye Steak 250g8$120$168+40%
Rohto Z! Pro Eye Drops 12ml15$55.9$69.9+25%

Does anyone else do this? No.

share of same-item prices that vary across a chain's own foodpanda storefronts

ChainStorefrontsItems that vary
Don Don Donki1736%
AEON1213%
Mannings131~0%
PARKnSHOP62~0%
Wellcome26~0%

This is the real punchline. Mannings holds a single price across all 131 of its foodpanda storefronts; PARKnSHOP across 62; Wellcome across 26 — one head-office price, every location, every delivery zone. Donki lets each storefront set its own. Same platform, opposite philosophy.

And no — tempting as it is, this isn't foodpanda's 20% in disguise. city'super's commission shows up as a clean ×1.20 on everything (a rule); Donki's spread is a continuous smear with no step at 20% (a per-storefront ladder). Same headline number, different cause — which is why we trust the shape, not the average. And no, it isn't the rich districts gouging: the Central store (100 Queen's Road) is mid-pack, while the dearest Donki is a working-class extended-delivery zone (Kwun Tong) — logistics and store format, not postcode. The takeaway for a shopper: even at the fair shop, the storefront you tap is worth checking.

Promo intensity

Who is always “on sale”?

A big red SALE sticker on half the shelf is a strategy, not a coincidence. Share of each chain's catalogue carrying a discount on snapshot day.

Share of catalogue on promotion

percent of priced SKUs marked down · snapshot 2026-05-26

The tell

Read the last digit, read the shop

A price ending in .90 is a small trick played on your eye: you read “$9 and change,” not “$10.” (Hermann Simon: the further right a digit sits, the less your brain weighs it.) So how a shop ends its prices is a quiet confession of who it's pretending to be. This is charm pricingCharm pricing: setting a price just under a round number ($9.90 not $10) so it feels cheaper. A round ending ($10.00) signals the opposite — quality, confidence, no gimmick. Hong Kong skips the Western .99 and splits between .90 and fully round..

Two camps fall out of the data, and they're exactly who you'd guess.

Playing cheap
Everything ends in .90 — Donki 87%, AEON 71%. The whole shop whispers “look how affordable.”
Pretending premium
Clean round numbers — city'super 92%, Wellcome 74%. The flex is refusing the trick at all.

How prices end, by chain

share of prices ending in .90 (deal-feel), round .00 (premium), and the rare .99

Why $9.90 lands differently than $10.00
Visual explainer slot — a plain-language illustration of this concept lands here.

Ghosts in the data

When a 2-litre bottle of water costs $9,999

Sift two million prices and you meet the ghosts. 26 listings priced at exactly HK$9,999. Nobody is paying ten thousand dollars for two litres of Kirin water. It's a placeholder — a price typed by a human who didn't want anyone to click buy.

HK$9,999
Kirin Japan Soft Water 2L · AEON
The lazy out-of-stock. Marking an item unavailable on these platforms is fiddly clicks; typing a number no human will ever pay is one. So the water “exists,” technically, at a price designed to be ignored. AEON does this 17 times; JHC Japan Home, 9.
HK$9,999
Georgia The Black coffee 500ml · AEON
Or it's a plain fat-finger — someone meant $19.90 and a zero ran away. Either way, a naive “average price” would swallow these whole and lie to you. It's exactly why the charts above lean on robust statisticsRobust statistics: methods (median, modified z-score, Qn scale) that ignore a handful of absurd values instead of letting them drag the average. A single $9,999 ghost can't move a median. instead of a plain mean.
The maths · the lie detector — modified z-score + Qn scale (Iglewicz & Hoaglin 1993; Maronna, Robust Statistics 2e)

Exactly how the dashboard ignores those ghosts: it scores each price against the median (which a typo can't drag) over Qn, a spread ruler that still works when half the data is junk. More than 3.5 units out and it's a fluke, not a deal.

Volatility by aisle

Where prices swing the most

Toilet paper: boring, everyone charges about the same. Hot pot and fresh seafood: a casino. The price spreadPrice spread: how far apart the cheapest and most expensive store are for the same product, as a percentage. Big spread = shopping around actually pays. within a category tells you where it's worth shopping around.

Median price spread by category

how much the same item varies store-to-store · higher = shop around

The maths · shop, or shelf? — Tukey's median polish, Exploratory Data Analysis 1977

When an aisle looks pricey, is it the shop or the product? We split each price into a baseline, a “this shop” effect, a “this aisle everywhere” effect, and a leftover — the tell, the only honest signal that a shop specifically marks up that aisle.

The whole haystack

Two million rows. In this tab. Right now.

The charts above ran on tidy little summaries. This runs on everything: 2,027,565 raw price observations, a single 33 MB ParquetParquet: a columnar file format. It stores data by column instead of by row, which compresses far better and lets a reader grab just the columns and chunks it needs. Our 10 GB of raw listings became one 290 MB Parquet, then 33 MB once trimmed. file sitting on object storage. Your browser reaches in and grabs only the bytes it needs, using HTTP range requestsHTTP range request: asking a server for just bytes 1,000–2,000 of a file instead of the whole thing. DuckDB uses the Parquet file's index to fetch only the chunks a query touches, so a query over 2M rows might download a few MB, not 33..

This is DuckDB-WASMDuckDB-WASM: the DuckDB analytical database compiled to WebAssembly (~3.5 MB), running entirely in your browser. Real SQL, no backend, no API. This is the 2026 way to ship a data product with zero servers. doing real analytical SQL with nothing behind it. Edit the query. Hit run. Watch a laptop-grade database chew through two million rows from a static file.

First run downloads the DuckDB engine and the file's index, then streams only what each query needs.
Run a query to pull live rows from the firehose.

The verdicts

The Hong Kong Grocery Awards

Here's the thing we can't hand out: a “cheapest shop” trophy. On the items we can compare, every chain sits within a few percent of the others on price — nobody is meaningfully cheaper across the board. The differences worth changing your habits over aren't about price level. They're about behaviour: who gouges you on delivery, whose sales are theatre, who just plays it straight. So those are the awards we could actually back with the numbers.

The maths · how we refuse to crown — the bootstrap, Efron & Tibshirani 1993

Before calling anything “cheapest” we reshuffle the basket 500 times, redraw the items at random, and re-run the contest. If a shop only wins sometimes, we don't crown it — we say so. That's why there's no “cheapest shop” trophy above.

★ Honest Cheap
Don Don Donki
lowest price vs market (~4% under) · 0% delivery gouge
The closest thing to a value winner, and the only one that earns it honestly: steady prices, no fake markdowns, and since foodpanda is its only shelf there's no walk-in price to mark up against. Order the household and Japanese stuff and don't think twice. (You still have to survive the store.)
King of the Couch Tax
city'super
+20% flat on delivery, ~3,007 items
The most systematic delivery markup we found. Same trolley, a fifth more, on almost everything, every time. The cheese is worth it; the delivery isn't. Walk if you possibly can.
The Permanent Sale
Market Place by Jasons
54% “on sale” · 1 in 3 dearer than Wellcome regular
More than half the shop wears a markdown sticker. But a third of those “deals” cost more than just buying the same item at full price in its sister shop Wellcome. The discount is decoration. Ignore the red tags.
Markup Champion
Mannings
+36% above market on average · +232% worst delivery gouge
The dearest of all before you catch a promo, and the wildest swings in town. Genuinely great if you time the rotation, genuinely brutal on autopilot. Never, ever pay its full price.
★ Fairest Delivery
Marks & Spencer
97% of items, identical price walk or deliver
Treats delivery as the same channel as the shop floor. Premium prices, sure, but no couch tax on top. Tap “deliver” with a clear conscience.
★ No Drama
AEON
CV 0.04 · the steadiest prices on the platform
No Hi-Lo games, no whiplash. What you saw last week is what you'll pay today. Boring, and boring is a feature when everyone else is running a casino.
The maths · the real-deal test — reference pricing, Nagle, Strategy & Tactics of Pricing 7e

A sticker shouts “50% off.” The honest question is 50% off what. Subtract what you actually save versus the going rate from the claimed discount. Some sellers here claim 50% off and save you 0% — the whole “discount” is that gap. That's how Market Place's theatre got caught.

So my wife was right all along. No single shop wins on price, no app will fix it, and the reason it can't is sitting one section down: the shops hide the one number that would let anyone compare. Until that changes, the hunt is the job — but now at least you know which shops to trust, which to time, and which to walk past.

Free consulting

Open letters to the shops

We roasted you. Now here's the part you'd normally pay a firm for. Each note is one number away from more orders or less resentment — and the number is sitting in the charts above. Every one ends with a P.S.: the deeper, more valuable thing the same two million prices can already tell you. Dear management:

An open letter to
Market Place by Jasons

54% of your shelf wears a markdown sticker — but 1 in 3 of those “deals” is dearer than the same item at full price in Wellcome, your own sister shop. Shoppers have learned to read your red tags as noise.

Our recommendation: retire the permanent sale. A believable “was” price you actually honour beats a 50%-off sticker no one trusts. You're a deli — sell the quality, not the theatre.

P.S. — and yes, we can score every one of your “deals” against Wellcome's shelf, SKU by SKU, and hand you the exact list of red tags that are quietly costing you trust.

— Grocery Intel, on behalf of the people ignoring your stickers
An open letter to
city'super

Your delivery quietly adds a flat +20% across ~3,007 items — the most systematic markup on the platform. The customers who can walk to your shelf, do. The rest feel it eventually.

Our recommendation: show one honest delivery fee at checkout instead of burying a fifth inside every SKU. Premium shoppers forgive a clear fee; they resent a hidden tax. You've earned the trust — stop spending it.

P.S. — and yes, we can back out foodpanda's effective take-rate on your catalogue from the shelf alone, then benchmark your flat +20% against how every rival chose to handle the very same cut.

— Grocery Intel, fans of your cheese, not your couch tax
An open letter to
Mannings

You sit +36% above market before any promo, with the wildest swings in town and a +232% worst case (a HK$59.90 scalp serum listed at HK$199 on delivery). Those are the screenshots that become your reputation.

Our recommendation: cap the channel markup. One absurd delivery price does more brand damage than a hundred fair ones do good. Put a ceiling on it before someone else makes the meme.

P.S. — and yes, we can run this daily, flagging every SKU the moment its markup crosses from “fair” into screenshot territory. A live feed, not a quarterly slide.

— Grocery Intel, who took the screenshot so you don't have to
An open letter to
AEON

Good news first: you're the steadiest, most honest-feeling shop here (CV 0.04). The catch: 26 of your listings sit at HK$9,999 placeholder prices, and your barcodes are internal, so no comparison tool can see how cheap you actually are.

Our recommendation: kill the $9,999 ghosts (they make you look broken) and publish real GS1 barcodes. You'd win every price-comparison anyone ever builds — you're already cheap and steady, just let people prove it.

P.S. — and yes, the day you publish barcodes we can hand you the exact categories where you already undercut Wellcome and ParknShop. Your comparison ads, pre-written.

— Grocery Intel, quietly rooting for you
An open letter to
Wellcome

Your foodpanda markup averages +13%, but the top tenth of items hits +50% — bimodal and unpredictable. That trains shoppers to double-check before every single order, which is the opposite of loyalty. Meanwhile your sister Market Place undercuts you on a third of items.

Our recommendation: make the delivery price boring. A predictable small markup beats an occasional big one. Boring is what keeps the basket in your app instead of in the comparison tab.

P.S. — and yes, we already pulled the list: 58 items your sister Market Place sells cheaper than you — including French Cellars wine (your HK$129 vs her HK$38) and Walkers shortbread ($55 vs $33). Same company, two prices; the shopper notices before you do.

— Grocery Intel, tired of checking
An open letter to
Don Don Donki

You're the one shop our data recommends without an asterisk: cheap (~4% under market), steady (CV 0.11), and 0% delivery gouge, because foodpanda is your only shelf and there's nothing to mark up against.

Our recommendation: say it out loud. “No fake sales, no delivery markup” is a line your rivals literally cannot copy without changing who they are. Own the honesty — it's the cheapest marketing you'll ever buy.

P.S. — and yes, we found something even you might miss from the inside: a third of your items swing ~20% across your 17 branches (Ichiran ramen runs $34.90 to $54 depending which Donki). Every other big chain holds one price across all its stores — only your tills disagree with each other.

— Grocery Intel, the one letter we enjoyed writing
An open letter to
foodpanda

Your commission is invisible to the shopper — until a merchant like city'super passes it through flat and it surfaces as a +20% surprise on every item. We can read your take-rate straight off their shelf. Worse for you: that surprise teaches shoppers to walk, killing the orders you both wanted.

Our recommendation: you already know your take-rate — make merchants price rationally on top of it. Surface or cap the delivery markup, or hand merchants the comparison data so they stop stacking their own margin on your commission. A “+20%” that reads as the shop is greedy is a reputation cost you're absorbing for free.

P.S. — and yes, we can rank every merchant by how hard they mark up your channel, from public prices alone — your conversion risk, sorted, no integration required.

— Grocery Intel, reading your take-rate off the shelf

None of this needed a consulting retainer or a focus group. It needed two million public prices and the right few formulas. Every P.S. above is a real analysis we can run on the same data — that's the whole pitch: the work that used to live behind a six-figure engagement now fits in a browser tab, which is also, conveniently, how this page works.

Follow the ownership

Half these “rival” shops are the same company

Wellcome, Mannings and Market Place by Jasons are all DFI Retail Group. PARKnSHOP and Watsons are both AS Watson. They aren't competing for you. They're one company wearing several masks, each priced for a different wallet so the masks don't eat each other's lunch.

DFI Retail Group
Wellcome · Mannings · Market Place by Jasons (· IKEA · 7-Eleven)
AS Watson
PARKnSHOP · Watsons (· Fortress · Watsons Water)

This isn't an accident, it's the textbook. As Hermann Simon puts it, a cheaper line is “often marketed under a second brand, in order to differentiate it clearly from the primary brand and reduce the risk of cannibalization.” Each group runs one mass-grocery banner and one health-and-beauty banner at a totally different price personality. So when our pipeline “matched the same product across two shops,” it was almost always two banners of the same parent. That's not competition you can shop against — and it's exactly why a true cross-company price map is so hard to build (next).

Why it's frozen

The honest part: where I stopped

Comparing the same product across different companies needs a shared barcodeBarcode / EAN / SKU: the 13-digit GS1 number on a product. It's the only bulletproof way to know two listings are literally the same item across different retailers.. Most chains don't hand that out. So I matched by normalised name instead (a family_idfamily_id: a fingerprint built from a cleaned-up product name, so ‘Yakult 5x100ml’ and ‘Yakult LT 500ml [random delivery]’ collapse into one comparable family even without a barcode.). That works, until you notice almost every cross-chain match is the same parent company wearing a different hat.

The match funnel

families that survive each honesty filter

All product families156,433
Appear at 2+ chains (by name)14,325
Genuinely cross-company (3+ rivals)131

Wellcome, Mannings and Market Place by Jasons are all DFI. PARKnSHOP and Watsons are all AS Watson. Strip the same-parent banners out and a 14,325-family mountain becomes a 131-family hill. To climb past it you need the loyalty-app price feeds (yuu, MoneyBack). That's a different, much bigger project. I chose to stop. Knowing where the diminishing returns start is the actual skill.

The local model earned its keep

messy merchant categories, mapped once, on my own laptop

Thousands of junk category strings (“$10 Flash Sale”, “3:15 PM Tea Break”) refused to map. Instead of paying an API per row, qwen2.5:7b on Ollama classified each distinct string exactly once into a durable map (632 of them), then never ran again. A second local model, bge-m3, matched Chinese names to their English twins where barcodes were missing. Two million rows, normalised privately, for the price of electricity.

Same-parent banners vs real rivals
Visual explainer slot — a plain-language illustration of this concept lands here.