# Trolley.co.uk Scraper — UK Grocery Price Comparison (`studio-amba/trolley-scraper`) Actor

Scrape trolley.co.uk cross-retailer grocery price comparisons: one product, prices at Asda, Tesco, Sainsbury's, Morrisons, Waitrose, Iceland and more, side by side. No login, no cookies.

- **URL**: https://apify.com/studio-amba/trolley-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 $1.20 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Trolley.co.uk Scraper — UK Grocery Price Comparison

Scrape trolley.co.uk product pages and get one row per product with every UK
retailer's current price for that exact item side by side — Asda, Tesco,
Sainsbury's, Morrisons, Waitrose, Iceland and more, whichever retailers
Trolley has matched to that product. No login, no cookies.

### Why use this actor?

Trolley.co.uk already does the hard part: matching the same grocery product
across UK supermarkets. This actor turns that matching into structured data
— one product, N retailer prices, ready to load into a spreadsheet or
pricing dashboard. Useful for CPG pricing teams checking shelf price
consistency, price-comparison sites that want a ready-made retailer match
instead of building their own, and anyone tracking how a basket of grocery
items moves in price across the big UK chains.

### How to scrape Trolley.co.uk data

1. Enter a **Category** slug from `trolley.co.uk/explore/{category}`, for
   example `milks`, `cheeses`, `yoghurts`, `eggs` or `bries`. The actor
   browses that category's listing pages and follows each product link.
2. Alternatively, paste specific **Product URLs** (advanced input) to
   scrape an exact list of products instead of browsing a category. This
   overrides Category when set.
3. Set **Max Results** to cap how many products the run returns. Category
   pages return 20 products per page, so the actor pages through
   `/explore/{category}` until it has enough or the category runs out.
4. Run the actor. Each product page contributes one dataset row: the
   product's own fields (name, brand, pack size, SKU, rating) plus a
   `retailers` array with one entry per supermarket that carries it.
5. Read `lowestPrice` for the cheapest price found across all retailers on
   that product, or sort within `retailers` per row to see which chain is
   cheapest for that specific SKU.
6. Use `retailerCount` to filter out weakly-matched products (a Trolley
   product page with only one retailer listed is a thinner comparison than
   one with six).

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `category` | String | No | Trolley.co.uk category slug to browse, e.g. `milks`, `cheeses`, `yoghurts`, `eggs`, `bries`. Defaults to `milks`. Trolley's own `/search/` endpoint is robots-disallowed, so discovery goes through category browsing. |
| `productUrls` | Array | No | Direct trolley.co.uk product page URLs to scrape instead of browsing a category, e.g. `https://www.trolley.co.uk/product/{slug}/{SKU}`. Overrides `category` when set. |
| `maxResults` | Integer | No | Maximum number of products to return (1–500, default 20). |
| `proxyConfiguration` | Object | No | Proxy settings. Trolley.co.uk has no anti-bot protection at any tier, so a proxy isn't required for the actor to work, but the free Apify automatic pool is used by default for reliability at scale. |

#### Example input

```json
{
    "category": "milks",
    "maxResults": 20,
    "proxyConfiguration": { "useApifyProxy": true }
}
```

### Output

Each result is one product with a nested array of per-retailer prices — the
core value of a comparison site: many prices for the same item on one row.

| Field | Type | Example |
|-------|------|---------|
| `productName` | String | `"Cravendale Pure Filtered Fresh Semi Skimmed Milk Fresher for Longer (1 Litre)"` |
| `brand` | String | `"Cravendale"` |
| `packSize` | String | `"1 Litre"` |
| `sku` | String | `"MNJ299"` |
| `url` | String | Full Trolley product page URL |
| `scrapedAt` | String | ISO 8601 timestamp |
| `lowestPrice` | Number | `1.40` |
| `currency` | String | `"GBP"` |
| `rating` | Number | `4.7` |
| `reviewCount` | Number | `720` |
| `imageUrl` | String | Product image URL |
| `category` | String | `"milks"` |
| `retailerCount` | Number | `6` |
| `retailers` | Array | One entry per retailer, see below |

Each entry in `retailers`:

| Field | Type | Description |
|-------|------|-------------|
| `name` | String | Retailer name, e.g. `"Asda"`, `"Tesco"`, `"Waitrose"` |
| `price` | Number | Current price at this retailer, in GBP |
| `originalPrice` | Number | Struck-through "was" price, when the retailer shows a discount |
| `pricePerUnit` | String | Unit price, e.g. `"£0.14 per 100ml"` |
| `promoText` | String | Promotion text, e.g. `"£1.40 CLUBCARD"` |
| `outboundUrl` | String | Outbound redirect link to the retailer's own product page |

### Example output

