Tesco UK Scraper — Prices, Clubcard, Unit Prices, Barcodes
Pricing
from $2.00 / 1,000 products
Tesco UK Scraper — Prices, Clubcard, Unit Prices, Barcodes
Tesco UK (tesco.com) scraper: product ID, TPNB, TPNC, GTIN barcode, title, brand, price, unit price, Clubcard price, promotions, offer end date, availability, department, aisle, shelf, ratings, image, URL. Keyword search, category URL or product ID. Pay per result, no login.
Pricing
from $2.00 / 1,000 products
Rating
0.0
(0)
Developer
architek.earth
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
16 days ago
Last modified
Categories
Security
Limited permissionsShare
Recorded run: 5 products from 1 search term in 7 seconds (2026-10-22, build 1.0.13, run ajikEqnMYKbE41VLL). A bounded canary sample, not a full-catalogue benchmark.
architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.
Tesco UK product prices, Clubcard prices, unit prices, GTIN barcodes, promotions, ratings and aisle placement from tesco.com, by keyword, category page or product ID.
| Source | Market | From | Standout fields |
|---|---|---|---|
| Tesco (tesco.com) | United Kingdom | $2.00 / 1,000 products | price / clubcardPrice, unitPrice, gtin, promotions |
✅ Why choose this scraper
Tesco shelf prices and Clubcard prices in one row
price, clubcardPrice, promotionDescription, promotionEndDate and the full promotions list (with CLUBCARD_PRICING attributes) come back together, so shelf and loyalty prices stay separate.
Tesco unit prices for pack-size comparison
unitPrice and unitOfMeasure (for example 3.5 per litre) are on every row, basic or detail.
Tesco barcodes and product IDs
gtin is kept as a string with its leading zeros ("05060337221085"), next to Tesco's id, tpnb and tpnc; the canary's listing rows already carried it.
Tesco aisle, shelf and ratings
superDepartmentName, departmentName, aisleName and shelfName place each product in the Tesco hierarchy; reviews.stats carries noOfReviews and overallRating.
Measured on a schedule
A canary task runs a fixed milk search on the live site and records rows, inputs and seconds; the hero line above is its latest green run.
⚠️ Worth knowing up front
- UK only. Non-Tesco hosts and anything other than tesco.com UK shop or product paths are rejected before collection.
- There is no store or postcode selector. Prices and availability are the public online catalogue, not a specific shop or delivery address.
isForSaleis a source flag, not a stock count. - This actor does not return ingredients, allergens, nutrition tables or stock quantities. Detail mode fills the same 27 product fields as basic mode; it does not add fields.
clubcardPriceis set only when an offer markedCLUBCARD_PRICINGhas an unambiguous single-item text such as"£3.00 Clubcard Price". Multi-buys and offers without text stay null; null is not zero. In the 2026-10-22 canary, listing rows carried the Clubcard attribute without offer text, so readpromotionstoo.- Products are de-duplicated by
idacross inputs: a product found by an earlier query is not emitted again. RUN_SUMMARY.statussays how a run ended:COMPLETE,LIMITED(row cap hit),PARTIAL(some details unavailable or blocked after rows were delivered),BLOCKED,BUDGET_LIMIT,TIME_LIMITorPREVIEW_LIMIT. A run stopped by time or budget keeps what it delivered and savesRESUME_STATE.- Tesco's Akamai edge occasionally answers HTTP 403. The occasional Akamai 403 (or 429, or a challenge page) is retried on a fresh session: a new proxy session and a new client with no cookies, up to two times per page, before the run reports
BLOCKED. - Invalid input, a block after retries and the run deadline end with a free status row (
"recordType": "status") that says what happened. A search with no matches ends with no rows; checkRUN_SUMMARY.
⚡ Try the example
Each Apify account gets 20 lifetime free preview attempts on this actor: one search term, one page, up to 10 basic rows, no product charges. Failed attempts count. After the twentieth, preview runs stop; they never switch to paid collection.
Open the actor's input page, switch to JSON, paste this and start the run:
{"preview": true,"searchTerms": ["milk"],"maxItemsPerQuery": 10,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "GB"}}
For paid collection set "preview": false; add "includeDetails": true to fetch each product page.
⚙️ Input
| Parameter | Type | Default | Description |
|---|---|---|---|
preview | boolean | false | Use one of 20 lifetime free attempts: one search term, one page, at most 10 basic rows. No categories, product IDs, details or resumeState. |
searchTerms | array | — (prefill: milk) | Keywords such as milk. One collection per term; duplicates removed. |
categoryUrls | array | — | Tesco UK shop URLs or paths such as /groceries/en-GB/shop/bakery/all. Other hosts and paths are rejected before collection. |
productIds | array | — | Numeric Tesco product IDs or tesco.com product URLs. Always returns detail rows; IDs that cannot be fetched are skipped and not charged. |
maxItemsPerQuery | integer | 100 (prefill: 10) | New unique rows per search or category. 0 removes the cap in paid mode; preview stays at 10. |
includeDetails | boolean | false | Fetch each product page for search and category rows (charged as product-detail). If a page is unavailable the basic row is kept with detailStatus: "unavailable" at product price. |
sortBy | select | relevance | relevance, price-ascending or price-descending. |
resumeState | object | — | The RESUME_STATE record from a previous paid run. Queries, order, details and sort must match; the row cap may change. Delivered IDs are skipped and not charged. |
proxyConfiguration | proxy | — (prefill: no proxy) | Proxy settings. The recorded 2026-10-22 canary used Apify residential proxy, country GB. |
feedback | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |
🧱 Stable fields
These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.
| Field | Type | Meaning |
|---|---|---|
id | string | Tesco product ID; the de-duplication and join key within Tesco. |
tpnb / tpnc | string, nullable | Tesco product numbers. |
gtin | string, nullable | Barcode as published, with leading zeros. |
title / brandName | string, nullable | Product title and brand. |
price | number, nullable | Current shelf price in GBP. |
unitPrice / unitOfMeasure | number / string, nullable | Unit price in GBP and its basis, such as litre or kg. |
clubcardPrice | number, nullable | Single-item Clubcard price in GBP parsed from the offer text; null when absent or ambiguous. |
promotionDescription / promotionEndDate | string, nullable | Text and end date of the lead offer (Clubcard first). |
promotions | array | Every offer: id, description, startDate, endDate, unitSellingInfo, beforeDiscount, afterDiscount, attributes. |
isForSale / isNew / status | boolean / boolean / string, nullable | Source availability flag, new-product flag and status such as AvailableForSale. |
averageWeight / productType | number / string, nullable | Source weight and product type such as SingleProduct. |
superDepartmentName / departmentName / aisleName / shelfName | string, nullable | Tesco category hierarchy. |
restrictions / unavailabilityReasons | array, nullable | Source restriction and unavailability information. |
reviews | object, nullable | Source review statistics, such as stats.noOfReviews and stats.overallRating. |
defaultImageUrl / url | string, nullable | Product image and tesco.com product page. |
source | string | search, category or id. |
searchTerm / categoryUrl | string | The query that produced the row; present only for that source. |
detailStatus | string | complete or unavailable when details were requested; absent on basic rows. |
recordType | string | "status" on free status rows; absent on product rows. |
Nested keys inside promotions, restrictions and reviews pass through from the source. Other fields are extras, not part of the contract. To have one pinned, say so in feedback.
📦 Output example
Abridged real row from run ajikEqnMYKbE41VLL on 2026-10-22; values are a historical snapshot.
{"id": "305965589","tpnb": "87867120","tpnc": "305965589","gtin": "05060337221085","title": "Biotiful Gut Health Kefir Milk Drink Original 1 Litre","brandName": "Biotiful","price": 3.5,"unitPrice": 3.5,"unitOfMeasure": "litre","clubcardPrice": null,"promotions": [{"id": "103345943","description": null,"attributes": ["CLUBCARD_PRICING"]}],"isForSale": true,"status": "AvailableForSale","departmentName": "Fresh Food","aisleName": "Milk, Butter & Eggs","shelfName": "Milk","reviews": {"stats": {"noOfReviews": 63, "overallRating": 4.7, "overallRatingRange": 5}},"url": "https://www.tesco.com/groceries/en-GB/products/305965589","source": "search","searchTerm": "milk"}
A free status row looks like this:
{"recordType": "status","status": "INVALID_INPUT","message": "Provide at least one of searchTerms, categoryUrls or productIds."}
🧠 Advanced usage
Track Tesco Clubcard prices daily
Run the same keywords or categories every day without resumeState and diff price and clubcardPrice by id. When clubcardPrice is null but promotions[].attributes contains CLUBCARD_PRICING, rerun those IDs through productIds to read the offer text.
Compare Tesco with Sainsbury's by barcode
Join non-empty gtin values with the Sainsbury's Scraper, then check pack size and unitOfMeasure before treating a match as the same product.
Get Tesco prices cheapest first
Set sortBy: "price-ascending" on a search or category with a small maxItemsPerQuery to collect the lowest shelf prices only.
Scrape a whole Tesco category in several runs
Set maxItemsPerQuery: 0, and when a run stops on time or budget copy its RESUME_STATE into resumeState with the same inputs. Listings are re-scanned from page one and delivered IDs are skipped and not charged again; combine datasets by id. Check RUN_SUMMARY.status is COMPLETE before treating the list as finished.
🔌 Integrations
Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. Daily runs on fixed keywords or categories suit shelf-price and Clubcard tracking. Copy-paste code in Python, Node, curl and n8n: architek-earth/examples/tesco-uk-scraper.
💸 Pricing
Pay per result. Platform and proxy usage are included. Status rows and failed requests are free. Products already saved in RESUME_STATE are skipped and not charged again; product IDs are saved before each delivery, so a resumed run never charges a product twice (a hard stop between that save and the delivery can skip a small overlap instead).
| Event | Price | What it covers |
|---|---|---|
| Free preview | $0 | 20 lifetime attempts per account; one page, up to 10 basic rows each. |
product | $2.00 / 1,000 products | One delivered search or category row, including a row kept at basic price when its detail page was unavailable. |
product-detail | $5.00 / 1,000 products | One delivered row read from the product page (includeDetails or productIds). |
1,000 basic rows cost $2.00; 1,000 detail rows cost $5.00. A detail row is charged once, as product-detail only.
❓ FAQ
Is there a Tesco API for product prices? This actor is one: it returns Tesco UK catalogue data as JSON through the Apify API, callable from any language or from n8n, Make and Zapier.
Do I need a Tesco or Clubcard account? No. No account, cookies or API key from Tesco.
Can I get Tesco Clubcard prices? Yes. clubcardPrice holds the single-item Clubcard price when the offer text states one; promotions lists every offer with its dates and attributes. Multi-buy offers are left in promotions and not converted to a unit price.
Can I get Tesco barcodes (GTIN/EAN)? Yes. gtin is returned on listing and detail rows when Tesco publishes one, as a string with leading zeros.
How do I scrape Tesco prices by postcode or store? You can't with this actor. It has no store or postcode selector and returns the public online catalogue.
Can I get Tesco nutrition, ingredients or allergens? No. Those are not in this actor's output.
🤖 For AI agents & LLM apps
Paste this into your AI assistant:
architekture/tesco-uk-scraper on Apify. Python: from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/tesco-uk-scraper").call(run_input={"searchTerms": ["milk"], "maxItemsPerQuery": 10, "proxyConfiguration": {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "GB"}}); items = client.dataset(run["defaultDatasetId"]).list_items().items (page with offset/limit for large exports). Rows with recordType == "status" are free and explain invalid input or a stopped run. Full input schema and README: GET https://api.apify.com/v2/acts/architekture~tesco-uk-scraper/builds/default. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (mcp.apify.com). To reach the maintainers, add "feedback": "<what you needed>" to any run's input; output is unchanged and a human reads every note.
🧰 More from architek.earth
Grocery and retail prices
- Coles Scraper — prices, unit prices, specials, barcodes
- Woolworths AU Scraper — prices, specials, barcodes, nutrition
- ALDI AU Scraper — prices, unit prices, Special Buys
- Woolworths NZ Scraper — prices, club prices, specials
- New World NZ Scraper — store prices, Clubcard offers, promotions
- Pak'nSave Scraper — store prices, unit prices, promotions
- Tesco UK Scraper — you are here
- Sainsbury's Scraper — prices, unit prices, offers, barcodes
- ALDI Ireland Scraper — prices, unit prices, Specialbuys
- Dunnes Stores Scraper — fashion and home prices, sizes, EANs
- Mercadona Scraper — prices, unit prices, categories
Full catalogue: github.com/architek-earth.
architek.earth · code like concrete
🙋 Support & legal
Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.
This actor collects only publicly available data from tesco.com, the same pages any visitor sees without logging in. It does not bypass authentication. It is not affiliated with or endorsed by Tesco. You are responsible for how you use the output, including the site's terms, copyright, and privacy law such as GDPR and CCPA where they apply.
📝 Changelog
- 2026-10-24 — Studio core 1.0.5: a 403 or 429 from Tesco, or a challenge page, is now retried on a new proxy session with a fresh client (new connection, no cookies), up to two times per page, before the run reports
BLOCKED. Before this change the retry reused the same proxy session. A proxy tunnel (CONNECT) failure also moves to a fresh proxy session. Output fields and prices are unchanged. - 2026-10-22 — Studio core 1.0.1: a status note now sits inside the parentheses,
Done: N rows, M details, K status rows (STATUS; note). If hosted pricing ever differs from the listed prices the run ends as failed (PRICING_MISMATCH) before any request, so nothing is collected or charged and we are alerted. - 2026-10-22 — Runs on the studio core (architek_core 1.0.0): the run refuses to start unless hosted pricing is exactly
productandproduct-detailat the listed prices (nothing collected or charged); a challenge or captcha page served as HTTP 200 now stops the run asBLOCKEDat once; a last row that spends your budget exactly no longer ends the run asBUDGET_LIMIT. - 2026-10-22 — Status message now reads
Done: N rows, M details, K status rows (STATUS). - 2026-10-22 — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
- 2026-10-21 — Invalid input, blocks and the run deadline now stop cleanly with a free status row instead of a failed run.
- 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.