Denmark Grocery Price Matrix
Pricing
from $1.20 / 1,000 result scrapeds
Denmark Grocery Price Matrix
One normalized feed of grocery products and prices across Foetex, Bilka, REMA 1000, SPAR, Netto and Lidl Denmark, matched to a common record shape and delivered as a single dataset.
Pricing
from $1.20 / 1,000 result scrapeds
Rating
0.0
(0)
Developer
Studio Amba
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
5 days ago
Last modified
Categories
Share
One normalized feed of grocery products across the major Danish supermarket chains: Foetex, Bilka, Bilka ToGo, REMA 1000, SPAR, MENY, Netto and Lidl. Give it search terms and it returns a single dataset with one common record shape per product, in DKK, with everyday and offer prices clearly labeled.
This actor does not scrape a website itself. It runs our eight Danish retailer scrapers in parallel and normalizes what comes back into one schema, so you get one feed and one API instead of stitching together eight different field layouts yourself.
Why use this actor?
Danish grocery chains publish price data in different shapes — SPAR labels a multi-buy discount differently than REMA 1000 labels a weekly campaign, and two chains (Netto, Lidl) don't publish an everyday catalogue at all, only rotating weekly offers. Pulling all eight by hand and reconciling the field names is the tedious part. This actor does the fan-out and the normalization in one call and returns a flat table you can load straight into a spreadsheet, warehouse, or pricing dashboard.
What it returns
Each row is one product at one retailer for one search term.
{"query": "mælk","retailer": "REMA 1000","name": "Arla Skummetmælk","brand": "Arla","price": 12.60,"originalPrice": null,"currency": "DKK","priceType": "everyday","offerValidFrom": null,"offerValidTo": null,"ean": "5700426101415","unitPrice": "12.6 DKK/l","unitOfMeasure": "l","packageSize": "1 LTR. / REMA 1000","category": "Mejeri > Mælk","inStock": null,"url": "https://shop.rema1000.dk/varer/123456","imageUrl": "https://...","productId": "123456","scrapedAt": "2026-09-09T15:33:58.000Z"}
An offer row (Netto, Lidl, or a temporary discount on any other chain) looks the same but with priceType: "offer" and, where the source publishes it, an offerValidFrom/offerValidTo window.
Retailer coverage
| Retailer | Catalogue type | Notes |
|---|---|---|
| Foetex | Everyday shelf catalogue | Algolia-backed storefront search |
| Bilka | Everyday shelf catalogue | JSON-LD product pages; brand/category/unit price are only present on some listings — see Limitations |
| Bilka ToGo | Everyday shelf catalogue | Live. Salling's fresh-grocery webshop (bilkatogo.dk) — full dairy/produce/meat range, unlike Foetex/Bilka's dry-goods-only index |
| REMA 1000 | Everyday shelf catalogue + campaigns | Public search API, full field coverage |
| SPAR | Everyday shelf catalogue + campaigns | Reads one physical store's Spartid webshop (sparStore input, default Kastrup) — not every SPAR store runs one |
| MENY | Everyday shelf catalogue + campaigns, per store | Reads one physical store's grocery webshop (menyStore input, default Parkvej) — only 9 of MENY's 116 stores run one, see docs/site-recon/meny-dk.md. MENY is SPAR's sibling Dagrofa banner; the two chains price independently per store, sampled at ~80% divergence between stores |
| Netto | Offers only | No shop or product catalogue exists on netto.dk at all — every row is a time-boxed weekly leaflet offer (Tjek platform). Always priceType: "offer". No EAN, no brand, no stock field in the source |
| Lidl | Offers only | Lidl DK's search API exposes only the current ~325-item weekly-offer rotation, not a static everyday catalogue. Always priceType: "offer" here |
Treat "everyday" rows as the shelf price you'd see walking into the store, and "offer" rows as a time-boxed campaign price — never assume an offer row is available outside its validity window.
How to scrape Danish grocery data
- Enter one or more Danish search terms in Search Queries, one per line (e.g.
mælk,kaffe,øl). - Choose which Retailers to include. Leave all eight selected for full coverage, or narrow it down.
- Set Max Items Per Retailer to control how many products each chain contributes per query.
- Optionally set SPAR Store or MENY Store to a different store subdomain, or use Category URLs to browse a Bilka or Lidl category page instead of searching.
- Run the actor. All selected retailers are queried in parallel per query, so adding retailers doesn't multiply run time.
- Open the dataset. Filter by
retailerto compare one chain, or bypriceTypeto separate everyday shelf prices from time-boxed offers.
Input
| Field | Type | Description |
|---|---|---|
retailers | array | Which of the eight chains to include (foetex, bilka, rema1000, spar, meny, netto, lidl, bilkatogo). Empty means all. |
searchQueries | array | Danish search terms to price-check. Defaults to ["mælk", "kaffe", "øl"]. |
categoryUrls | object | Optional per-retailer category page URL, e.g. {"bilka": "https://www.bilka.dk/mejeri"}. Only Bilka and Lidl support this; when set for a retailer, searchQueries is skipped for that retailer. |
sparStore | string | SPAR Spartid store subdomain to read (default kastrup). |
menyStore | string | MENY grocery-webshop store subdomain to read (default parkvej). |
maxItemsPerRetailer | integer | Max products considered per retailer per query (1-200, default 20). |
timeoutPerRetailerSecs | integer | Max seconds to wait for each retailer scraper before treating it as failed (30-600, default 150). |
proxyConfiguration | object | Proxy settings passed to the retailers that need one (Foetex, Bilka, REMA 1000, Lidl, Bilka ToGo). SPAR, MENY and Netto always run on the free pool — see FAQ. |
Example input
{"retailers": ["foetex", "bilka", "rema1000", "spar", "meny", "netto", "lidl", "bilkatogo"],"searchQueries": ["mælk", "kaffe", "øl"],"sparStore": "kastrup","menyStore": "parkvej","maxItemsPerRetailer": 20,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "DK" }}
Output
| Field | Type | Description |
|---|---|---|
query | string | The search term this row was fetched for, or (category browse) |
retailer | string | Foetex, Bilka, Bilka ToGo, REMA 1000, SPAR Denmark, MENY Denmark, Netto Denmark or Lidl |
name | string | Product name |
brand | string | Product brand, when the retailer exposes one — see Retailer coverage |
price | number | Effective current price in DKK — the campaign price when priceType is offer, the shelf price otherwise |
originalPrice | number | Regular/everyday price, populated only when price reflects a current discount |
currency | string | Always DKK |
priceType | string | everyday or offer |
offerValidFrom / offerValidTo | string | ISO timestamps the current offer price runs, when the source publishes them |
ean | string | EAN/GTIN barcode, when published. Never present on Netto |
unitPrice | string | Retailer-formatted per-unit price, e.g. 24,00 DKK/kg |
unitOfMeasure | string | Unit the unit price is expressed in, when parseable |
packageSize | string | Pack size / quantity, free text |
category | string | Product category |
inStock | boolean/null | true/false when the source states availability, null when it doesn't — never guessed |
url | string | Product (or leaflet) page URL |
imageUrl | string | Product image URL |
productId | string | Retailer-internal product or offer id |
scrapedAt | string | ISO timestamp this row was fetched |
FAQ
Does this actor scrape the retailer websites itself? No. It calls our own Foetex, Bilka, Bilka ToGo, REMA 1000, SPAR, MENY, Netto and Lidl scrapers and normalizes their output into one schema. Each of those actors is also available separately on the Store.
Why does Netto only show offer prices? Netto Denmark doesn't run an online shop or publish any product catalogue — its only structured price data is the weekly leaflet, a time-boxed campaign feed. There is no everyday shelf price to fall back to.
Why does Lidl only show offer prices too? Lidl DK's product search API returns only the current weekly-offer rotation (roughly 325 items) rather than a static everyday catalogue, so every Lidl row here is treated as an offer for honesty, even though a handful carry a discount off a regular price.
Why is brand/category/unitPrice sometimes empty on Bilka?
Bilka's product pages carry this data in embedded JSON-LD, and not every listing includes the full set of optional fields — this is a source data-quality gap, not a bug in the matching. name, price, ean and url are always populated.
Do I need my own proxy? No, for the retailers that need one (Foetex, Bilka, REMA 1000, Lidl, Bilka ToGo) proxy handling is passed through automatically with a sensible Danish residential default. SPAR, MENY and Netto read plain public JSON APIs with no anti-bot, so they always run on the free proxy pool regardless of your setting — forcing a paid proxy onto them would cost more for no benefit.
Can I get just one physical SPAR or MENY store's catalogue?
Yes — set sparStore (e.g. erslev for erslev.spar.dk) or menyStore (e.g. saltum for saltum.meny.dk). Not every physical SPAR or MENY store runs the webshop this actor reads from — see the SPAR Denmark Scraper and MENY Denmark Scraper READMEs for verified store lists.
A retailer returned nothing for my query — did the run fail?
Only if every retailer returned nothing. A single retailer or query combination failing (timeout, no matches) is logged and the run continues with everything else; check KV store > RUN_SUMMARY for a per-retailer breakdown of what succeeded.
Pricing and cost
Cost estimate: this actor charges a start fee (per GB of memory) plus a small fee per result. The eight retailer scrapers it launches each bill their own pricing to your account on top, exactly as if you ran them directly — this meta adds its own charge per result on top of what the underlying scrapers already charge, the same disclosed double-charge model used by our other price-comparison metas. A three-query run across all eight chains at default settings returns 500-650 rows and costs roughly $1.50-3 depending on how many retailers you include; narrow retailers or searchQueries to control cost.
Usage cost for a run only settles once it reports SUCCEEDED — checking cost on a still-running or partially-finished run will show a number far below the final total.
Limitations
- This is a combined feed, not a cross-retailer price match. Unlike some comparison tools, rows are not merged across retailers into one line per product — Danish own-brand goods (REMA 1000's milk vs Foetex's milk) are genuinely different SKUs with different EANs, so merging them would be guesswork. Filter or pivot on
name/eanyourself if you need a side-by-side view. - Netto and Lidl rows are always
priceType: "offer"— see Retailer coverage. - Bilka's optional fields (brand, category, unit price, package size) are inconsistently populated at the source.
- SPAR and MENY coverage is per physical store, not nationwide — set
sparStore/menyStoreto the store you care about. MENY and SPAR are sibling Dagrofa banners that price independently per store; do not assume their rows are comparable to a "national" price either. - Data is scraped from public retailer sources and may change or move without notice.
Related actors
This matrix is built on our standalone Danish grocery scrapers, each of which you can also run on its own for a full-catalogue pull:
- Foetex Scraper
- Bilka Scraper
- Bilka ToGo Scraper
- REMA 1000 Denmark Scraper
- SPAR Denmark Scraper
- MENY Denmark Scraper
- Netto Denmark Offers Scraper
- Lidl Scraper
We run this comparison as a managed service too: scheduled runs, a fixed SKU list, and delivery to your inbox, Google Sheets, or API, maintenance included. See studioamba.dev/services or email hello@studioamba.dev for a free data sample.
Known issues (Sep 2026)
- Bilka's own site search occasionally returns zero sitemap matches for a Danish search term containing special characters (e.g. "mælk") even though the chain does stock matching products — this is a search-matching gap in the standalone
bilka-scraper, not in this matrix. The affected query/retailer combination is reported as failed inRUN_SUMMARYrather than silently dropped. - Bilka ToGo and MENY were wired into this matrix 2026-09-09 (parity rule: every standalone we own in a vertical belongs in its multiscraper) and cloud-verified alongside the other six retailers.