```json
{
    "productName": "Cravendale Pure Filtered Fresh Semi Skimmed Milk Fresher for Longer (1 Litre)",
    "brand": "Cravendale",
    "packSize": "1 Litre",
    "sku": "MNJ299",
    "url": "https://www.trolley.co.uk/product/cravendale-filtered-fresh-semi-skimmed-milk-fresher-for-longer/MNJ299",
    "scrapedAt": "2026-08-10T19:49:01.798Z",
    "lowestPrice": 1.40,
    "currency": "GBP",
    "rating": 4.7,
    "reviewCount": 720,
    "imageUrl": "https://www.trolley.co.uk/img/product/MNJ299",
    "category": "milks",
    "retailerCount": 6,
    "retailers": [
        {
            "name": "Waitrose",
            "price": 1.40,
            "originalPrice": 2.10,
            "pricePerUnit": "£0.14 per 100ml",
            "promoText": null,
            "outboundUrl": "https://redirect.trolley.co.uk/open_store.php?i=35606712&p=MNJ299&sp=11"
        },
        {
            "name": "Tesco",
            "price": 1.75,
            "originalPrice": null,
            "pricePerUnit": "£0.18 per 100ml",
            "promoText": "£1.40 CLUBCARD",
            "outboundUrl": "https://redirect.trolley.co.uk/open_store.php?i=34250363&p=MNJ299&sp=2"
        }
    ]
}
```

### FAQ

**Does this actor scrape the retailer sites (Tesco, Asda, etc.) directly?**
No. It scrapes trolley.co.uk, which has already matched the same product
across retailers and published each one's current price on one page. That
matching is Trolley's own work — this actor just turns it into structured
data.

**Why does one product only show one retailer?**
Trolley's own retailer coverage varies per product — not every item is
tracked at every chain. `retailerCount` tells you how many retailers were
matched for that specific product, so you can filter out thin comparisons.

**How is discovery done, since `/search/` is disallowed by robots.txt?**
The actor browses `trolley.co.uk/explore/{category}` listing pages instead,
which are not disallowed, and follows each product link found there.

**Can I scrape a fixed list of products instead of browsing a category?**
Yes, use `productUrls` with direct Trolley product page links. This
overrides `category`.

### Cost estimate

This actor uses roughly **1-2 requests per returned product** (one category
listing page shared across ~20 products, plus one product-detail page per
result). At standard Apify STANDARD tier pricing, that works out to a
fraction of a cent per product for typical runs.

### Limitations

- Only products Trolley.co.uk has itself matched across retailers are
  covered — this actor does not independently discover products at each
  supermarket.
- `retailerCount` and which retailers appear varies per product and can
  change as Trolley updates its own matching.
- Prices reflect Trolley's most recently indexed price per retailer, which
  may lag the live price on the retailer's own site by up to a few hours.
- Data is scraped from the public website and may change without notice.
- Trolley.co.uk's Terms of Service state a general restriction on automated
  access; this actor only requests publicly served, non-authenticated pages
  and does not access the disallowed `/search/` endpoint.

### Related actors

- [Tesco Scraper](https://apify.com/studio-amba/tesco-scraper)
- [Sainsbury's Scraper](https://apify.com/studio-amba/sainsburys-scraper)
- [Asda Scraper](https://apify.com/studio-amba/asda-scraper)
- [UK Grocery Price Matrix](https://apify.com/studio-amba/uk-grocery-price-matrix) — barcode-matched price comparison built from our own retailer scrapers directly

### Need this data on a schedule, or a custom version?

We run this scraper as a managed service for businesses: scheduled runs,
deduplication, delta detection, and delivery to your inbox, Google Sheets,
or API — maintenance included. We can also build a custom version with your
exact fields and filters, or combine multiple sources into one feed.

See [studioamba.dev/services](https://studioamba.dev/services/) or email
<hello@studioamba.dev> for a free data sample.
We maintain 300+ European web scrapers and answer within one business day.

# Actor input Schema

## `category` (type: `string`):

Trolley.co.uk category slug to browse (from /explore/{category}), e.g. 'milks', 'cheeses', 'yoghurts', 'eggs', 'bries'. The site's own /search/ endpoint is robots-disallowed, so discovery goes through category browsing instead.

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

Optional: scrape specific trolley.co.uk product page URLs directly (e.g. https://www.trolley.co.uk/product/{slug}/{SKU}) instead of browsing a category. When set, this overrides Category.

## `maxResults` (type: `integer`):

Maximum number of products to return. Category pages return 20 products per page, so the crawler pages through /explore/{category} until this is reached.

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

Proxy settings. trolley.co.uk has no anti-bot protection at any tier (verified via recon: plain curl with no User-Agent returns full HTML on every page type), so a proxy isn't required for it to work, but the free Apify automatic pool is used by default for reliability at scale.

## Actor input object example

```json
{
  "category": "milks",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "category": "milks",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/trolley-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 = {
    "category": "milks",
    "maxResults": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/trolley-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 '{
  "category": "milks",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call studio-amba/trolley-scraper --silent --output-dataset

```

## MCP server setup

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