# Tesco Groceries Scraper — UK Products & Prices (`studio-amba/tesco-scraper`) Actor

Scrape the full Tesco UK groceries catalogue: product names, prices, Clubcard offers, price per unit, EAN codes, images, ratings and categories. Walks the department tree or a specific category. No login, no cookies.

- **URL**: https://apify.com/studio-amba/tesco-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** E-commerce
- **Stats:** 7 total users, 1 monthly users, 74.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 result scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Tesco Groceries Scraper

Scrape Tesco's UK OWN grocery range: product names, prices, Clubcard offers, price per unit, EAN codes, images, ratings, stock and full category paths. Tesco's own range only, with third-party Marketplace sellers excluded. No login, no cookies.

### Why use this actor?

Tesco is the UK's largest grocer, and its online range is one of the biggest price datasets in British retail. This actor gives you a clean, structured feed of Tesco's own grocery catalogue for price monitoring, competitor benchmarking, product matching, market research, or building a grocery price comparison. You get the same data the Tesco website shows shoppers, including Clubcard prices, without needing an account or a delivery slot.

**Own range only, no Marketplace.** Tesco now mixes third-party Marketplace items (sold and shipped by partners) into its listings, even inside grocery categories. This actor filters every listing to Tesco's own range at the source (`productSource=Ghs`) and additionally rejects any third-party record by type and seller field, so the output is 100% Tesco own-range with zero Marketplace contamination.

**Complete past the 10,000 cap.** Every Tesco listing is capped at 10,000 results, so a single broad crawl silently truncates large departments. This actor walks the category tree (superdepartment → department → aisle → shelf) and, whenever a node's own-range total hits the cap, descends into its child categories until every listing stays under 10,000. Products are deduped by Tesco product id (TPNC), and the run ends with a self-checking completeness assertion that compares the captured unique count against the authoritative own-range totals.

Tesco sits behind Akamai Bot Manager with hard IP-reputation blocking, so ordinary scrapers get an "Access Denied" before any product loads. This actor routes every request through the Bright Data Web Unlocker, which solves the Akamai challenge and returns the fully rendered page, so you get reliable results run after run.

### How to scrape Tesco data

1. Add this actor to your Apify account.
2. Provide a Bright Data API key (field `brightDataApiKey`, stored as a secret) or set the `BRIGHT_DATA_API_KEY` environment variable. Tesco cannot be reached without it.
3. Choose what to scrape:
   - Leave the input empty to walk the whole own grocery catalogue (all 10 grocery superdepartments).
   - Set `searchQuery` to scrape a keyword, e.g. `milk` or `cheddar`.
   - Set `categoryUrl` to scrape one category, e.g. `food-cupboard/world-foods` or a full `https://www.tesco.com/shop/en-GB/browse/...` URL.
   - Or paste a list of listing URLs into `startUrls`.
4. Set `maxProducts` to cap the run (default 100, prefilled 20 for a quick test). Set it high, e.g. `40000`, for a full-catalogue pull.
5. Run the actor. Results stream to the dataset and can be exported as JSON, CSV, Excel or fed to an API.

The actor filters every listing to Tesco's own range, reads the authoritative own-range total, and paginates 48 products per page. Whenever a listing's own-range total hits Tesco's 10,000 cap, the actor drills into that category's children so no shelf is ever truncated. Set `enumerateOnly: true` to walk the tree and report the exact own-range catalogue size without scraping products, a cheap way to verify completeness before a full run.

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `brightDataApiKey` | String (secret) | Yes\* | Bright Data Web Unlocker API key. \*Falls back to `BRIGHT_DATA_API_KEY` env var. |
| `searchQuery` | String | No | Search Tesco groceries by keyword |
| `categoryUrl` | String | No | A Tesco category URL or slug to scrape |
| `startUrls` | Array | No | One or more Tesco listing URLs |
| `maxProducts` | Integer | No | Maximum products to return (default 100) |
| `enumerateOnly` | Boolean | No | Walk the tree and report the authoritative own-range catalogue size without scraping products |
| `proxyConfiguration` | Object | No | Proxy settings for auxiliary requests |

If no category, search or URLs are given, the actor scrapes every grocery superdepartment (own range only).

**`searchQuery` takes product keywords, not barcodes.** Tesco's own site search returns no results for a bare EAN/GTIN (verified Aug 2026: `5000157024671` gives "No matches" on tesco.com itself), so an EAN as `searchQuery` returns 0 items. Search by name instead — every result carries its `ean` field, so you can match by barcode on your side after scraping.

### Output

Each result contains:

| Field | Type | Example |
|-------|------|---------|
| `name` | String | `"Tesco Pink Lady Apples 5 Pack"` |
| `brand` | String | `"Tesco"` |
| `price` | Number | `2.90` |
| `currency` | String | `"GBP"` |
| `pricePerUnit` | String | `"£0.58/each"` |
| `discount` | String | `"£2.50 Clubcard Price"` |
| `ean` | String | `"00000003249833"` |
| `sku` | String | `"77091643"` (Tesco TPNB) |
| `productId` | String | `"284477542"` (Tesco TPNC) |
| `inStock` | Boolean | `true` |
| `rating` | Number | `4.5` |
| `reviewCount` | Integer | `190` |
| `imageUrl` | String | Primary product image URL |
| `category` | String | `"Fresh Food > Fresh Fruit > Apples & Pears > Pink & Red Apples"` |
| `url` | String | Full product page URL |
| `scrapedAt` | String | ISO 8601 timestamp |

