# Waitrose Scraper: Products, Prices, Offers & Barcodes (`zaiq/waitrose-scraper`) Actor

Scrape Waitrose (waitrose.com) products and prices: search, categories, offers and product URLs. Gets price, was price, unit price, offers with end dates, myWaitrose prices, barcodes (GTIN), ratings and stock. UK home connections. Failed lookups are free.

- **URL**: https://apify.com/zaiq/waitrose-scraper.md
- **Developed by:** [Zaiq](https://apify.com/zaiq) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.20 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Waitrose Scraper: Products, Prices, Offers & Barcodes

Get products and prices from Waitrose (waitrose.com) as structured data: price, was price, unit price, every offer
with its end date, myWaitrose member prices, barcodes (GTIN), size, stock, ratings, category and image. Search
waitrose.com, read whole categories or offer groups, or look up products by URL or line number.

The Actor reads the same product data the Waitrose website loads for any visitor, through home (residential)
internet connections in Great Britain. It does not log in and needs no account.

### What it does

- **Search** waitrose.com for any words and return the products in the order Waitrose shows them.
- **Categories**: give a category page (or its ID) and every product in it is read, page by page, 96 at a time.
- **Offers**: every grocery product on offer (about 5,000), or one offer group: Half price, Multi-buy, Percentage
  off, Save a third, myWaitrose member offers. Offer pages such as Bakery offers work as category URLs too.
- **Product lookups**: product page URLs or Waitrose line numbers, 60 per request.
- **One clean row per product**: a product found by two searches is written and charged once.
- **Barcodes**: every barcode Waitrose lists for the product, plus the main EAN-13 picked out as `gtin`.
- **Offers with dates**: the offer text, its type (price cut, multi-buy, new lower price, introductory, member
  price), the end date, the offer price and how many you must buy.
- **Unit prices** normalised to a number and a unit (for example 1.11 per l), and pack sizes normalised to grams,
  millilitres or items.
- **Stock**: products Waitrose marks as sold out online come back as `out_of_stock` with Waitrose's wording.
- **Run summary** in the key-value store (`RUN-SUMMARY`): what each input found, products not found, why the run
  stopped, the UK connections used and the traffic.

### Who uses it

- **Price comparison and grocery apps** that need Waitrose prices, unit prices and offers every day.
- **Brands and suppliers** tracking their products' shelf price, promotions and end dates at Waitrose.
- **Analysts** measuring how many products are on offer, how deep the cuts are and how long they last.
- **Product data teams** matching Waitrose products to other retailers by barcode.

### Example

Runs on 4 October 2026, through UK home connections:

| Input | Result |
|---|---|
| Every grocery product on offer | 5,070 products in 80 seconds (53 requests). Every row has an offer end date: 3,356 price cuts, 1,261 multi-buys, 374 new lower prices, 79 introductory offers. 123 were sold out online. |
| Three searches (chocolate, milk, free range eggs), maximum 450 | 450 products in 10 seconds. 93 products found by two searches were written once. Barcode, unit price and rating on all 450. |
| Two category pages (Milk; Bakery offers) | 279 products in 7 seconds: all 177 milk products and all 102 bakery offers. |
| Half price offer group | 141 products, all with barcodes. |
| Six product references (URLs, line numbers, a made-up line number, two non-product entries) | 3 products; the unknown line number is listed in the run summary as not found; the two non-product entries are skipped with a warning. All free except the 3 products. |

### Input

```json
{
  "searchTerms": ["essential semi skimmed milk"],
  "categoryUrls": ["https://www.waitrose.com/ecom/shop/browse/groceries/fresh_and_chilled/milk_butter_and_eggs/milk"],
  "productUrls": ["053457"],
  "offers": "none",
  "offersOnly": false,
  "maxItems": 100
}
```

| Field | What it does |
|---|---|
| `searchTerms` | Words to search for on waitrose.com. |
| `categoryUrls` | Category pages (`/ecom/shop/browse/groceries/...`), offer pages (`/ecom/shop/browse/offers/...?categoryId=...`) or category IDs such as `301241`. |
| `productUrls` | Product page URLs or line numbers such as `053457`. Up to 5,000. |
| `offers` | `none` (default), `all` (every grocery product on offer), `half-price`, `multi-buy`, `percentage-off`, `save-third` or `my-waitrose`. |
| `offersOnly` | Keep only products with an offer, a lower price than before or a member price. The other products are not charged. |
| `maxItems` | Stop after this many products (default 100; 0 means no limit). |
| `proxyConfiguration` | UK home connections (Apify Proxy, RESIDENTIAL, GB) by default. |
| `maxConcurrency` | Inputs worked on at once (default 4). |

### Output

One row per product. A row from the search above:

```json
{
  "retailer": "Waitrose",
  "productId": "053448-26749-26750",
  "sku": "053448",
  "gtin": "5000295012684",
  "name": "Essential British Free Range Semi-Skimmed Milk 2 Pints",
  "brand": "Waitrose Ltd",
  "size": "1.136litre",
  "quantity": 1136.0,
  "quantityUnit": "ml",
  "packCount": null,
  "price": 1.25,
  "wasPrice": 1.3,
  "currency": "GBP",
  "unitPrice": 1.11,
  "unitPriceUnit": "l",
  "memberPrice": null,
  "memberPriceProgram": null,
  "memberPriceValidFrom": null,
  "memberPriceValidTo": null,
  "onOffer": true,
  "promotionText": "New Lower Price",
  "promotionType": "new-lower-price",
  "offerStartDate": null,
  "offerEndDate": "2026-12-08",
  "promotions": [
    {"text": "New Lower Price", "type": "new-lower-price", "id": "554887", "validFrom": null,
     "validTo": "2026-12-08", "memberOnly": false, "requiredQuantity": null, "price": 1.25}
  ],
  "available": true,
  "availability": "in_stock",
  "availabilityText": null,
  "category": "Groceries > Fresh & Chilled > Milk, Butter & Eggs > Milk > Semi-Skimmed Milk > 2 Pints",
  "categoryPath": ["Groceries", "Fresh & Chilled", "Milk, Butter & Eggs", "Milk", "Semi-Skimmed Milk", "2 Pints"],
  "imageUrl": "https://ecom-su-static-prod.wtrecom.com/images/products/4/LN_053448_BP_4.jpg",
  "productUrl": "https://www.waitrose.com/ecom/products/essential-british-free-range-semi-skimmed-milk-2-pints/053448-26749-26750",
  "rating": 4.42,
  "reviewCount": 616,
  "sponsored": false,
  "gtins": ["5000295012684", "0000001015317", "5000373711102", "5010989781123", "5000373713762", "5000169015315", "01015317"],
  "description": "Fresh pasteurised homogenised free range semi-skimmed milk.",
  "sourceType": "search",
  "source": "essential semi skimmed milk",
  "position": 3,
  "scrapedAt": "2026-10-04T16:08:03Z",
  "charged": true
}
```

- `price` is what a shopper pays today without a loyalty card; `wasPrice` is the earlier price when Waitrose shows
  one. `memberPrice` is the myWaitrose price with its end date (myWaitrose offers are rare: one on 4 October 2026).
- `quantity` and `quantityUnit` describe the whole pack in grams, millilitres or items; `unitPriceUnit` is `kg`,
  `l`, `100g`, `each` and so on.
- `availability` is `in_stock` or `out_of_stock` (Waitrose's "sold out online").
- `position` is the product's place in the search, category or offer list it was found in.
- The same fields are used by our other UK grocery scrapers (Ocado, ALDI UK, Lidl GB), so their data can be joined.

### Use it from code, automations and AI agents

- **API**: run it and get the results in one call: `POST https://api.apify.com/v2/acts/zaiq~waitrose-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input as JSON. The API tab on this page has ready-made Python, JavaScript and cURL examples.
- **Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("zaiq/waitrose-scraper").call(run_input={'searchTerms': ['milk'], 'maxItems': 20})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

- **No code**: connect it to Google Sheets, Zapier, Make or n8n from the Integrations tab, schedule runs in Apify Console, and get a webhook when a run finishes.
- **AI agents (MCP)**: add it as a tool to Claude, Cursor or any MCP client through Apify's MCP server: `https://mcp.apify.com?tools=zaiq/waitrose-scraper`.

### Pricing

**$4.00 per 1,000 products** (event `product-scraped`, $0.004 per product row). Apify platform usage is included.

You pay only for product rows with a price. Searches that find nothing, failed or blocked requests, products not
found, products found again by a second input and products without a price cost nothing. Examples: the 20-product
test input costs $0.08; all 5,070 grocery offers cost $20.28. Set a maximum cost per run in Apify and the Actor
stops cleanly when it is reached.

### Limits

- Prices are Waitrose's online prices for a shopper without a delivery address. Stock is the online "sold out"
  flag, not shelf stock in a branch.
- Waitrose shows myWaitrose member prices only on a few products at a time; when there are none, `memberPrice` is
  empty.
- The Actor reads what the website shows; if Waitrose changes its site, the Actor needs an update (the run summary
  shows which inputs failed, and failed inputs are free).

### FAQ

**Does it need a Waitrose account?** No. It reads public product data without logging in.

**Why UK home connections?** They are what the website expects from shoppers. Each connection is checked to be in
Great Britain by two IP-location services before it is used.

**How do I track prices every day?** Save your input as a task and schedule it in Apify. Join rows on `productId` or
`gtin` to see price and offer changes.

**Is it legal?** The Actor reads data the website shows to any visitor. Check Waitrose's terms and the law that
applies to your use before you use the data.

This Actor is not affiliated with Waitrose & Partners.

# Actor input Schema

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

Words to search for on waitrose.com, for example milk or free range eggs. Products come in the order Waitrose shows them.

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

Waitrose category pages such as https://www.waitrose.com/ecom/shop/browse/groceries/fresh_and_chilled/milk_butter_and_eggs/milk, offer pages such as https://www.waitrose.com/ecom/shop/browse/offers/fresh_and_chilled_offers?categoryId=301134, or category IDs such as 301241. Every product in the category is read, page by page.

## `productUrls` (type: `array`):

Product pages such as https://www.waitrose.com/ecom/products/essential-british-free-range-semi-skimmed-milk-4-pints/053457-26759-26760, or Waitrose line numbers such as 053457. Up to 5,000. Products that no longer exist are listed in the run summary, free.

## `offers` (type: `string`):

Read one of the groups on the Waitrose Offers page as well as (or instead of) the searches and categories above.

## `maxItems` (type: `integer`):

The run stops after writing this many products. 0 means no limit. Each product is written and charged once, even when several inputs find it.

## `offersOnly` (type: `boolean`):

Keep only products with a promotion, a lower price than before, or a member price. Products left out are not charged.

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

UK home (residential) connections work best; Apify Proxy, group RESIDENTIAL, country GB is the default. Every connection is checked to be in Great Britain before use.

## `maxConcurrency` (type: `integer`):

How many inputs are worked on at the same time. The site is never sent more than this many requests at once.

## Actor input object example

```json
{
  "searchTerms": [
    "milk"
  ],
  "offers": "none",
  "maxItems": 20,
  "offersOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  },
  "maxConcurrency": 4
}
```

# Actor output Schema

## `products` (type: `string`):

Dataset with one row per product: prices, offers, unit price, availability, category, image and page.

## `summary` (type: `string`):

What each input found, products not found, why the run stopped, the UK connections used and the traffic.

# 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"
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "GB"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("zaiq/waitrose-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"],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("zaiq/waitrose-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"
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "GB"
  }
}' |
apify call zaiq/waitrose-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zaiq/waitrose-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/aAQuadV5UPyuQSESu/builds/VjPxvWvxE9FL1XkuZ/openapi.json
