Grocery Flyer Scraper — Weekly Deals & Price Drops avatar

Grocery Flyer Scraper — Weekly Deals & Price Drops

Pricing

$80.00 / 1,000 per merchant flyer digests

Go to Apify Store
Grocery Flyer Scraper — Weekly Deals & Price Drops

Grocery Flyer Scraper — Weekly Deals & Price Drops

Scrape every grocery store flyer for a US ZIP or Canadian postal code from Flipp: item name, sale price, brand and validity dates across 110+ merchants. Tracks week-over-week price drops and outputs a markdown digest ready for an AI meal planner. No login or API key.

Pricing

$80.00 / 1,000 per merchant flyer digests

Rating

0.0

(0)

Developer

Scrapers Delight

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

5

Total users

0

Monthly active users

19 days ago

Last modified

Share

Get every grocery store flyer for a US ZIP or Canadian postal code in one run — item name, sale price, brand, and the exact dates the deal runs — across 110+ merchants from a single input.

Then it does the part nobody else does: tells you what changed since last week.

🔄 Since last run (2026-08-03)
26 new · 56 cheaper · 0 pricier · 0 ended
Price drops
| Item | Was | Now | Save | Merchant |
| Fresh Boneless, Skinless Chicken Breast | $8.19 | $6.69 | $1.50 | Starsky |
| Long Eggplant | $3.48 | $1.98 | $1.50 | Food Depot |

What you get

One row per flyer item:

fieldnotes
merchant, merchantIdthe store
name, brandproduct as printed on the flyer
price, priceRawsale price (null when the flyer prints no price — see below)
validFrom, validToexactly when the deal runs
isMultiProductBundle⚠︎ flag — see the honesty note
changeTypenew · price_drop · price_rise · unchangednull on run 1, and on any store your previous run didn't open
previousPrice, priceDeltaversus your last run
discountflyer's own "% off" badge — merchant-dependent, 16–52% of items (measured 133/808 on a 3-store run, 1,160/2,219 on the 15-store default, 2,606/6,549 on a 47-store sweep). Flipp carries it as null for the rest — it is not a scrape miss
imageUrlproduct cutout image
categories, flyerId, postalCode, localeprovenance

Measured field fill — three live runs of different width, M5V3L9. merchant, merchantId, itemId, flyerId, validFrom, validTo, flyerValidFrom, flyerValidTo, categories, imageUrl, isMultiProductBundle, postalCode, locale, scrapedAt are 100% on all three. The rest depend on which stores you open:

runrowsprice/priceRawbranddiscountname
3 stores (Sobeys · No Frills · Costco)80886.5%80.6%16.5%100%
15 stores (the default, no merchants set)2,21986.0%60.3%52.3%98.3%
47 stores (allCategories, whole postal code)6,54985.3%56.4%39.8%98.9%

price is stable at ~85–87% whatever you open. brand and discount are not — they swing entirely with the merchant mix, so quote the row for the run shape you actually use.

These are the source's own gaps, not scrape misses: Flipp's payload returns "price": "", "brand": null and "discount": null for exactly those items. Fetched raw and un-proxied straight from Flipp's backend (12 flyers / 889 items) the same fields read price 80.4% · brand 76.9% · discount 40.9%, and per flyer brand ranges from 0% (Healthy Planet — unbranded whole foods) to 100% (Ginkgo Health Shop). The un-branded rows are genuinely un-branded produce and house items ("Locally Grown Blue Plums", "Pineapple Peach Protein Smoothie").

Plus OUTPUT.md — a markdown digest in the key-value store: price drops sorted by biggest saving, what's new this week, then the full listing grouped by merchant. Paste it straight into ChatGPT/Claude and ask for a meal plan built around what's actually on sale.

Pick your own stores

Most people shop at four or five places. Run once in discovery mode to see what's available:

{ "postalCode": "M5V3L9", "listMerchantsOnly": true }

That returns every store delivering flyers to your postal code — 115 for downtown Toronto, 51 of them grocery — with name, merchant ID, categories and flyer dates. Pick yours, then:

{ "postalCode": "M5V3L9", "merchants": ["sobeys", "Costco", "3384"] }

The merchants list accepts any mix of:

  • store names, case-insensitive and partial — "sobeys" matches "Sobeys"
  • merchant IDs from the discovery run — "3384"
  • pasted flipp.com links — any 4+ digit run in the URL is read as a merchant ID

