🇬🇧 ASDA Example solution JavaScript /suggest + /products

Feeding 4 for £50 at ASDA — the script that settled the bet

This script tests whether feeding four people for a week on £50 is achievable at ASDA. It uses Pepesto's /suggest endpoint to find budget-friendly dinners, then /products to price each ingredient list at asda.com — tallying the total against the target.

Run this yourself

$ PEPESTO_API_KEY=your_key node asda-budget-week-50-pounds.js

Full script: asda-budget-week-50-pounds.js. You'll need an API key to run it — get one here.

Getting started

One API key, one environment variable, same as always.

js
async function requestApiKey(email) {
  console.log(`\nRequesting API key for: ${email}`);

  const response = await fetch(`${BASE_URL}/link`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  });

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`POST /api/link failed (${response.status}): ${text}`);
  }

  const data = await response.json();

  console.log('\n✓ Done, data contains now your api key.');
  console.log('\nOnce you have your key, run:');
  console.log('  export PEPESTO_API_KEY=pep_sk_your_key_here\n');

  return data;
}

The first call — finding budget recipes with /suggest

The /suggest endpoint searches the Pepesto recipe database without needing a URL. Pass a free-text query and get back structured recipes — complete with ingredients, steps, and a kg_token ready for the product lookup step.

js
// /suggest returns 3 recipes per call, so several queries are needed to fill a
// week. The same dish can come back from two queries — and even twice within a
// single call — with a different kg_token each time, so duplicates have to be
// dropped by title. Each query pulls on a different main ingredient.
async function suggestRecipes(limit = 5) {
  const queries = [
    'cheap family dinner with chicken, under 10 pounds',
    'cheap family dinner with beef mince, budget friendly',
    'cheap vegetarian family dinner, budget friendly',
  ];

  const responses = await Promise.all(queries.map(async (query) => {
    const response = await fetch(`${API_BASE}/suggest`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`,
      },
      body: JSON.stringify({ query }),
    });

    const text = await response.text();

    if (!response.ok) {
      throw new Error(`/suggest failed: ${response.status} ${text}`);
    }

    return JSON.parse(text);
  }));

  const seen = new Set();
  const recipes = [];
  for (const r of responses.flatMap(d => d.recipes)) {
    const key = r.title.trim().toLowerCase();
    if (seen.has(key)) continue;
    seen.add(key);
    recipes.push({
      title: r.title,
      kg_token: r.kg_token,
      nutrition: r.nutrition,
      ingredients: r.ingredients,
    });
  }

  if (recipes.length < limit) {
    console.log(`Only ${recipes.length} distinct dinners came back — planning for those.`);
  }

  return recipes.slice(0, limit);
}

Each call comes back with three fully structured recipes. Here's one of them:

json — one recipe from /suggest
{
  "title": "Baked Tortillas with Minced Meat, Vegetables, and Cheese",
  "ingredients": [
    "130g canned corn", "1 red pepper (~110g)", "1 yellow bell pepper (~150g)",
    "400g canned tomatoes", "1 onion (~150g)", "1 bunch parsley",
    "200g cheddar cheese", "700g ground beef", "300g tortillas",
    "sunflower oil", "salt", "black pepper", "chilli powder",
    "smoked paprika", "soy sauce"
  ],
  "nutrition": { "calories": 4287, "protein_grams": 238, "fat_grams": 260 },
  "kg_token": "EjoKOEJha2VkIFRvcnRpbGxhcyB3aXRoIE1pbmNlZCBNZWF0..."
}

Then I hit /products for each recipe at asda.com, picking the cheapest product per ingredient line.

js
async function fetchAsdaProducts(kgToken) {
  const response = await fetch(`${API_BASE}/products`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${API_KEY}`,
    },
    body: JSON.stringify({
      recipe_kg_tokens: [kgToken],
      supermarket_domain: 'asda.com',
    }),
  });

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`/products failed: ${response.status} ${text}`);
  }

  const data = await response.json();
  return data.items;
}

Then one helper takes the cheapest matching product for every ingredient line and adds them up:

js
function cheapestCost(items) {
  return items.reduce((total, item) => {
    if (!item.products || item.products.length === 0) return total;
    // Sort by price ascending to find the cheapest match
    const prices = item.products.map(p => p.product.price.price).sort((a, b) => a - b);
    return total + prices[0];
  }, 0);
}

main() runs those two across all five recipes in parallel and tallies the week.

What the data showed

The script returned £43.17 for five dinners at ASDA, using the cheapest available product per ingredient. Note that some ingredients are priced for pack quantities you won't fully use in a single recipe — the smoked paprika being a typical example.

A few observations from the data. ASDA's own-brand ranges are competitive: ASDA Finely Sliced Bavarian Style Ham 120g came in at £2.24 including a live promotion. Canned goods were uniformly low: Island Sun Sweetened Condensed Milk 397g at £1.47, Tropical Sun Cornmeal Fine Polenta 500g at £1.00. Fresh protein was the main budget driver — chicken thighs and beef mince pushed individual meal costs toward £12–14.

Next steps

From a list of priced items, the natural next step is a cart. Build a skus array from the matched products (each with session_token and num_units_to_buy), call /api/session with the supermarket_domain and skus, then pass the returned session_id to /api/checkout to get the basket URL:

json — example /session response
{
  "session_id": "ses_4rQnBwKpLxZm7vTe"
}

Pass the session_id to /checkout to get a pre-filled ASDA basket link. Since ASDA doesn't have a native guest-shareable cart URL, the Pepesto session acts as the share mechanism — you send the link to anyone in the household and they can open it and order.

The result

The main implementation work is formatting the output table — a per-meal line, a grand total, and an over/under budget verdict. The API calls themselves are straightforward.

Running this weekly over a month, results vary by about £4–8 depending on active promotions and which specific recipes the query returns. The £50 budget is achievable most weeks — the main risk is beef mince pricing.

What else you could do?

Three natural extensions. Filter for items currently on promotion — ASDA runs significant rollback pricing that the promo: true flag surfaces directly. Add a "prefer own-brand" sort that deprioritises branded products when a cheaper ASDA own-brand match exists on the same ingredient line. Run the same five meals against Tesco and Morrisons using the same kg_tokens — no re-parsing needed — to find the cheapest store for your specific recipe set.

Links

Ready to build?

Start building budget shopping lists at ASDA

Use /suggest to find budget-friendly recipes and /products to price them at ASDA with a single key.

27supermarkets 13countries 1schema Instant access