# Tesco UK Scraper — Prices, Clubcard, Barcodes (`architekture/tesco-uk-scraper`) Actor

Search Tesco UK prices, Clubcard offers, unit prices and barcodes. Keyword, category and product-ID inputs. Pay per result.

- **URL**: https://apify.com/architekture/tesco-uk-scraper.md
- **Developed by:** [architek.earth](https://apify.com/architekture) (community)
- **Categories:** AI, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 products

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Tesco UK Scraper — Prices, Clubcard, Barcodes

**A recorded validation run returned 10 preview product rows in 3 seconds.** Run `f7eZtaratQj5ey7LG`, build 1.0.8, 15 October 2026. This was an owner test, not customer activity or a throughput guarantee.

From **Architekture**, makers of the Woolworths AU, Coles AU and ALDI AU scrapers. See our [studio profile](https://apify.com/architekture).

Collect Tesco UK prices, Clubcard offers, unit prices, available GTIN barcodes and review information through keywords, category pages or product IDs. **27 product fields** in both basic and detail modes, excluding query/status metadata. Detail mode requests the product page to populate the same fields; individual values may remain empty.

### Why choose this actor?

- Keep shelf and loyalty prices distinct with `price`, `clubcardPrice` and `promotions`.
- Match products using `id` and available `gtin`; preserve barcode leading zeros.
- Identify missing enrichment through `detailStatus`; usable basic fallbacks use the basic event.
- Continue larger collections with `RESUME_STATE` and inspect coverage through `RUN_SUMMARY`.
- Use the same product-field layout across inputs; `source`, `searchTerm` and `categoryUrl` identify context.

### Worth knowing up front

- UK scope only. Other countries and non-Tesco URLs are rejected.
- This actor does not return ingredients, allergens, nutrition tables or per-store stock quantities. `isForSale` is a source availability flag, not a stock count or delivery promise for your address.
- `clubcardPrice` requires a source promotion marked `CLUBCARD_PRICING` with an unambiguous single-item price in its offer text. Null is not zero. Interpret multi-buy offers from `promotions` rather than assuming a unit price.
- Detail mode has the same 27 fields as basic mode. A successful response does not guarantee a barcode, rating or other optional value.
- `reviews` contains available source review information, not a promise of individual review text. Nested source structures may change.
- The implementation reads embedded page state and retries blocked responses. Direct connection is the validated default. Upstream blocking can still occur; retries do not guarantee access.
- A successful run may have capped or partial coverage. Read `RUN_SUMMARY` before treating the dataset as complete.

### Try the example

Each authenticated Apify account receives **20 lifetime attempts**, each limited to one search term, one result page and **10 basic rows**. Failed attempts count after reservation. No automatic paid fallback.

```json
{
  "preview": true,
  "searchTerms": ["milk"],
  "maxItemsPerQuery": 10,
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

Preview makes no custom result-event charge. Exhaustion returns `PREVIEW_LIMIT` without collection; quota service errors fail closed. The ten-row preview completed in 3 seconds during validation; timings vary. For paid collection, explicitly set `preview: false`. The Console prefill is paid basic mode with ten rows; omitted `maxItemsPerQuery` defaults to 100.

### Input parameters

Paid runs can combine search, categories and direct IDs. Duplicate product IDs are delivered once across sources; the first occurrence supplies context. There is no separate operation selector.

| Parameter | Type | Used by | Description |
|---|---|---|---|
| `searchTerms` | string array | Search, preview | Nonempty keywords; duplicate terms removed. Preview accepts one term. |
| `categoryUrls` | string array | Paid categories | Tesco UK shop URLs or paths, such as `/groceries/en-GB/shop/bakery/all`. |
| `productIds` | string array | Paid direct lookup | Numeric IDs or UK product URLs. Always requests details. Not subject to the per-query row cap. |
| `maxItemsPerQuery` | integer | Search, categories | New unique rows per query per run. Default 100, form prefill 10; 0 means uncapped. Preview remains capped at 10. |
| `includeDetails` | boolean | Search, categories | Default false. Product-page enrichment with basic fallback if unavailable. Not allowed in preview. |
| `sortBy` | string | Search, categories | `relevance`, `price-ascending` or `price-descending`. Default relevance. |
| `proxyConfiguration` | object | All collection | Direct connection by default; optional proxy configuration. |
| `preview` | boolean | Preview | Default false. Twenty lifetime attempts; no categories, IDs, details or continuation. |
| `resumeState` | object | Paid continuation | Copy a prior `RESUME_STATE`; collection settings must match. Row cap may change. |
| `feedback` | string | All | Up to 4,000 characters logged for human review; does not affect output. No credentials or personal data. |

### Stable fields for automations

These 27 top-level product-field names are the compatibility contract. New fields are additive. Handle null/empty values; nested undocumented source keys are passthrough bonuses, not stable bindings.

| Fields | Meaning |
|---|---|
| `id`, `tpnb`, `tpnc` | Product identifiers; normalized string `id` is the deduplication key. |
| `gtin` | Available barcode. Preserve leading zeros. |
| `title`, `brandName` | Title and available brand name. |
| `price`, `unitPrice`, `unitOfMeasure` | Shelf price, unit price and measure; UK monetary values are GBP. |
| `clubcardPrice` | Single-item GBP amount parsed from the selected Clubcard offer text, or null. Ambiguous offers remain null. |
| `promotionDescription`, `promotionEndDate`, `promotions` | Selected offer text/end date and normalized offer list; selected offer favors Clubcard. |
| `isForSale`, `isNew`, `status` | Source availability, new-product flag and status. |
| `averageWeight`, `productType` | Source weight and product classification. |
| `superDepartmentName`, `departmentName`, `aisleName`, `shelfName` | Category hierarchy. |
| `restrictions`, `unavailabilityReasons`, `reviews` | Available source information; nested structures are passthrough. |
| `defaultImageUrl`, `url` | Available image URL and constructed UK product URL. |

Conditional metadata: `source` is `search`, `category` or `id`; `searchTerm` appears for search and `categoryUrl` for categories. `detailStatus` is `complete` for a delivered detail response, `unavailable` for a basic fallback, and absent for basic-only collection. Complete does not mean every optional value exists.

`RUN_SUMMARY` contains `status`, `listRows`, `detailRows`, `unavailableDetails` and `preview`. Statuses: `COMPLETE`, `LIMITED`, `PARTIAL`, `BUDGET_LIMIT`, `PREVIEW_LIMIT`, `FAILED`. Inspect counts too: a single status cannot express every condition. Input errors before collection may not produce a summary.

### Output examples

Recorded 15 October 2026: search/preview on build 1.0.4, category and direct ID on build 1.0.6. These are abridged historical observations, not current-price guarantees. Optional fields can be null.

#### Search / preview

Run `dD8kotVz6YnIsMHab`.

```json
{
  "id": "254656543",
  "title": "Tesco British Semi Skimmed Milk 2.272L, 4 Pints",
  "gtin": "05000436589457",
  "price": 1.65,
  "unitPrice": 0.73,
  "unitOfMeasure": "litre",
  "source": "search",
  "searchTerm": "milk"
}
```

#### Category

Run `j6Z0IPhAirfwYMoAL`.

```json
{
  "id": "310128122",
  "title": "Soreen Gingerbread Christmas Lunchbox Loaves 5 Pack 150G",
  "gtin": "05088722226002",
  "price": 1.8,
  "unitPrice": 12,
  "unitOfMeasure": "kg",
  "source": "category",
  "categoryUrl": "https://www.tesco.com/groceries/en-GB/shop/bakery/all",
  "detailStatus": "complete"
}
```

#### Direct ID

Run `j6Z0IPhAirfwYMoAL`.

```json
{
  "id": "305965589",
  "title": "Biotiful Gut Health Kefir Milk Drink Original 1 Litre",
  "gtin": "05060337221085",
  "price": 3.5,
  "clubcardPrice": 3.0,
  "promotionDescription": "£3.00 Clubcard Price",
  "unitPrice": 3.5,
  "unitOfMeasure": "litre",
  "source": "id",
  "detailStatus": "complete"
}
```

### Advanced usage

#### Monitor prices and Clubcard offers

Start with daily fresh collections and measure usefulness before increasing frequency. Omit `resumeState` to collect existing IDs again. Save the run timestamp with each observation and compare shelf and Clubcard prices separately.

#### Join retailer datasets

Use nonempty `gtin` as a starting point, then verify pack size, measure and variant. Do not join Tesco IDs to other retailers' IDs. A barcode match does not establish identical availability or offer eligibility.

#### Continue a larger collection

Copy the complete JSON value of `RESUME_STATE` from the prior run's default key-value store into `resumeState`. Keep query inputs, order, details and sort unchanged. The row cap may change. `continuePreviousRun` is unsupported; storage is never opened implicitly.

The checkpoint contains `version`, a collection `fingerprint` and `deliveredProductIds`. Continuation re-scans from page one and skips checkpointed products, including basic fallbacks. It costs requests and is not a frozen catalog snapshot. Start fresh to update prices or retry enrichment for a delivered fallback.

Checkpoints follow confirmed output batches, including partial batches at a billing limit. Output and checkpoint writes are separate: a crash or failed checkpoint write may cause repeated rows on resume. Deduplicate combined exports by `id`. No exactly-once guarantee is made.

#### Process partial results

Read `RUN_SUMMARY`. `PARTIAL` indicates missing enrichment; inspect `detailStatus`. `LIMITED` indicates a collection cap and `BUDGET_LIMIT` a billing stop. Collection errors record `FAILED` when storage is available. Already delivered rows remain useful; failed explicit lookups remain eligible for retry.

### Integrations

- **Dataset exports:** use Apify JSON/CSV exports. Preserve nested promotions/reviews as JSON or flatten deliberately.
- **Google Sheets:** map stable scalar fields through an integration or CSV import. No preconfigured Sheets workflow is bundled.
- **Make, Zapier and n8n:** start a run, wait, inspect its summary, then fetch dataset items with pagination.
- **Webhooks:** trigger downstream work on completion/failure; platform success can still mean partial coverage.
- **Apify Scheduler:** schedule fresh inputs for monitoring. Avoid overlapping runs unless downstream processing deduplicates them.

### Pricing: pay per event

Hosted pricing verified on 15 October 2026. Platform usage is included: the studio pays those costs.

| Event | Price | What it covers |
|---|---|---|
| Preview | No result-event charge | Reserved attempt, one page, up to ten basic rows. |
| `product` | $2 / 1,000 rows | Delivered basic row, including usable enrichment fallback. |
| `product-detail` | $5 / 1,000 rows | Delivered detail row, instead of the basic event, not in addition. |

Nothing is charged for failed requests by this actor's result-event logic. A usable fallback is a delivered result. There is no startup event. At these rates, ten basic rows cost $0.020 and two details cost $0.010. Owner validation costs are not customer revenue.

### FAQ

**Do I need a Tesco or Clubcard login?** No customer login or cookies are requested. This does not guarantee uninterrupted upstream access.

**Why is `clubcardPrice` null?** The source may omit its offer text, or the offer may not specify an unambiguous single-item Clubcard price. Null is not zero.

**Does it return ingredients, allergens or stock quantities?** No. Those are outside this actor's schema.

**Does detail mode add fields?** No. It can populate more values in the same 27 fields; optional values may still be empty.

**Do previews reset monthly?** No. Twenty lifetime attempts per authenticated account, with failures counted after reservation. No paid fallback.

**Can I resume a different query?** No. Collection settings must match; you can change the row cap. Start fresh for a different query or price refresh.

**Does a successful run cover every product?** No. Caps, missing enrichment and changing source listings affect coverage. Read the summary.

**Why was an enriched request billed as basic?** Its detail lookup was unavailable but the listing row was usable. The row carries `detailStatus: "unavailable"`.

### Copy to your AI assistant

Use Tesco actor ID `KgfjS0Wkc7ffg3Uxi` (`architekture/tesco-uk-scraper`). Python: `from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("KgfjS0Wkc7ffg3Uxi").call(run_input={"preview": True, "searchTerms": ["milk"], "maxItemsPerQuery": 10, "proxyConfiguration": {"useApifyProxy": False}}); rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())`. Preview caps this at ten rows; stream large paid datasets instead of materializing them. Inspect authenticated `GET https://api.apify.com/v2/acts/KgfjS0Wkc7ffg3Uxi/builds/default` for `inputSchema`, `actorDefinition.storages.dataset` and `readme`. Obtain a token at [Apify integrations](https://console.apify.com/account/integrations) and keep it secret. Read `RUN_SUMMARY` and `RESUME_STATE` in the run's default key-value store. `feedback` is logged for human review without changing output; exclude credentials and personal data.

### More Architekture scrapers

Check the [studio profile](https://apify.com/architekture) for availability. Pipeline entries are not promises of release.

**Grocery — Australia & New Zealand:** Woolworths AU, ALDI AU and Coles AU are published. Woolworths NZ is in preparation.

**Grocery — United Kingdom:** Tesco UK — **you are here**. Sainsbury's UK is published.

**Research & regulatory:** PubMed, ClinicalTrials.gov, arXiv and openFDA — in preparation.

**Startups & companies:** Y Combinator, Product Hunt, SEC EDGAR, UK Companies House and Apollo.io — in preparation.

**Communities & publishing:** Telegram, Meetup, Substack, Hacker News and Reddit — in preparation.

**Utilities:** Screenshot & PDF Renderer — repository candidate; listing unverified. Retired catalog tools are omitted from this product family.

### Support

Open an issue on the actor page with the run ID and a small reproducible input, excluding secrets. A human reply within one day is our studio commitment, not a measured response-time claim. Use `feedback` for field requests and notes.

### For AI agents & LLM apps

`tesco-uk-scraper` returns Tesco UK products with prices — including resolved Clubcard and multibuy promotion strings — as deterministic, schema-stable JSON, one object per line, so agents and RAG pipelines can parse it without defensive code. Invoke it from any agentic workflow via the Apify API or the Apify MCP server (`mcp.apify.com`) as a tool call; pay-per-event billing charges per delivered row (`product` or `product-detail`) and nothing for failed requests. Honest limits: pages missing Tesco's embedded product state are treated as blocks and retried rather than emitted as empty results, not-found products are logged by ID, and detail-fetch failures fall back to listing data instead of inventing fields.

### Legal compliance

Intended for public product data, without customer-account access. Architekture is independent of Tesco; branding identifies the target retailer and does not imply endorsement. Users are responsible for their use of output, applicable site terms and GDPR/CCPA obligations where relevant. Exclude personal data and credentials from feedback.

### Changelog

- **2026-10-15** — Live validation recorded basic search, category, direct-ID and preview output. Corrected Clubcard price extraction after detecting regular-price values in promotion price objects; ambiguous offer amounts remain null. Prefer the session profile verified in live runs, try distinct alternatives on blocks, and retry unavailable listings before failing. The final continuation check returned four new detail rows in nine seconds (run `xeJFbeyOAatJzLQdU`, build 1.0.8); 48 offline regression checks pass.

- **2026-10-15** — Added twenty lifetime previews, durable quota reservations, continuation and compatibility checks. Regression tests cover quota limits, failed responses, billing limits and continuation.

- **2026-10-15** — Added listing validation, repeated-page detection, UK input checks, integrated billing and bounded form prefill.

- **2026-10-14** — Added basic fallback billing, partial-result reporting, empty-response retries and feedback logging.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords to search, e.g. "milk", "hobnobs". One result set per term.

## `categoryUrls` (type: `array`):

Tesco UK shop URLs or paths, e.g. /groceries/en-GB/shop/bakery/all. Unsupported URLs fail before collection..

## `productIds` (type: `array`):

Numeric Tesco product IDs or Tesco UK product URLs. Requests product-page details; optional values may be absent. Invalid inputs fail before collection.

## `maxItemsPerQuery` (type: `integer`):

Cap results per term or category. 0 = no cap.

## `includeDetails` (type: `boolean`):

Requests product-page enrichment in the same 27-field layout. Successful detail rows use product-detail only; unavailable enrichment retains usable basic rows at product pricing. Optional values may remain empty.

## `sortBy` (type: `string`):

Tesco sort order for search and category listings.

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

Direct connection is the validated default. Optional proxy settings are available if your access conditions require them.

## `feedback` (type: `string`):

Tell the maintainers what you needed and did not get. Logged for human review; does not change the output. Do not include credentials or private personal data.

## `preview` (type: `boolean`):

20 lifetime attempts per authenticated account. One search term, one page, up to 10 basic rows. Failed attempts count. No automatic paid fallback.

## `resumeState` (type: `object`):

Paste RESUME_STATE from a prior paid run. Keep queries, order, details and sort unchanged; the row cap may change. Re-scans listings and skips delivered IDs. Omit for fresh price monitoring.

## Actor input object example

```json
{
  "searchTerms": [
    "milk"
  ],
  "maxItemsPerQuery": 10,
  "includeDetails": false,
  "sortBy": "relevance",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "preview": false
}
```

# Actor output Schema

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

Scraped items in the default dataset (one JSON object per row).

## `run_summary` (type: `string`):

No description

## `resume_state` (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 = {
    "searchTerms": [
        "milk"
    ],
    "maxItemsPerQuery": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("architekture/tesco-uk-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 = {
    "searchTerms": ["milk"],
    "maxItemsPerQuery": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("architekture/tesco-uk-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 '{
  "searchTerms": [
    "milk"
  ],
  "maxItemsPerQuery": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call architekture/tesco-uk-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,architekture/tesco-uk-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/KgfjS0Wkc7ffg3Uxi/builds/MBXNaQfuki5vkFrnb/openapi.json
