# Coop UK Scraper — Groceries Products & Prices (`studio-amba/coop-uk-scraper`) Actor

Scrape UK Co-op (shop.coop.co.uk) groceries: product names, prices, member prices, price per unit, EAN barcodes, brands, images and categories. Postcode-scoped quick-commerce catalogue, not Coop Denmark.

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

## Pricing

from $5.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

## Coop UK Scraper

Scrape UK Co-op groceries from shop.coop.co.uk: product names, prices, Co-op Membership prices, price per unit, EAN barcodes, brands, images and categories. No login and no delivery slot booking needed on your end.

This actor targets the **UK Co-operative Group's** online grocery shop
(`shop.coop.co.uk`). It is not related to Coop Denmark (Kvickly, SuperBrugsen,
Brugsen, 365discount) — that is a separate company covered by a different
actor in this catalogue.

### Why use this actor?

Co-op is one of the UK's largest convenience-store chains, and its online
shop carries both Co-op own-brand lines and national grocery brands. This
actor gives you a clean, structured feed of that catalogue for price
monitoring, competitor benchmarking, product matching against other UK
grocers, or market research — including the EAN/barcode on nearly every
product, which is what makes cross-retailer product matching possible.

Co-op's online shop is a postcode-scoped quick-commerce app, and both
`coop.co.uk` and `shop.coop.co.uk` sit behind Imperva/Incapsula Bot Manager.
This actor handles the postcode → store → delivery-slot flow and the
anti-bot challenge automatically (via a Bright Data Scraping Browser
session), so you get reliable results run after run without touching a
browser yourself.

### How to scrape Coop UK data

1. Add this actor to your Apify account.
2. A Bright Data Scraping Browser endpoint is required — provide it as
   `sbrWsCdp` in the input, or have it set as the `SBR_WS_CDP` environment
   variable on the actor. `shop.coop.co.uk` cannot be reached without it
   (see Limitations).
3. Choose what to scrape:
   - Set `searchQuery` to scrape a keyword, e.g. `milk`, `bread`, `crisps`.
   - Set `categoryUrl` to browse one category instead, e.g.
     `dairy-eggs-and-chilled-juice-fresh-milk` or a full
     `https://shop.coop.co.uk/category/fresh-milk--dairy-eggs-and-chilled-juice-fresh-milk` URL.
   - Leave both empty and the actor defaults to `searchQuery: "milk"` so a
     run always returns data.
4. Set `maxProducts` to cap the run (prefilled 20 for a quick test).
5. Run the actor. Results stream to the dataset and can be exported as
   JSON, CSV, Excel or fed to an API.

Under the hood, the actor opens one browser session, completes Co-op's
postcode/store/delivery-slot flow once, then queries Co-op's own search API
(a first-party proxy in front of Algolia) directly from that session —
up to 1,000 results per request, so most searches complete in a single
API call rather than a slow page-by-page crawl.

### Input

| Field | Type | Required | Description |
|-------|------|----------|--------------|
| `sbrWsCdp` | String (secret) | Yes\* | Bright Data Scraping Browser CDP endpoint (`wss://...`). \*Falls back to the `SBR_WS_CDP` environment variable. |
| `searchQuery` | String | No | Search Co-op groceries by keyword (default: `milk`) |
| `categoryUrl` | String | No | A Co-op category URL or bare category ID to browse instead of searching |
| `maxProducts` | Integer | No | Maximum products to return (default 20) |
| `postcode` | String | No | UK postcode used to select a delivery hub/store (default `M1 1AE`). The catalogue itself is nationwide; this only needs to resolve to a valid store. |
| `proxyConfiguration` | Object | No | Proxy settings for auxiliary requests (primary site access goes through the Scraping Browser) |

### Output

Each result contains:

| Field | Type | Example |
|-------|------|---------|
| `name` | String | `"Co-op Semi-Skimmed Milk 4 Pints/2.272L"` |
| `brand` | String | `"Co-op"` |
| `price` | Number | `1.85` |
| `currency` | String | `"GBP"` |
| `pricePerUnit` | String | `"81.5p per ltr"` |
| `discount` | String | `"Member price £1.65 (72.7p per ltr)"` |
| `memberPrice` | Number or null | `1.65` |
| `memberPromotion` | String or null | `null` |
| `promotion` | String or null | `"BUY ANY 2 FOR £5"` |
| `promoValidFrom` | String or null | `null` |
| `promoValidUntil` | String or null | `"2026-11-17"` |
| `ean` | String | `"5000128104524"` |
| `productId` | String | `"5000128104524"` |
| `category` | String | `"Dairy, eggs and chilled juice > Fresh milk"` |
| `categories` | Array | `["Dairy, eggs and chilled juice", "Fresh milk"]` |
| `imageUrl` | String | Primary product image URL |
| `maxQuantity` | Integer | `6` (per-order purchase limit, if any) |
| `url` | String | A working Co-op search URL for this exact product |
| `scrapedAt` | String | ISO 8601 timestamp |

### Example output

