Grocer.nz Public Price Data

SkillDatabases & data

Query grocer.nz public NZ supermarket data — canonical product and barcode/GTIN lookup, cross-retailer product matching, store lookup, current per-store prices, and historical price rows from public DuckDB/parquet assets. Use for exact NZ grocery product matching or pricing across Woolworths, New World, PAK'nSAVE, Fresh Choice, and related stores. Read-only; no login or private user data.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Grocer.nz Public Price Data skill

What this skill tells your AI

The instructions your AI receives, as published by thecolab-ai/.skills in skills/grocer-nz/SKILL.md and read by ahel’s review.

Overview

This skill queries public data exposed by grocer.nz, a New Zealand grocery price comparison app. It is useful as a canonical product/GTIN crosswalk as well as for current supermarket price checks, per-store comparisons, and historical product price rows.

The CLI uses:

  • Grocer public static assets: https://assets-prod.grocer.nz/public/
  • Public DuckDB base database: base_v3.duckdb.br
  • Per-store current price parquet files: prices_per_store_v3/public_prices_<store_id>.parquet
  • Per-product history parquet files: price_history_v3/price_history_<product_id>.parquet
  • Grocer frontend Meilisearch product index for search.

Script path:

skills/grocer-nz/scripts/cli.py

The script bootstraps duckdb via uv when the active Python does not already have it.

When to Use

  • Adam asks for historic supermarket pricing in NZ.
  • Adam wants to compare prices across Woolworths, New World, PAK'nSAVE, Fresh Choice, Super Value, The Warehouse, etc.
  • Adam wants a store id for a known store, e.g. Papakura.
  • Adam wants price history for a known Grocer product id.
  • Adam wants current prices for one product across selected stores.
  • Adam wants the public DuckDB/parquet download URLs so another agent can download or analyse the raw files itself.
  • Adam wants to identify the same exact product in the Woolworths, New World, or PAK'nSAVE skills.
  • Adam has a UPC, EAN, or GTIN and needs the corresponding Grocer product id.

Do not use this for authenticated Grocer Pro features, private user lists, or anything requiring sign-in. This skill is read-only public data only.

Commands

Set a helper variable:

GROCER=skills/grocer-nz/scripts/cli.py

Get raw asset download URLs

Use assets when the caller wants the source files rather than parsed rows. This command only constructs links from Grocer's fixed public asset layout. It makes no network request, starts no DuckDB process, and does not download or parse any file.

# Base catalogue DuckDB URL (also the default with no selectors)
python3 $GROCER assets --base --json

# Current-price parquet URLs for selected stores
python3 $GROCER assets --store-id 230 --store-id 307 --json

# Per-product history parquet URLs
python3 $GROCER assets --product 5452 --product 5461 --json

# Combine all three asset types
python3 $GROCER assets --base --store-id 230 --product 5452 --json

The result contains delivery: external_url and a resources array. Each resource has a declared format, filename, and public url. Through The Colab MCP, supported resources are also emitted as native MCP resource_link content, so the client downloads directly from Grocer rather than through the MCP server. The URLs are deliberately not probed; a nonexistent store or product id can therefore produce a link that returns 403/404 when downloaded. At most 50 links may be requested in one call.

List stores

python3 $GROCER stores --query Papakura
python3 $GROCER stores --vendor "PAK" --limit 20 --json

Useful known Papakura ids from live smoke:

  • 118 — Woolworths Papakura
  • 206 — Fresh Choice Papakura
  • 230 — PAK'nSAVE Papakura
  • 307 — New World Papakura

Search products

Search only:

python3 $GROCER search "milk" --limit 5

Search plus current prices in matching stores:

python3 $GROCER search "milk" --store-query Papakura --limit 3
python3 $GROCER search "whittakers" --store-id 230 --store-id 307 --json

The search command returns Grocer product ids, canonical GTIN-14 barcodes, and compact retailer_search_terms. Use the product ids with prices and history; use the retailer search terms with the retailer-native skills.

Resolve a barcode

Grocer stores barcodes in zero-padded GTIN-14 form. The command accepts compact UPC/EAN forms too:

python3 $GROCER barcode 94152210
python3 $GROCER barcode 00000094152210 --json

For Anchor Blue Milk 2L, this resolves to Grocer product 5452, canonical GTIN 00000094152210, and retailer search term 94152210.

Match the exact product across retailers

  1. Search Grocer by product description and select the right brand/name/size candidate.
  2. Copy a returned retailer_search_terms value.
  3. Search each retailer skill with that value:
python3 skills/woolworths-nz/scripts/cli.py search 94152210 --json
python3 skills/paknsave-nz/scripts/cli.py search 94152210 --json
python3 skills/newworld-nz/scripts/cli.py search 94152210 --json

Woolworths search can include loosely related results, so keep only rows whose barcode, normalised to GTIN-14, equals Grocer's canonical barcode. Foodstuffs barcode search returns the matching New World/PAK'nSAVE product id; both banners use the same Foodstuffs product id for the same product. If barcode search returns no exact result, do not silently substitute a similar size or variant—fall back to brand/name/size matching and label it as a probable match.

