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

Scrape products, prices, price per unit, offers and stock status from ocado.com, the UK's largest online-only supermarket. Search by keyword or browse a category. No login or cookies required.

- **URL**: https://apify.com/studio-amba/ocado-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 15 total users, 6 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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/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

## Ocado Scraper — UK Groceries Products & Prices

Scrape the product catalog of [Ocado](https://www.ocado.com), the UK's largest online-only supermarket. Get product names, prices, price per unit, offers, ratings, images and stock status as clean structured JSON. No login, no cookies, no browser automation.

Search by keyword or browse a category. Built for price monitoring, promotion tracking, assortment analysis and market research across the UK grocery sector.

### What it does

This actor reads Ocado's own product API directly (the same JSON backend the ocado.com website calls), so you get accurate live prices and offers without rendering pages in a browser. It's fast, stable and cheap to run.

- **Keyword search** — pass a term (`milk`, `chicken`, `pasta`) and get matching products.
- **Category browse** — pass a full Ocado category URL to pull a specific department or shelf.
- **Offers** — current promotion text (e.g. "Everyday Savers") is captured alongside the regular price.
- **Smart Pass prices** — the member price Ocado shows for Smart Pass offers is returned in `memberPrice`, with the offer text in `memberPromotion`.
- **Price per unit** — Ocado's per-litre/per-kg unit price, converted to a readable `£X.XX/unit` string.
- **Ratings** — average star rating and review count, where Ocado shows them.

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `searchQuery` | String | No | Search Ocado by keyword (e.g. `milk`). Default: `milk`. |
| `categoryUrl` | String | No | A full Ocado category URL (copy from the address bar after clicking a category on ocado.com). When set, `searchQuery` is ignored. |
| `maxProducts` | Integer | No | Maximum number of products to return. Default: `20`. |
| `proxyConfiguration` | Object | Yes (prefilled) | Apify Proxy settings. Must be **Residential + GB** — see Limitations. |

### Output

Each product is one record:

```json
{
  "name": "Ocado British Whole Milk 4 Pints",
  "brand": "Ocado",
  "price": 1.65,
  "currency": "GBP",
  "pricePerUnit": "£0.73/litre",
  "discount": "Everyday Savers",
  "memberPrice": null,
  "memberPromotion": null,
  "packSize": "2.272L",
  "productId": "78920011",
  "sku": "7af52d5a-b86e-47a5-9047-aa1d222b365f",
  "inStock": true,
  "rating": 3.9,
  "reviewCount": 180,
  "imageUrl": "https://www.ocado.com/images-v3/.../300x300.jpg",
  "category": "Fresh & Chilled Food > Dairy & Eggs > Milk > Fresh Milk > Whole",
  "url": "https://www.ocado.com/products/ocado-british-whole-milk-4-pints/78920011",
  "scrapedAt": "2026-07-14T19:37:31.515Z"
}
```

#### Fields

| Field | Description |
|-------|-------------|
| `name` | Product name |
| `brand` | Brand name |
| `price` | Current shelf price in GBP |
| `currency` | Always `GBP` |
| `pricePerUnit` | Price per unit (e.g. `£0.73/litre`) |
| `originalPrice` | Regular (was) price when the product is on a price cut such as "Now £4, was £5.45". `price` is then the price you pay today |
| `discount` | Promotion text, e.g. `Everyday Savers` |
| `memberPrice` | Price for one item for Ocado Smart Pass members when a Smart Pass offer applies, otherwise `null`. |
| `memberPromotion` | Smart Pass offer text, e.g. `Save 25% with Smart Pass`, otherwise `null`. |
| `packSize` | Pack size description, e.g. `2.272L`, `400g` |
| `ean` | Barcode/GTIN. Always empty — see Limitations. |
| `productId` | Ocado's retailer product ID (also the trailing segment of the product URL) |
| `sku` | Ocado's internal product UUID |
| `inStock` | Whether the product is available for sale |
| `rating` | Average review rating (0–5) |
| `reviewCount` | Number of reviews |
| `imageUrl` | Primary product image URL |
| `category` | Full category breadcrumb path |
| `url` | Link to the product page |
| `scrapedAt` | ISO timestamp of the scrape |

### How to scrape Ocado data

1. Open the actor and set your **Search Query** (for example `milk`) — or leave it empty and paste a **Category URL** to browse a department instead.
2. Set **Max Products** to the number of products you want.
3. Keep the default **Proxy Configuration** (Residential, GB) — this is required, not optional (see Limitations).
4. Click **Start**. When the run finishes, download the dataset as JSON, CSV or Excel, or pull it from the API.

#### Input example

```json
{
  "searchQuery": "chicken",
  "maxProducts": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "GB"
  }
}
```

#### Browsing a category

```json
{
  "categoryUrl": "https://www.ocado.com/categories/fresh-chilled-food/dairy-eggs/milk/fresh-milk/whole/56428ac7-7c31-4ec5-b8f8-f2a2fed6246d",
  "maxProducts": 50
}
```

### Common use cases

- **Price monitoring** — track Ocado prices over time and compare against Tesco, Sainsbury's, Asda, Morrisons, Waitrose and Aldi.
- **Promotion tracking** — capture current offers and promotion mechanics.
- **Assortment analysis** — see which products and brands a category carries.
- **Market research** — build a dataset of UK grocery prices for analysis.

### Tips

- One search request returns up to 300 products (Ocado's own server-side limit — verified live, requests above that are rejected). Larger `maxProducts` values page through the API automatically.
- Category browsing pulls Ocado's sitewide merchandising feed and filters it down to your category client-side, so it scans more pages than a plain search for the same yield — use search where you can.
- Prices change frequently; run on a schedule to keep your data fresh.

### Cost estimate

This actor makes roughly one HTTP request per 300 products (search mode) via Apify's Residential proxy, so the compute cost per result is low — comparable to other JSON-API-based grocery scrapers in this catalog (Albert Heijn, Sainsbury's). Category-mode runs cost more per result because of the client-side filtering pass described above.

### Limitations

- **No EAN/barcode.** Ocado does not expose GTIN/EAN anywhere accessible: not in the listing API, not in the product page's structured data (schema.org JSON-LD), and not in the product page's full internal state (description, nutrition table, storage info). This was verified directly against live responses, not assumed. The `ean` field is kept in the output for schema parity with sibling grocery scrapers but is always empty.
- **Residential GB proxy is required, not optional.** Ocado's product API sits behind AWS WAF and geo-gates non-UK IPs (confirmed live: a non-UK IP gets an HTTP 202 bot challenge on the same endpoint that returns a clean 200 through a UK residential IP). Datacenter and non-GB proxies will fail or get throttled.
- **Category browsing is best-effort.** Ocado's category endpoint returns a general merchandising feed (featured + on-offer items across the whole site), not a strict filtered grid, so this actor filters client-side by matching each product's own category breadcrumb. This is accurate but scans more raw product rows than a plain keyword search for the same number of matches.
- Data is scraped from the public website and may change without notice.
- Respect Ocado's terms of service and use responsibly.

### FAQ

**Does this need an Ocado account or delivery postcode?**
No. The actor only reads public catalog data. No account, password, cookies, or delivery address is required.

**Can I get the barcode (EAN)?**
No — Ocado does not publish it anywhere on the site or its API, for any product. This is a site limitation, not a missing feature.

**Why do I need a Residential + GB proxy specifically?**
Ocado's product API blocks or challenges non-UK traffic. A UK residential IP is the only proxy type confirmed to get clean, un-challenged responses.

**Is this legal?**
The actor collects publicly available product information. You are responsible for using the data in line with Ocado's terms and applicable law.

### Disclaimer

This actor is an independent tool and is not affiliated with, endorsed by, or connected to Ocado Group plc or Ocado Retail Ltd. All product data belongs to its respective owners.

# Actor input Schema

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

Search Ocado by keyword (e.g., 'milk', 'chicken', 'pasta'). Leave empty and set a Category URL to browse a category instead.

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

A full Ocado category URL to browse instead of searching, e.g. https://www.ocado.com/categories/fresh-chilled-food/dairy-eggs/milk/fresh-milk/whole/56428ac7-7c31-4ec5-b8f8-f2a2fed6246d (copy from the address bar after clicking a category on ocado.com). When set, Search Query is ignored.

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

Maximum number of products to return.

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

Proxy settings. Ocado geo-restricts most of its product API to UK IPs, so Residential proxies scoped to GB are required — datacenter proxies and other countries get blocked or return incomplete data.

## Actor input object example

```json
{
  "searchQuery": "milk",
  "maxProducts": 20,
  "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/ocado-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/ocado-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/ocado-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/ocado-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/lm3yTEITLiQYkvXJ8/builds/kPifMFNgU9USfIh5E/openapi.json
