Skip to content

jgalea/grocery-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

grocery-cli

Go License Single binary

Unofficial, agent-friendly CLI for online supermarkets, behind one interface. Pick a store, search the catalog, read prices, price a shopping list.


Overview

grocery is a single Go binary that talks to several online supermarkets through one set of commands. You choose a store with --store (or GROCERY_STORE), and every store is an adapter behind a common interface, so the commands never change. Every command supports --json (data to stdout, logs to stderr) and --toon (fewer tokens, for LLMs).

It's not a scraper for "any supermarket on earth" — it's an explicit registry of stores that each have a verified adapter, spanning several backend types (guest APIs, server-rendered catalogs, Algolia search, WooCommerce, and a few bespoke REST/HTML sources). Run grocery stores to see what's supported right now.

Why use it

For a one-off shop, the store's own app is easier. grocery earns its place when you want to do things an app can't:

  • Compare prices across stores. One query, same basket, across every chain in your area — "who's cheapest for my weekly list, Mercadona or Consum or DIA?" No single app shows you that; each only knows its own prices.
  • Give an agent a clean primitive. The --json / --toon output is there so an LLM can drive the shop: hand it a shopping list (or a photo of your fridge), have it price the items across your stores and, for stores with an account, fill your cart (see Shopping cart).
  • Track prices over time. Run it on a schedule, log what your regular items cost, and watch how prices move.