Anything that matches nothing is called out by name in the log rather than silently ignored, and an explicit store list is never truncated by maxMerchants (that cap only guards unfiltered runs).

Picking stores also improves the data: a real-grocer list measured 91.3% price fill against 78.9% for the broad category filter.

Flipp's website is a single-page app — a bogus flyer id and a real one return byte-identical HTML — so a pasted link can't be validated. And flyer IDs are reissued every week when new flyers publish, so a saved flyer link would break every 7 days. Merchant IDs are stable (verified: no merchant carries more than one), so a saved store list keeps working. Links are still accepted, but they're read as a merchant hint, not a flyer address.

Weekly monitoring

Set an Apify Schedule and each run diffs against the last one for that postal code. Flyers run about seven days (validFromvalidTo), so weekly is the natural cadence — point it at the morning your local flyers drop. Daily works too and costs little; most days will simply report few changes.

  • Run 1 writes the baseline and reports no changes.
  • Run 2+ label every row and add the "Since last run" section.
  • Snapshots are per postal code + locale, so you can track several areas independently.
  • onlyChanges: true narrows the dataset to just new + changed rows, ideal for a recurring schedule.
  • Changing your store list mid-schedule is safe. Labels and the new/cheaper/pricier/ended tallies are scoped to the stores both runs opened — add a store and it starts unlabelled (changeType: null) rather than reporting a wall of fake "new" items, and a store you drop is never reported as "ended". Its baseline is kept, so it resumes diffing when you add it back.
  • A run that stops early (time limit or budget cap) merges into the baseline instead of replacing it, so a truncated run never costs you next week's comparison.

Input

inputdefaultnotes
postalCodeM5V3L9M5V3L9 (Canada) or 10001 (US). Partial codes are rejected. Leave it empty and the Actor runs that sample code, so an API or agent call with {} still returns real rows.
merchantsYour stores: names, merchant IDs or pasted links. Empty = use the category filter.
listMerchantsOnlyfalseDiscovery run — return the available stores and scrape nothing.
localeen-caen-ca or en-us. US and Canada only.
maxMerchants15Uncapped, a dense metro emits ~18,000 items.
categoriesGroceries, Pharmacy, RestaurantsFilters flyers before fetching them.
allCategoriesfalseTake every flyer (mostly hardware/sporting goods).
trackChangestrueDiff against your previous run.
onlyChangesfalseEmit only what changed (ignored on run 1).
includeMarkdowntrueWrite OUTPUT.md.

Pricing

$0.08 per merchant flyer digest — one charge per merchant, not per item. A default 15-merchant run costs about $1.20; all 139 merchants in a dense metro about $11.

Per-item pricing would be dishonest here: a single postal code yields ~18,000 items, so any sane per-row rate bills $45–113 for one run. The merchant-week is also the unit you actually care about. Merchants are charged before their rows are pushed, so if you hit a budget cap you get complete merchant digests rather than a fragment of every one.

Honest limits

  • ~85-87% of items carry a price (86.5% / 86.0% / 85.3% across the 3-, 15- and 47-store runs measured above). Some flyer tiles are image-only or promotional; those come back with price: null rather than a guess. brand and discount are far more variable — see the fill table; a store list of unbranded grocers can read brand as low as 0%.
  • ~36% of item names bundle multiple products under one price — "RED BULL or GURU ENERGY DRINK, $5.50". Those are flagged isMultiProductBundle and marked ⚠︎ in the digest. Treat them as an offer, not a per-SKU price. If you are doing per-SKU price intelligence, filter them out.
  • US and Canada only. Other locales return no data.
  • Change tracking matches on merchant + normalised name, because item IDs are re-issued with each new flyer. About 1.4% of rows share a merchant+name pair and collapse into one tracked item.
  • An invalid-but-well-formed code (e.g. 00000) makes the upstream API return an empty list rather than an error. The Actor says so in the run status message and stops there — it never reports a silent successful empty run, and it never marks the run failed for what is an input problem.
  • A very large sweep (allCategories: true, maxMerchants: 139) can outlast a short run timeout. The Actor watches the clock, stops opening new flyers before the limit, keeps everything it has already delivered, and says how far it got in the status message. Measured: 139-flyer sweep under a 120-second timeout finishes SUCCEEDED at ~102 s with 11.7k-14.9k rows. Give it the default 1-hour timeout for a full metro.

Data comes from Flipp's public flyer service. You are responsible for complying with Flipp's Terms of Service and for how you use the data.