🇬🇧 Tesco UK Example solution JavaScript /oneshot API

Adding a "Shop This Recipe at Tesco" button in one evening

This guide shows how to add a "Shop this recipe at Tesco" button to any web page using the Pepesto /oneshot endpoint. Pass a recipe URL, get back a redirect URL that opens a pre-filled Tesco cart — one call, no product catalog, no session management needed.

Run this yourself

$ PEPESTO_API_KEY=your_key node tesco-recipe-to-cart-oneshot.js

Full script: tesco-recipe-to-cart-oneshot.js. You'll need an API key to run it — get one here.

Getting started

Send a POST to /api/link with your email and get a key back immediately — no waiting for approval.

js
const response = await fetch('https://s.pepesto.com/api/link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'alex@myblog.example' }),
});
const { api_key } = await response.json();
console.log(api_key); // pepesto_live_xxxxxxxxxxxx

Save the key as an environment variable in your hosting platform's dashboard, then pass it as the Authorization header on API calls.

The first call

The /api/oneshot endpoint is designed exactly for this use case. You pass it a recipe URL and a supermarket domain, and it returns a redirect_url that opens a pre-filled cart. That's the entire API surface for this project.

js
const API_KEY = process.env.PEPESTO_API_KEY;

async function buildTescoCart(recipeUrl, extraText = '') {
const body = {
  content_urls: [recipeUrl],
  supermarket_domain: 'tesco.com',
};

if (extraText) {
  body.content_text = extraText;
}

const res = await fetch('https://s.pepesto.com/api/oneshot', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${API_KEY}`,
  },
  body: JSON.stringify(body),
});

const data = await res.json();
return data.redirect_url;
}

// On the blog post page — swap in the actual recipe URL
const cartUrl = await buildTescoCart(
'https://www.bbcgoodfood.com/recipes/pizza-margherita-4-easy-steps',
'also add sparkling water and olive oil'
);

// Open the pre-filled Tesco basket in a new tab
window.open(cartUrl, '_blank');

The API response looks like this:

json — response
{
"redirect_url": "https://app.pepesto.com/composed?force=1&req=GigKJmFsc28gYWRk..."
}

When a reader hits that URL, Pepesto parses the recipe, matches ingredients to Tesco SKUs, and bounces them directly into a populated Tesco basket. All server-side — the reader never sees a loading state beyond a brief redirect.

What the data showed

The content_text field is useful for appending staples to every cart — "also add sparkling water and olive oil" — without modifying the recipe itself. Matching held up well on obscure recipes (za'atar, pomegranate molasses, dried barberries). A few niche ingredients came back unmatched, but staples were consistently there.

Response time is consistently under two seconds for a typical BBC Good Food URL. For high-traffic pages, server-side render the redirect URL at build time and serve it statically.

Next steps

Once you have the redirect URL, the simplest integration is a plain anchor tag with target="_blank". A slightly more polished approach: pre-fetch the URL when the reader scrolls the recipe into view using an IntersectionObserver, so by the time they reach the button it's already resolved. The button goes from disabled to active with a small transition — no spinner, no wait.

js — browser snippet
const btn = document.querySelector('#tesco-btn');
const recipeUrl = document.querySelector('link[rel="canonical"]').href;

const observer = new IntersectionObserver(async ([entry]) => {
if (!entry.isIntersecting) return;
observer.disconnect();

const res = await fetch('/api/cart-url', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ recipeUrl }),
});
const { cartUrl } = await res.json();

btn.href = cartUrl;
btn.removeAttribute('disabled');
btn.textContent = 'Shop this recipe at Tesco';
}, { threshold: 0.5 });

observer.observe(document.querySelector('.ingredients'));

The /api/cart-url route on my server just proxies to Pepesto with the API key, so the key never touches the browser.

The result

One /oneshot call per page load, a redirect URL returned, and a pre-filled Tesco basket on click. Tesco's basket may substitute products when a specific SKU is unavailable — that's Tesco's own availability logic, not something Pepesto controls.

What else you could do?

Add Sainsbury's as a second cart option — the API supports it with a one-line change to supermarket_domain. Add a "serves N" multiplier so readers can adjust quantities before sending to cart — that requires switching from /oneshot to /parse + /products + /session to control individual quantities. Show an estimated cart total on the button itself ("Shop this recipe (~£12)") using a /products call to get prices before building the session.

Links