Current prices for a product

python3 $GROCER prices 5461 --store-query Papakura
python3 $GROCER prices 5461 --store-id 230 --store-id 307 --json

--all-stores is supported but can be slow because it downloads many per-store parquet files:

python3 $GROCER prices 5461 --all-stores --limit 30

Price history for a product

python3 $GROCER history 5461 --store-query Papakura --limit 20
python3 $GROCER history 5461 --store-id 230 --json

History files are per product and include store-level rows when available.

Raw product metadata

python3 $GROCER product 5461 --json

Ad-hoc SQL query (guarded, read-only)

Run a single SELECT/WITH statement over the dataset for analysis the fixed commands don't cover (cheapest-across-stores, vendor trends, joins, aggregates).

Always-available relations: products, barcodes, stores, vendors. Load extra data with flags: --store-id/--store-query/--all-stores populate a prices table; --product populates a history table (with a product_id column added).

# cheapest blue-top milk (product 5452) across Papakura stores
python3 $GROCER query "select s.name, p.original_price_cent from prices p join stores s on s.id=p.store_id where p.product_id=5452 order by 2" --store-query Papakura --json

# count products by vendor that stock a given product's price rows
python3 $GROCER query "with p as (select id,name,brand from products where lower(brand)='anchor') select * from p" --limit 20

# resolve compact retailer barcodes against Grocer's canonical catalogue
python3 $GROCER query "select b.barcode, p.* from barcodes b join products p on p.id=b.product_id where b.barcode='00000094152210'" --json

# price history rows for a product
python3 $GROCER query "select store_id, price_cent, updated_at from history order by updated_at desc" --product 5461 --limit 30 --json

Safety: the statement must be a single read-only SELECT/WITH (no ;, no DDL/DML). Filesystem and network access are disabled (enable_external_access= false, lock_configuration=true) after the requested parquet are loaded, and the base catalogue is attached READ_ONLY. An arbitrary query can therefore only read the public grocery data exposed here — it cannot write, read other files, reach the network, or change settings. Results are capped (--limit, max 5000).

Data Notes

See references/api-notes.md for the sniffed endpoints and file layout.

Important model fields:

  • Products: id, name, brand, unit, size, redirected_to
  • Barcodes: zero-padded GTIN-14 barcode, product_id
  • Stores: id, vendor_id, name, is_enabled
  • Current prices: updated_at, store_id, product_id, original_price_cent, sale_price_cent, club_price_cent, online_price_cent, multibuy fields
  • Price history: updated_at, store_id, price_cent

Prices are stored in NZ cents. $5.50 is represented as 550.

Common Pitfalls

  1. No store selected = limited price context. Product search comes from Meilisearch. Current per-store pricing requires store ids or --store-query so the CLI can download the relevant parquet files.
  2. --all-stores can be noisy/slow. It may download hundreds of current price parquet files. Prefer targeted store ids for fast checks.
  3. The .br DuckDB file may arrive already decompressed. The CLI treats Grocer's base_v3.duckdb.br CDN response as a DuckDB file directly because Cloudflare/Caddy often transparently decodes it.
  4. DuckDB DDL cannot use prepared parameters for read_parquet(?). Quote local parquet paths explicitly before create view ... as select * from read_parquet('<path>'); parameter binding works for normal select filters but not this statement shape.
  5. History parquet schema is leaner than the app's local table. Per-product history files may only include updated_at, store_id, and price_cent; get vendor_id by joining public_stores, and infer product id from the requested history file.
  6. Historical availability varies. Some products/stores have no history parquet or no rows for a selected store.
  7. Effective price is conservative. The CLI reports effective_price_cent as the minimum of original/sale/club/online when present. Multibuy needs human interpretation because quantity matters.
  8. Live smoke tests depend on upstream stock/history. Product 5461 and Papakura stores were live-valid when added, but upstream catalogue changes can make the smoke fail without a code bug.
  9. This is public read-only data. Do not try to bypass sign-in or scrape private Grocer user/list/pro features.
  10. Retailer barcode search has different shapes. Search Foodstuffs with the compact barcode (leading zero padding removed). Woolworths returns its own barcode; normalise and verify that field because its text search may include unrelated rows.
  11. assets constructs but does not validate links. Use it for low-overhead handoff of raw public files. Use the parsed commands when the agent needs verified rows immediately.

Verification Checklist

Run these before relying on a fresh install:

python3 $GROCER stores --query Papakura --json
python3 $GROCER assets --base --store-id 230 --product 5452 --json
python3 $GROCER search "milk" --store-query Papakura --limit 2
python3 $GROCER prices 5461 --store-query Papakura --limit 10
python3 $GROCER history 5461 --store-query Papakura --limit 5

Expected smoke signal: Papakura stores include Woolworths/New World/PAK'nSAVE, and product 5461 returns Anchor 2L milk pricing/history rows for at least PAK'nSAVE/New World/Woolworths Papakura when current Grocer data includes them.

Signals

GitHub stars
25
Forks
6
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
grocer-nz
Source
github.com/thecolab-ai/.skills