Reads need no account and work for every store. Several stores also support filling your own cart (Mercadona, Bonpreu, Continente, Pingo Doce, Auchan, Scotts, PAVI/PAMA, Tesco, Sainsbury's); the CLI never places the order. Matching "the same product" across chains is fuzzy — batch and compare score search hits against each term (token overlap, size proximity, penalties for obvious category mismatches) and pick the cheapest passing hit; terms with no plausible match come back as not-found. Use batch --candidates N --json when an agent wants the scored shortlist to choose from.

Supported stores

Run grocery stores for the live list. Currently 29 stores across 5 countries:

Key Store Country Backend Supports
mercadona Mercadona ES Algolia + REST search, batch, total, product, categories, cart
bonpreu Bonpreu i Esclat ES uTLS search, batch, total, product, categories, cart
ametller Ametller Origen ES SCAPI search, batch, total, product, categories
gadis Gadis ES REST search, batch, total, product, categories
aldi ALDI España ES Algolia search, batch, total, product, categories
makro Makro España ES REST search, batch, total, product, categories
consum Consum ES REST search, batch, total, product
dia DIA ES REST search, batch
eroski Eroski ES HTML search, batch
alcampo Alcampo ES SSR search, batch
masymas masymas ES SSR search, batch
lidl-es Lidl España ES REST search, batch
continente Continente PT SSR search, batch, cart
pingodoce Pingo Doce PT SSR search, batch, cart
auchan Auchan PT SSR search, batch, cart
lidl-pt Lidl Portugal PT REST search, batch
morrisons Morrisons UK REST search, batch
iceland Iceland UK Algolia search, batch, total, product
tesco Tesco UK cookie + GraphQL search, batch, product, categories, cart (login)
sainsburys Sainsbury's UK cookie + REST search, batch, product, cart (login)
edeka24 Edeka24 DE HTML search, batch
scotts Scotts MT WooCommerce search, batch, total, product, categories, cart
pavipama PAVI/PAMA MT REST search, batch, categories, cart
welbees Welbee's MT HTML search, batch
spar SPAR MT HTML search, batch
savemart Savemart MT HTML search, batch
convenienceshop The Convenience Shop MT REST search, batch
greens Greens MT REST categories
smart Smart Supermarket MT HTML categories

Two of the Spanish stores need reading differently from the rest:

aldi prices the catalog ALDI publishes online, which it does not sell online, so there is no cart. Prices differ by region; GROCERY_ALDI_REGION picks peninsula, Canarias or Baleares.

makro is cash-and-carry. It quotes its trade customers net prices, and it sells wholesale packs, so the adapter reports the gross (VAT-inclusive) price and works the per-unit price off the whole pack: "leche entera 1 L contiene 6 unidades" is priced as the six litres it is. Its descriptions are long and brand-first, so search finds more than compare and batch do.

Not included: some chains sit behind bot-management (Cloudflare/Akamai/DataDome) that a plain HTTP client can't clear — Carrefour, El Corte Inglés (ES/PT), Condis, Intermarché, ASDA, Waitrose, Ocado, REWE, Kaufland, Netto. Their catalogs would need a headed stealth browser, which is out of scope for this CLI. Lidl in some markets has no shoppable priced online catalog (weekly-flyer only).

Install

go install github.com/jgalea/grocery-cli/cmd/grocery@latest

Or from source:

git clone https://github.com/jgalea/grocery-cli.git
cd grocery-cli
go build -o grocery ./cmd/grocery
./grocery version

Quick start

# List stores and what each supports
grocery stores

# Ametller Origen (ES) — the default store
grocery search llet --cheapest --limit 5
grocery product 1251

# Continente (PT) — pick it with --store
grocery --store continente search leite --limit 5
printf 'leite\npão\novos\n' | grocery --store continente batch -f -

# Set a default store for the session
export GROCERY_STORE=continente
grocery search arroz --limit 3

# Compare a shopping list across stores and rank by basket total
printf 'leche\naceite\nhuevos\ncafe\npan\n' | grocery compare -f - --stores mercadona,consum,dia,alcampo
printf 'leite\novos\ncafe\n' | grocery compare -f - --country PT --detail

Commands

Command Description
grocery stores List supported stores, their country, backend and capabilities
grocery search <term…> Full-text search. --limit N, --eco (where supported), --cheapest (rank by €/kg·L·u)
grocery batch [-f file] Best matching hit per term (one per line, # comments ok; or positional). --candidates N with --json/--toon includes scored alternatives
grocery compare [-f file] Price one shopping list across several stores and rank them (--stores a,b,c or --country ES, --detail)
grocery total [-f file] Deterministic basket total from <id> [qty] lines, summed in integer cents
grocery product <id> Product detail (price, brand, origin, ingredients, nutrition)
grocery categories [--id N] Category tree, or one category's products with --id
grocery usuals Regularly-bought items from local purchase history (--store, --min N, default 2)
grocery usuals order --store <key> Refill the cart with usual items (--dry-run, --min N)

Common flags (before or after the command): --store <key>, --lang <code>, --json, --toon.

Not every store supports every command yet — an SSR store without a product-detail path returns a clear "not supported for this store yet", and grocery stores lists each store's capabilities.

Shopping cart

grocery can fill your own cart on stores that support it (grocery stores lists cart). It adds items and stops there — it never places the order; you review and pay in the browser.

Cart stores today, by login style:

  • Continente, Pingo Doce, Auchan (PT) and Scotts (MT) — guest cart, no login needed (verified live); paste a cookie only if you want items in your own account cart. Pingo Doce needs a home-delivery postal code (defaults to Lisbon 1000-001; override with GROCERY_PINGODOCE_POSTAL).
  • Mercadona (ES) — email + password: grocery --store mercadona login (password read hidden, never stored; only the token is cached).
  • Bonpreu (ES) — paste your browser Cookie header (SSO/cookie login): grocery --store bonpreu login, then paste it from DevTools → Network → any request → Cookie.
  • PAVI/PAMA (MT) — paste your login token from a logged-in session: grocery --store pavipama login.
  • Tesco, Sainsbury's (UK) — behind Akamai, so they need your logged-in browser Cookie even to search: grocery --store tesco login first, then search and cart work.
grocery --store mercadona cart add 4240 2    # add 2× a product by id
grocery --store mercadona cart               # show the cart
grocery --store mercadona cart set 4240 0    # remove it
grocery --store mercadona cart clear         # empty the cart

cart add/set take a --max <eur> cap that refuses an over-budget line before writing.

Each successful cart add appends a line to ~/.grocery/history.jsonl (override with GROCERY_CONFIG_DIR). Use grocery usuals to see what you buy often, and grocery usuals order --store mercadona to refill the cart in one step.

The point of this is the agent flow: with the bundled grocery-shop Claude skill, you can say "add milk, eggs and coffee to my Mercadona basket" (or share a photo of a list) and the agent resolves each item, shows a priced plan, and fills the cart after you approve. Install it by copying .claude/skills/grocery-shop into your Claude skills directory. Only stores that list cart in grocery stores support this.

MCP server

grocery mcp runs a stdio Model Context Protocol server in the same binary. Any MCP client can search catalogs, price a list across stores, and fill carts — no shell access needed. Cart tools fill the basket but never place an order; you review and pay in the browser.

Tools exposed: stores, search, batch, compare, product, categories, cart_get, cart_add, cart_set, cart_clear. Results are compact JSON (same shapes as --json). Logs go to stderr; stdout is the protocol channel.

Claude Code:

claude mcp add grocery -- grocery mcp

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "grocery": {
      "command": "grocery",
      "args": ["mcp"]
    }
  }
}

Use the full path to grocery if it's not on your PATH. Cart stores still need a one-time grocery --store <key> login in a terminal before the MCP client can write to your cart.

Adding a store

Add an entry to internal/registry, pointing at either the SCAPI adapter (with the store's short code, org, guest client id and site id) or the SSR adapter (base URL, site id, locale). A store on a different platform (a custom API, a different commerce backend) gets a small new adapter implementing the store.Store interface.

Configuration

Env Purpose
GROCERY_STORE Default store key
GROCERY_CONFIG_DIR Override ~/.grocery (per-store session cache)
GROCERY_SCAPI_BASE Override the SCAPI host (debugging)
GROCERY_GADIS_STORE Gadis assortment store id (defaults to the site's own default)
GROCERY_ALDI_REGION ALDI price region: pen (default), can, bal
GROCERY_MAKRO_STORE Makro depot id (defaults to 00001)

License

MIT. See LICENSE.

Author: Jean Galea (@jgalea). Repository: github.com/jgalea/grocery-cli.

Inspired by bonpreu-cli and mercadona-cli.

About

Unofficial, agent-friendly CLI for online supermarkets behind one interface. Pick a store (Ametller, Continente, ...) and search, price a basket, read products.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages