Shrinkflation Detector for Grocery Price Data avatar

Shrinkflation Detector for Grocery Price Data

Pricing

from $2.00 / 1,000 results

Go to Apify Store
Shrinkflation Detector for Grocery Price Data

Shrinkflation Detector for Grocery Price Data

Find shrinkflation in grocery and retail product data: smaller packs at the same or higher price. Compare two runs of a supermarket scraper or your own snapshots and get size before and after, price, price per 100 g / ml / item and the hidden price rise per product. No AI.

Pricing

from $2.00 / 1,000 results

Rating

0.0

(0)

Developer

Ultimate Foresight Inc

Ultimate Foresight Inc

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

19 hours ago

Last modified

Share

Shrinkflation Detector finds products that got smaller at the same (or higher) price in grocery and retail product data. Give it two runs of a supermarket scraper, or your own dated product rows, and it returns the size before and after, price, price per 100 g / 100 ml / item, and the hidden price increase for every pack-size change. Click Start with the prefilled example to see it work in a few seconds.

Results are calculated from the data you supply. Pack sizes are parsed from text and can be wrong, and a smaller pack doesn't prove intent, so check the product before publishing a claim.

What does Shrinkflation Detector do?

  • ✅ Matches the same product across snapshots by product ID (id, sku, product_id, stockcode, EAN/GTIN, URL), and by name when the barcode changed with the new size
  • ✅ Parses pack sizes from a size field or the product name: 200g, 1.5 L, 75cl, 18 oz, 1 lb 4 oz, 12 x 12 fl oz, 6x1,5 L, 4 pk 250 ml each, 42 ct, 12 Mega Rolls, 100 tablets, pack of 6
  • ✅ Converts everything to grams, millilitres or items, so 18 oz and 510 g count as the same size
  • ✅ Ignores numbers that aren't pack sizes: 20g protein, 160mg caffeine, 1000mg strength, 128GB, 5G, Size 10, Ages 3+, 2 for $5
  • ✅ Uses the regular price when the data also has a promo price (regularPrice, original_price, priceNormal, wasPrice ...)
  • ✅ Labels each change: shrinkflation, shrinkflation_partial_price_cut (price dropped less than the size), size_decrease_with_matching_price_cut, size_increase, unit_price_increase, price_increase, price_decrease
  • ✅ Returns sizeBefore, sizeAfter, priceBefore, priceAfter, unitPriceBefore, unitPriceAfter, sizeChangePct, priceChangePct, unitPriceChangePct, hiddenPriceIncreasePct, severity, matchedBy, notes and a timestamp per product
  • ✅ Rule-based and deterministic: no AI, no scraping, no external APIs

Who is it for?

  • Journalists and consumer groups tracking shrinkflation at supermarkets
  • Price-intelligence and retail analysts who already scrape grocery prices and want size-adjusted inflation
  • CPG brands and category managers watching competitors' pack-size moves
  • Researchers measuring hidden price increases across thousands of products

How to detect shrinkflation

  1. Click Start with the prefilled example (made-up products) to see the output.
  2. Replace Product snapshots with your rows: id, name, size (optional if the name has it), price, date. Or pick an Older dataset and a Newer dataset from two runs of the same grocery scraper (Coles, Woolworths, ALDI, Lidl, Tesco, Walmart and similar).
  3. Choose What to report: shrinkflation only, all size changes (default), size and price changes, or everything.
  4. Run it, then download the table as CSV, Excel or JSON, or schedule it after each scraper run.

Input example

The prefill (example data, not real products):

{
"products": [
{
"id": "example-1",
"name": "Example Chocolate Bar 200g",
"price": 3.0,
"date": "2026-01-15"
},
{
"id": "example-1",
"name": "Example Chocolate Bar 180g",
"price": 3.0,
"date": "2026-04-15"
},
{
"id": "example-2",
"name": "Example Cola",
"size": "12 x 12 fl oz",
"price": 7.99,
"date": "2026-01-15"
},
{
"id": "example-2",
"name": "Example Cola",
"size": "10 x 12 fl oz",
"price": 7.49,
"date": "2026-04-15"
},
{
"id": "example-3",
"name": "Example Butter 250g",
"price": 3.5,
"date": "2026-01-15"
},
{
"id": "example-3",
"name": "Example Butter 250 g",
"price": 3.5,
"date": "2026-04-15"
}
]
}

Comparing two scraper runs:

{
"beforeDatasetId": "YOUR_OLDER_DATASET_ID",
"afterDatasetId": "YOUR_NEWER_DATASET_ID",
"report": "shrinkflation"
}

If your data uses unusual field names, set Product ID field, Size field, Price field or Date field. For a size field that's a bare number (e.g. 500), set Unit for numeric sizes (g, ml, oz), or include a unit field such as unitMeasure.

Output example

Real output from running the prefill on 27 October 2026 (the before and after detail objects are trimmed here). Butter didn't change, so with the default setting it isn't saved:

