# 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
- **Stats:** 2 total users, 1 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## 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.
- **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",
  "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` | Strike-through price when discounted (rarely populated by Ocado) |
| `discount` | Promotion text, e.g. `Everyday Savers` |
| `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"
  }
}
```

# 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("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=studio-amba/ocado-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Ocado Scraper — UK Groceries Products & Prices",
        "description": "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.",
        "version": "0.1",
        "x-build-id": "3G0thTrNEYEvhvwof"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/studio-amba~ocado-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-studio-amba-ocado-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/studio-amba~ocado-scraper/runs": {
            "post": {
                "operationId": "runs-sync-studio-amba-ocado-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/studio-amba~ocado-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-studio-amba-ocado-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "searchQuery": {
                        "title": "Search Query",
                        "type": "string",
                        "description": "Search Ocado by keyword (e.g., 'milk', 'chicken', 'pasta'). Leave empty and set a Category URL to browse a category instead."
                    },
                    "categoryUrl": {
                        "title": "Category URL",
                        "type": "string",
                        "description": "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": {
                        "title": "Max Products",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Maximum number of products to return.",
                        "default": 20
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "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."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
