Scanning the Waitrose catalog for this week's best promotions
This guide shows how to pull live product data from Waitrose and scan for active promotions using the Pepesto /catalog endpoint — useful for building a price alert tool or a weekly deals tracker. The script filters for promo: true, sorts by percentage saved, and outputs a ranked list of discounts.
Run this yourself
$ PEPESTO_API_KEY=your_key node waitrose-promotions-scanner.jsFull script: waitrose-promotions-scanner.js. You'll need an API key to run it — get one here.
Getting started
Getting an API key is instant.
const res = await fetch('https://s.pepesto.com/api/link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'eleanor@example.com' }),
});
const { api_key } = await res.json();
// export PEPESTO_API_KEY=pepesto_live_...One thing to understand before calling /api/catalog: it is an expensive API operation, priced separately from the other endpoints. It returns a full snapshot of the supermarket's product range — thousands of items. You should call it once, cache the result as a JSON file, and filter locally. Don't call it on every page load.
The first call — pulling the full Waitrose catalog
The /catalog call itself is simple — one POST with the domain. The response is a map of product URLs to product objects.
const catalogRes = 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: 'waitrose.com',
}),
});
const { parsed_products } = await catalogRes.json();
// parsed_products is an object with product URLs as keysEach product in the catalog looks like this. Note the promo and promo_percentage fields — these are what we filter on.
{
"https://www.waitrose.com/ecom/products/ainsley-harriott-moroccan-medley-couscous/401856-535088-535089": {
"entity_name": "Couscous",
"tags": ["mixture", "flavoured"],
"names": { "en": "Ainsley Harriott Moroccan Medley Couscous" },
"price": 70,
"promo": true,
"promo_percentage": 26,
"price_per_meausure_unit": "0.70 / 100g",
"quantity_str": "100g",
"quantity": { "Unit": { "HundredGrams": 1 }, "accurate_grams": 100 }
},
"https://www.waitrose.com/ecom/products/all-things-butter-organic-salted-butter/934056-850330-850331": {
"entity_name": "Salted butter",
"tags": ["bio"],
"names": { "en": "All Things Butter Organic Salted Butter" },
"price": 225,
"promo": true,
"promo_percentage": 26,
"price_per_meausure_unit": "£11.25/kg",
"quantity_str": "200g"
}
}Filtering and sorting is straightforward JavaScript — the Pepesto API has already done the hard work of normalising the data.
function extractPromotions(products) {
const promoItems = [];
for (const [url, product] of Object.entries(products)) {
if (!product.promo) continue;
promoItems.push({
url,
name: product.names?.en || product.entity_name,
pricePence: product.price,
promo_percentage: product.promo_percentage || 0,
pricePerUnit: product.price_per_meausure_unit || '',
quantity: product.quantity_str || '',
tags: product.tags || [],
});
}
// Sort by discount percentage, highest first
promoItems.sort((a, b) => b.promo_percentage - a.promo_percentage);
return promoItems;
}
const promotions = extractPromotions(parsed_products);
console.log(`${promotions.length} items on promotion`);
promotions.slice(0, 15).forEach(item => {
console.log(`${item.promo_percentage}% off £${(item.pricePence / 100).toFixed(2)} ${item.name}`);
});What the data showed
The catalog snapshot surfaces promotions immediately: Ainsley Harriott Moroccan Medley Couscous and Ainsley Harriott Roasted Vegetable Couscous both at 26% off, All Things Butter Organic Salted Butter at 26% off, All Things Cottage Cheese Natural at 11% off. The average discount across all promoted items ran at about 18%. Waitrose clusters promotions at round percentages rather than the fractional figures common at discount chains.
The tags field enables further filtering: combining promo: true with tags.includes("bio") gives a shortlist of organic items currently on promotion.
Next steps
Plan meals around what's discounted this week rather than the other way around. Take the top promoted proteins, produce, and dairy, then run a /suggest query mentioning those items to get recipes that use them. Then /products to confirm Waitrose stocks everything else you need, and /session to build the cart.
Run the scanner as a weekly cron job and diff the output to see what's new or changed since last week.
The result
The script runs in about four seconds, most of which is the catalog fetch. Add a local cache: if a JSON file named waitrose-catalog-{YYYY-MM-DD}.json exists, load from that instead of calling the API — re-runs are instant and the catalog quota is used only once per day. Note: promo_percentage isn't present on every promoted item. Some items have promo: true but no percentage (e.g. MyWaitrose member pricing). Treat a missing percentage as zero so those items sort to the bottom rather than causing an error.
What else you could do?
Send a weekly email digest of the top promotions — pipe the top 20 into a simple HTML email scheduled for Saturday morning. Track which promotions are recurring versus genuinely new — if Waitrose has offered 26% off the same couscous every month, the "promotion" is effectively the normal price. Flag items that are promoted this week but weren't last week, as those are the genuinely time-limited deals worth acting on.