REWE vs Coop CH: two catalog API calls to check whether Germany really is 30% cheaper
This compares grocery prices between Germany and Switzerland by pulling the full product catalogs for REWE and Coop CH via Pepesto's /catalog endpoint. The script matches products by category, converts CHF to EUR, and produces a country-by-category scorecard showing which market is cheaper.
Run this yourself
$ PEPESTO_API_KEY=your_key node rewe-germany-vs-switzerland-prices.jsFull script: rewe-germany-vs-switzerland-prices.js. You'll need an API key to run it — get one here.
Getting started
The plan: pull the full catalog for REWE and Coop CH, match products by category, convert CHF to EUR at a fixed rate, and compare. No scraping, no browser automation — two API calls.
// Get your key first:
const linkRes = await fetch('https://s.pepesto.com/api/link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'your@email.com' }),
});
const { api_key } = await linkRes.json();
// export PEPESTO_API_KEY=your_keyThe first call — two catalogs, in parallel
Both catalog fetches run simultaneously using Promise.all. The Pepesto /catalog endpoint returns structured products including an entity_name field — the normalised product category (e.g. "Ham", "Spaghetti", "Fresh cheese") — which is what makes cross-store matching possible without fuzzy string matching.
const CHF_TO_EUR = 1.04; // April 2026 approximation
async function fetchCatalog(domain) {
const res = await fetch('https://s.pepesto.com/api/catalog', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.PEPESTO_API_KEY}`,
},
body: JSON.stringify({ supermarket_domain: domain }),
});
const data = await res.json();
return data.parsed_products;
}
const [reweCatalog, coopCatalog] = await Promise.all([
fetchCatalog('shop.rewe.de'),
fetchCatalog('coop.ch'),
]);Here's what the REWE catalog returns for one product:
{
"https://www.rewe.de/shop/p/3-glocken-genuss-pur-spaghetti-500g/8013454": {
"entity_name": "Spaghetti",
"names": {
"de": "3 Glocken Genuss Pur Spaghetti 500g",
"en": "3 Glocken Genuss Pur Spaghetti"
},
"price": 99,
"currency": "EUR",
"promo": true,
"promo_deadline_yyyy_mm_dd": "2026-04-11",
"price_per_meausure_unit": "1.98 EUR / kg",
"quantity_str": "500g"
}
}And the equivalent Coop CH entry (price in CHF, converted for comparison):
{
"https://www.coop.ch/de/lebensmittel/brot-backwaren/baeckerei/bio-tessinerbrot/p/7180433": {
"entity_name": "Bread",
"names": {
"de": "Bio Tessinerbrot",
"en": "Organic Tessin bread"
},
"price": 310,
"currency": "CHF",
"price_per_meausure_unit": "0.78 CHF / 100g",
"quantity_str": "397g",
"tags": ["bio"]
}
}The matching logic
For each category present in both catalogs, take the cheapest product in each store and compare the EUR-equivalent prices. Multiple products share the same entity_name category — the index keeps only the cheapest one per store, so you're always comparing the best available price. The CHF conversion is a fixed approximation — good enough for a snapshot comparison, even if it drifts slightly over time.
function buildCategoryIndex(catalog, storeCurrency) {
const index = {};
for (const [url, product] of Object.entries(catalog)) {
const category = product.entity_name; // normalised category, e.g. "Spaghetti"
if (!category) continue;
let priceEur = product.price;
if (storeCurrency === 'CHF') priceEur = Math.round(product.price * CHF_TO_EUR);
// Keep only the cheapest product for this category
if (!index[category] || priceEur < index[category].priceEur) {
index[category] = { name: product.names?.en || product.names?.de,
priceEur, quantityStr: product.quantity_str, url };
}
}
return index;
}
const reweIndex = buildCategoryIndex(reweCatalog, 'EUR');
const coopIndex = buildCategoryIndex(coopCatalog, 'CHF');What the data showed
REWE wins on pasta — the 3 Glocken Genuss Pur Spaghetti 500g came in at €0.99 on promotion, versus the Coop equivalent at over €2.50 converted. That's a real and significant gap. Canned goods and pulses also tended to be cheaper at REWE: Al Amier Kichererbsen (chickpeas, 265g) for €1.99 versus comparable Coop options for €2.80–3.20.
Dairy was more mixed. The Almette Natur fresh cheese at REWE (150g, €1.99) looks cheap, but Coop's Bio Sauerteigbrot (organic sourdough, 400g, CHF 4.60 = ~€4.78) is a completely different product class — the comparison breaks down when Swiss stores systematically carry more premium bio ranges.
The overall result: REWE was cheaper in roughly 65% of matched categories. But the average difference was closer to 18–22%, not 30%. And in categories like premium bread, Swiss organic products, and fresh cheese, Coop was often competitive once you account for what you're actually getting.
Next steps
To make the comparison more rigorous, you'd want to normalise by pack size (price per 100g) for every match, not just check the sticker price. The API does include price_per_meausure_unit for many products, so this is doable without manual effort — just parse that field instead of comparing raw prices.
The result
Two catalog API calls, currency conversion, and a category scorecard. Running time: under 8 seconds including both API calls. Germany is cheaper, but the gap is real — not 30%.
What else you could do?
Add Migros to the comparison as a second Swiss store — useful for checking whether REWE is closer to Coop or Migros on price for the same items. Run the comparison weekly and track drift over time — CHF has been strengthening, which changes the effective gap. Add a filter for promo: true on the REWE side to show best-case Germany prices, since REWE runs significant weekly promotions.