```json
{
    "name": "Co-op Semi-Skimmed Milk 4 Pints/2.272L",
    "brand": "Co-op",
    "price": 1.85,
    "currency": "GBP",
    "url": "https://shop.coop.co.uk/search?term=5000128104524",
    "scrapedAt": "2026-07-14T19:46:57.664Z",
    "pricePerUnit": "81.5p per ltr",
    "discount": "Member price £1.65 (72.7p per ltr)",
    "ean": "5000128104524",
    "productId": "5000128104524",
    "category": "Dairy, eggs and chilled juice > Fresh milk",
    "categories": ["Dairy, eggs and chilled juice", "Fresh milk"],
    "imageUrl": "https://cdn.pr.commerce.coop.co.uk/5000128104524_1_512x512_20260429.png",
    "maxQuantity": 6,
    "memberPrice": 1.65,
    "memberPromotion": null
}
```

### Prices and member pricing

- `price` is always the standard (non-member) shelf price shown in GBP.
- Co-op Membership carries a separate, lower price on many products. When
  present and lower than the standard price, it's captured in `discount` as
  e.g. `"Member price £1.65 (72.7p per ltr)"` — this is not a time-boxed
  promotion, it reflects Co-op's ongoing membership pricing structure.
- `memberPrice` is the Co-op Member Price in GBP as a number. It is `null`
  when the product has no member price.
- `memberPromotion` is the text of any member-only promotion on the product,
  such as a multi-buy deal for members. It is `null` when there is none,
  which is the case for most products. Multi-buy deals never go into
  `memberPrice`.
- `pricePerUnit` gives the comparable unit price (per litre, per kg, etc.)
  at the standard rate.
- Stock/availability can vary by store; this actor scopes to a single
  representative delivery hub (configurable via `postcode`) and returns the
  catalogue-wide search results, which is the right baseline for catalogue
  and price data.

### Cost estimate

The actor opens one Scraping Browser session per run and typically needs
just one or a handful of search API calls (up to 1,000 results per call),
regardless of `maxProducts` up to that ceiling. A single search or category
run is fast and cheap; use `maxProducts` to control the size of larger runs.

The Bright Data Scraping Browser cost of a run is dominated by the postcode
bootstrap step, not by result count: Co-op's Imperva/Incapsula challenge
rejects a large share of fresh sessions, so a run can open several browser
sessions (up to 10) before one gets through, each one a billed session plus
its data transfer, before a single product is scraped. That means the
bootstrap cost is roughly fixed per run regardless of `maxProducts` — a
small `maxProducts` run absorbs it as a larger share of the run's price
than a large one, since the Algolia search step itself returns up to 1,000
results in one call once the session exists. Your run's usage cost only
settles after the run reports SUCCEEDED.

### Limitations

- **A Bright Data Scraping Browser endpoint is required.** Both
  `www.coop.co.uk` and `shop.coop.co.uk` sit behind Imperva/Incapsula Bot
  Manager plus a UK-only geo-block; this cannot be bypassed with a plain
  HTTP request or a stateless Bright Data Web Unlocker call — the postcode →
  store → delivery-slot flow needs a real, JS-capable browser session.
- **Session bootstrap can take several retries.** Co-op's Imperva challenge
  rejects a variable share of fresh Scraping Browser sessions (as of
  2026-09-16, most sessions in a run were rejected); the actor opens a new
  session per attempt (up to 10) and only fails the run if every attempt is
  rejected. A run that fails on this step returns zero items rather than
  partial or incorrect data.
- **No individual product-page URL.** Co-op's search results open products
  in an in-page quick-view rather than a unique page. `url` is instead a
  working Co-op search link scoped to that exact EAN, which reliably
  surfaces the same product on the live site.
- EAN is present on nearly every product in this catalogue (own-brand and
  national brands alike); the rare item without one is a genuine gap on
  Co-op's side, not a parsing failure.
- Brand is only as clean as Co-op's own `brandFilter` field — a small
  number of items may carry a generic or missing brand.
- Data is scraped from the public website and may change without notice.
  Respect Co-op's terms of service and use responsibly.

# Actor input Schema

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

Search Co-op groceries by keyword (e.g., 'milk', 'bread', 'crisps').

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

A Co-op category to scrape. Full URL (https://shop.coop.co.uk/category/fresh-milk--dairy-eggs-and-chilled-juice-fresh-milk) or just the category ID (dairy-eggs-and-chilled-juice-fresh-milk). Leave empty to use Search Query instead.

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

Maximum number of products to return.

## `postcode` (type: `string`):

A real UK postcode used to select a Co-op delivery hub/store (catalogue is nationwide regardless of the postcode chosen; this only needs to resolve to a valid store). Default works out of the box.

## `sbrWsCdp` (type: `string`):

Bright Data Scraping Browser CDP WebSocket endpoint (wss://...). Required — shop.coop.co.uk is behind Imperva/Incapsula Bot Manager and can only be reached with a real browser session that solves the challenge. Falls back to the SBR_WS_CDP environment variable.

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

Proxy settings for any auxiliary requests. Primary site access goes through the Bright Data Scraping Browser (Imperva/Incapsula bot protection).

## Actor input object example

```json
{
  "searchQuery": "milk",
  "maxProducts": 20,
  "postcode": "M1 1AE",
  "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/coop-uk-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/coop-uk-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/coop-uk-scraper --silent --output-dataset

```

## MCP server setup

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