### Example output

```json
{
    "name": "Tesco Pink Lady Apples 5 Pack",
    "brand": "Tesco",
    "price": 2.90,
    "currency": "GBP",
    "pricePerUnit": "£0.58/each",
    "discount": "£2.50 Clubcard Price",
    "ean": "00000003249833",
    "sku": "77091643",
    "productId": "284477542",
    "inStock": true,
    "rating": 2.9,
    "reviewCount": 190,
    "imageUrl": "https://digitalcontent.api.tesco.com/v2/media/ghs/...jpeg",
    "category": "Fresh Food > Fresh Fruit > Apples & Pears > Pink & Red Apples",
    "url": "https://www.tesco.com/groceries/en-GB/products/284477542",
    "scrapedAt": "2026-07-03T14:51:02.015Z"
}
```

### Prices and stock

- Prices are in GBP and reflect the standard Tesco online shelf price.
- `discount` carries the Clubcard price or promotion text when an offer is active.
- `pricePerUnit` gives the comparable unit price (per kg, per litre, per each, etc.).
- Stock and availability can vary by delivery postcode. This actor uses Tesco's default national online context, which is the right baseline for catalogue and price data.

### Cost estimate

Each Tesco listing page returns 48 products. Tesco's own grocery range is roughly 29,000 products, so a full own-range pull needs roughly 600-700 page fetches through the Bright Data Web Unlocker. For everyday use, scraping a single category or a search term is fast and cheap. Use `maxProducts` to control the size and cost of each run, or `enumerateOnly` to check the exact catalogue size first.

### Limitations

- A Bright Data Web Unlocker API key is required. Tesco's Akamai protection cannot be bypassed without it.
- Output is Tesco's own range only. Third-party Marketplace items are intentionally excluded.
- Brand is only populated for detectable own-brand lines; the Tesco listing data does not expose a brand field for every product.
- Tesco caps very broad listings at 10,000 results per view. The actor handles this automatically by drilling the category tree so no shelf is truncated; the full grocery own range is well under this cap per shelf.
- Clubcard prices are time-boxed promotions and change frequently.
- Data is scraped from the public website and may change without notice. Respect Tesco's terms of service and use responsibly.

### How to scrape Tesco data on a schedule

We run this scraper as a managed service for businesses: scheduled runs, deduplication, delta detection, and delivery to your inbox, Google Sheets, or API, maintenance included. We can also build a custom version with your exact fields and filters, or combine multiple grocery sources into one feed.

See [studioamba.dev/services](https://studioamba.dev/services/) or email <hello@studioamba.dev> for a free data sample. We maintain 300+ European web scrapers and answer within one business day.

# Actor input Schema

## `searchQuery` (type: `string`):

Search Tesco groceries by keyword (e.g., 'milk', 'coffee', 'cheddar').

## `categoryUrl` (type: `string`):

A Tesco groceries category to scrape. Full URL (https://www.tesco.com/groceries/en-GB/shop/fresh-food/fresh-fruit/all) or a slug (fresh-food/fresh-fruit/all).

## `startUrls` (type: `array`):

One or more Tesco category or search listing URLs to scrape.

## `maxProducts` (type: `integer`):

Maximum number of products to return across all seeds. Set high (e.g., 60000) for a full-catalogue pull.

## `requestDelaySecs` (type: `integer`):

Minimum pause between page fetches. Bright Data rate-limits rapid-fire requests to Tesco; a delay of 15-30s keeps a full-catalogue run under the limit (slower but reliable). 0 = full speed.

## `enumerateOnly` (type: `boolean`):

Walk the category tree and report the authoritative own-range catalogue size (summed leaf totals) without paginating or pushing products. Cheap completeness check.

## `brightDataApiKey` (type: `string`):

Bright Data API key for the Web Unlocker zone. Required — Tesco is behind Akamai Bot Manager and cannot be reached without it. Falls back to the BRIGHT\_DATA\_API\_KEY environment variable.

## `proxyConfiguration` (type: `object`):

Proxy settings. Tesco access itself goes through the Bright Data Web Unlocker; this proxy is used for any auxiliary requests.

## Actor input object example

```json
{
  "searchQuery": "milk",
  "maxProducts": 20,
  "requestDelaySecs": 0,
  "enumerateOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQuery": "milk",
    "maxProducts": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "GB"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/tesco-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQuery": "milk",
    "maxProducts": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/tesco-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQuery": "milk",
  "maxProducts": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}' |
apify call studio-amba/tesco-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/tesco-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/tvaX4WQq7a2aJVVdl/builds/sNFJg5lW0SDkrPlEP/openapi.json