[
{
"productKey": "example-1",
"name": "Example Chocolate Bar 180g",
"eventType": "shrinkflation",
"severity": "high",
"sizeBefore": "200 g",
"sizeAfter": "180 g",
"priceBefore": 3,
"priceAfter": 3,
"unitPriceBefore": 1.5,
"unitPriceAfter": 1.6667,
"unitPriceBasis": "per 100 g",
"sizeChangePct": -10,
"priceChangePct": 0,
"unitPriceChangePct": 11.11,
"hiddenPriceIncreasePct": 11.11,
"snapshotsSeen": 2,
"matchedBy": "id",
"notes": [],
"checkedAt": "2026-10-27T18:20:06+00:00",
"disclaimer": "Detected from the data you supplied: pack sizes and prices are parsed from text and may be wrong. A size change on its own does not prove intent. Check the product before publishing a claim."
},
{
"productKey": "example-2",
"name": "Example Cola",
"eventType": "shrinkflation_partial_price_cut",
"severity": "medium",
"sizeBefore": "12 x 12 fl oz (4258.6 ml)",
"sizeAfter": "10 x 12 fl oz (3548.8 ml)",
"priceBefore": 7.99,
"priceAfter": 7.49,
"unitPriceBefore": 0.1876,
"unitPriceAfter": 0.2111,
"unitPriceBasis": "per 100 ml",
"sizeChangePct": -16.67,
"priceChangePct": -6.26,
"unitPriceChangePct": 12.49,
"hiddenPriceIncreasePct": 12.49,
"snapshotsSeen": 2,
"matchedBy": "id",
"notes": [
"Pack count changed from 12 to 10."
],
"checkedAt": "2026-10-27T18:20:06+00:00",
"disclaimer": "Detected from the data you supplied: pack sizes and prices are parsed from text and may be wrong. A size change on its own does not prove intent. Check the product before publishing a claim."
}
]

hiddenPriceIncreasePct is how much more you now pay per 100 g, 100 ml or item.

How it decides

  • Size change = (new size − old size) / old size, after converting to g, ml or items. Changes within 1% are treated as rounding (e.g. 32 oz vs 2 lb).
  • shrinkflation: size down by more than 1% and price down by less than 0.5% (same or higher).
  • shrinkflation_partial_price_cut: size down, price down too, but price per unit still up by at least the Minimum unit-price rise (default 2%).
  • size_decrease_with_matching_price_cut: price fell as much as the size, so the unit price didn't rise.
  • Severity: high = shrinkflation with a unit-price rise of 5% or more; medium = smaller shrinkflation, a partial price cut, or a bigger pack with a higher unit price; low = other size changes and price increases; info = price decreases and size cuts matched by a price cut.
  • Sizes in different measures (e.g. 80 wipes vs 500 g) aren't compared; the row says so in notes.
  • With 3+ snapshots, the latest size change is reported (or every step with Report every change step).

How much does it cost?

Pay per result: $0.002 per product row saved, plus a $0.00005 start fee and small platform usage paid by you (the prefill run used about $0.0001). Only changed products are saved by default, so unchanged products cost nothing. Examples: 100 flagged products ≈ $0.20; 1,000 ≈ $2.00, plus platform usage. You can cap spending with the maximum cost per run setting.

Use it through the API, integrations or AI agents

  • API (run and get results in one call): https://api.apify.com/v2/acts/ultimatemc~shrinkflation-detector/run-sync-get-dataset-items
  • MCP for AI agents: https://mcp.apify.com?tools=ultimatemc/shrinkflation-detector
  • Chain it after a grocery scraper with an Apify webhook or schedule: pass the previous and the latest dataset IDs, and send the results to Google Sheets, Slack or Make with Apify integrations.

FAQ

Does it scrape supermarkets?

No. It only compares data you give it: your own rows or datasets from a scraper you ran.

Is it AI?

No. Sizes and prices are parsed with fixed rules, so the same input always gives the same output.

What if the barcode changed with the new size?

With Match by name when the ID changes on (default), a product seen only in the older data is joined to one seen only in the newer data when their names match once the size is removed, at the same retailer. matchedBy then says name (id changed).

Does a flag prove shrinkflation?

No. It shows the size and price in your data changed. Check the product page or pack before publishing.

Which currencies work?

Any. Prices like $3.99, 3,99 €, £1.50 and 99c are read; rows with different currencies aren't compared.

Limitations

  • Pack sizes are read from text; unusual formats may be missed (the row then has no size and isn't flagged).
  • Weight oz and fl oz are different measures; a product listed as 16 oz then 16 fl oz isn't compared.
  • It doesn't detect reformulation (cheaper ingredients) or fewer sheets per roll unless the size text says so.
  • Promo prices are only separated when the data has a regular-price field.
  • Name matching needs the same retailer and a near-identical name; renamed products won't be joined.

Other tools by Ultimate Foresight

Missed a size change or found a false alarm? Open an issue on the Issues tab with the two rows, and we'll review the rule.