Skip to content

Products

GET /v1/products lists the entire indexed grocery catalogue in stable id order. Where search answers “match this query”, the list endpoint answers “give me everything” - full-catalogue exports, nightly sync jobs, or category sweeps where relevance and facets do not matter. Requires an API key and each request costs 50 credits.

Every item comes back in the exact search-hit shape, so a listed product, a hydrated product, and a search hit parse identically.

Terminal window
curl -H "X-API-Key: $MEALCP_API_KEY" \
"https://api.mealcp.com/v1/products?country=AT&limit=100"
Param Type Default Description
country string - ISO 3166-1 alpha-2, e.g. AT
retailer string - Retailer slug (e.g. billa-at)
category string - Category slug at any level; matches the whole subtree
limit int 100 Items per page, max 100
cursor string - Continuation token from a prior response’s next_cursor

All filters combine with AND. Omit cursor to start from the beginning of the catalogue.

Valid retailer tokens appear in the retailer_slug facet counts of any search response; valid category tokens come from GET /v1/categories.

First page for Austria (trimmed to one hit):

{
"found": 40213,
"items": [
{
"id": "rp_01a0144a41fc7fcba462fffa45cd23a1",
"name": "Lindt Excellence Fleur de Sel",
"brand": "Lindt",
"country_code": "AT",
"retailer_slug": "billa-at",
"chain_slug": "billa",
"quantity_value": 100.0,
"quantity_unit": "g",
"latest_price": 3.79,
"latest_unit_price": 37.9,
"latest_unit_price_uom": "kg",
"currency": "EUR",
"latest_observed_at": "2026-08-18T09:57:59Z",
"tags": [],
"store_cities": []
}
],
"next_cursor": "MzQ1Ng"
}

Items carry the full hit shape, including category_slug, category_path, url, and image_url - see the hit fields table.

Field Type Description
found number Total catalogue size for the applied filters
items[] array Products in the search-hit shape
next_cursor string Token for the next page; null when the walk is done

Two behaviors worth knowing:

  • found is a memoised snapshot total (short cache TTL), not a per-request count - treat it as an estimate for planning, and let next_cursor: null decide when to stop.
  • Enumeration is gap-free. Items are ordered by a stable internal key, so walking next_cursor until null sees no duplicates or gaps even as nightly ingest inserts new rows mid-walk.
Terminal window
curl -H "X-API-Key: $MEALCP_API_KEY" \
"https://api.mealcp.com/v1/products?country=AT&limit=100&cursor=MzQ1Ng"

Keep requesting with the previous next_cursor until it comes back null. Each request costs a flat 50 credits regardless of limit, so always use limit=100 - the most rows per credit - and send the same filters on every call.

Need Use
Everything, in bulk, gap-free GET /v1/products (this page)
Keyword matching, facets, sorting GET /v1/search
One product by id GET /v1/products/{id}

An empty-q search can also browse the catalogue, but it paginates with page numbers - if rows shift between requests, a page-based walk can skip or repeat products. For durable full-catalogue syncs, prefer the cursor walk.

The list reads live catalogue storage, so it may run slightly ahead of the search index between reingests - a product can appear here before it matches in search. Check each item’s latest_observed_at when freshness matters.

Search’s error semantics apply, plus one endpoint-specific case:

  • 400 - malformed cursor token (passed the wire pattern but does not decode to a catalogue position). Start a fresh walk instead of retrying the same token.