🇬🇧 Waitrose Example solution JavaScript /catalog API

Scanning the Waitrose catalog for this week's best promotions

This guide shows how to pull every Waitrose product currently on promotion using the Pepesto /catalog endpoint with promo_only set — useful for building a price alert tool or a weekly deals tracker. The script sorts the offers by percentage saved and outputs a ranked list of discounts.

Run this yourself

$ PEPESTO_API_KEY=your_key node waitrose-promotions-scanner.js

Full 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.

js
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 a bulk operation, priced separately from the other endpoints. Without any options it returns a full snapshot of the supermarket's product range — thousands of items — for €9.60. Setting promo_only to true returns only the products on offer, in the same shape, for €3.20. Either way, call it once, cache the result as a JSON file, and work from the cache. Don't call it on every page load.

The first call — pulling the Waitrose promotions

The /catalog call itself is simple — one POST with the domain and promo_only. The response is a map of product URLs to product objects.

js
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',
    promo_only: true,
  }),
});

const { parsed_products } = await catalogRes.json();
// parsed_products is an object with product URLs as keys

Each product in the response looks like this. Note the promo and promo_percentage fields — these are what we sort on.

json — example entry
{
  "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"
  }
}

Flattening and sorting is straightforward JavaScript — the Pepesto API has already done the hard work of normalising the data. The promo check is only a guard: with promo_only set, everything in the response is already on offer.

js
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 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 fetch. Add a local cache: if a JSON file named waitrose-promotions-{YYYY-MM-DD}.json exists, load from that instead of calling the API — re-runs are instant and you pay for the call 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.

Links

Ready to build?

Start scanning Waitrose promotions

Pull every Waitrose promotion in one call and surface the best discounts sorted by percentage saved.

28supermarkets 13countries 1schema Instant access