# Morrisons Scraper — Full UK Grocery Catalog & Prices (`studio-amba/morrisons-scraper`) Actor

Scrape the complete Morrisons UK groceries catalog: product names, brands, GBP prices, price per unit, categories, images, ratings, promotions and availability. Optional nutrition, ingredients and allergens. No login or cookies required.

- **URL**: https://apify.com/studio-amba/morrisons-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 88.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Morrisons Scraper

Scrape the complete Morrisons UK groceries catalog — every product, with prices,
categories, images, ratings and availability. No login, no cookies, no account
required.

Morrisons (groceries.morrisons.com) runs on the Ocado Smart Platform. This actor
talks directly to the site's public product API, so it returns clean structured
data fast and reliably — not brittle HTML scraping.

### Why use this actor?

Grocery buyers, price-comparison sites, market researchers and brand teams need a
full, current view of what a major UK supermarket sells and at what price. This
actor pulls the entire Morrisons catalog (around 30,000 products) in a single run,
deduplicated across every department, so you get one clean product feed instead of
a partial sample.

- **Complete catalog, provably.** The actor walks every department and cross-checks
  its unique product count against the Morrisons product sitemap, so you can verify
  coverage rather than trust it.
- **No cookies, no login.** The data comes from the site's open API. Nothing to
  authenticate, no session to keep alive.
- **GBP prices and price-per-unit.** Every product carries its current sell price
  and a normalised unit price (per each, per kg, per litre, etc.).

### How to scrape Morrisons data

1. Add the actor to your Apify account.
2. Leave every field on its default to pull a quick 100-product sample, or set
   **Max products** to `0` to scrape the **complete catalog** (~30,000 products).
3. Optionally set a **Category filter** to scope the run to one department (for
   example `Fruit & Veg` or `Beer, Wines & Spirits`), or set a **Search query**
   (for example `heinz baked beans`) to search the catalog by keyword instead
   of browsing categories. Search query takes priority over the category filter.
4. Optionally tick **Fetch nutrition, ingredients & allergens** for deeper
   per-product detail (slower — one extra request per product).
5. Run the actor. Results stream into the dataset and can be exported as JSON,
   CSV, Excel or fed to an API.

The Morrisons API is open and needs no proxy, but a UK residential proxy is
prefilled and recommended for large runs.

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `maxProducts` | Integer | No | Max products to return. `0` = the complete catalog (~30,000). Default `100`. |
| `category` | String | No | Limit to one department or sub-category (e.g. `Fruit & Veg`) or a Morrisons categoryId. Empty = all departments. Ignored when `searchQuery` is set. |
| `searchQuery` | String | No | Search the catalog for a keyword (e.g. `heinz baked beans`) instead of browsing categories. Overrides `category` when set. |
| `enrichDetails` | Boolean | No | Fetch nutrition, ingredients, allergens and country of origin per product. Default `false`. |
| `proxyConfiguration` | Object | No | Proxy settings. GB residential prefilled. Optional for small runs. |

#### Get the complete catalog

```json
{
    "maxProducts": 0,
    "enrichDetails": false,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB"
    }
}
````

#### Search by keyword

```json
{
    "searchQuery": "heinz baked beans",
    "maxProducts": 50
}
```

### Output

Each result contains:

| Field | Type | Example |
|-------|------|---------|
| `name` | String | `"Morrisons British Strawberries 350g"` |
| `brand` | String | `"Morrisons"` |
| `price` | Number | `2.49` |
| `currency` | String | `"GBP"` |
| `pricePerUnit` | String | `"£7.11/kg"` |
| `productId` | String | `"108413093"` |
| `url` | String | Full product page URL |
| `availability` | String | `"in stock"` |
| `inStock` | Boolean | `true` |
| `category` | String | `"Cucumber"` |
| `categories` | Array | `["Fruit & Veg", "Salads", "Cucumber"]` |
| `imageUrl` | String | Primary product image URL |
| `imageUrls` | Array | All product image URLs |
| `packSize` | String | `"350g"` |
| `alcohol` | Boolean | `false` |
| `isNew` | Boolean | `false` |
| `promotion` | String | `"Price Match"` |
| `rating` | Number | `3.6` |
| `reviewCount` | Number | `25` |
| `productDescription` | String | Only when enrichment is enabled |
| `ingredients` | String | Only when enrichment is enabled |
| `nutrition` | String | Only when enrichment is enabled |
| `allergens` | String | Only when enrichment is enabled |
| `countryOfOrigin` | String | Only when enrichment is enabled |
| `scrapedAt` | String | ISO 8601 timestamp |

### Example output

```json
{
    "name": "Morrisons Whole Cucumber",
    "brand": "Morrisons",
    "price": 0.99,
    "currency": "GBP",
    "pricePerUnit": "£0.99/each",
    "productId": "108413093",
    "url": "https://groceries.morrisons.com/products/morrisons-whole-cucumber/108413093",
    "availability": "in stock",
    "inStock": true,
    "category": "Cucumber",
    "categories": ["Fruit & Veg", "Salads", "Cucumber"],
    "imageUrl": "https://groceries.morrisons.com/images-v3/.../300x300.jpg",
    "imageUrls": ["https://groceries.morrisons.com/images-v3/.../500x500.jpg"],
    "packSize": null,
    "alcohol": false,
    "isNew": false,
    "promotion": "Price Match",
    "rating": 3.6,
    "reviewCount": 25,
    "scrapedAt": "2026-07-03T12:00:00.000Z"
}
```

### Cost estimate

A full catalog run (around 30,000 products) needs roughly 200 listing requests and
finishes in a couple of minutes of compute — the API returns 200 fully populated
products per request. Enabling nutrition/ingredients enrichment adds one request
per product, so a full enriched run is much heavier. For price and catalog data,
leave enrichment off.

### Completeness

The actor deduplicates products by their Morrisons product ID across every
department, then compares the unique count to the Morrisons product sitemap and
writes a `COMPLETENESS_REPORT` to the run's key-value store. That gives you a
verifiable coverage figure for the run instead of an unprovable claim.

### Limitations

- **No EAN / GTIN barcodes.** Morrisons does not expose barcodes anywhere in its
  data — only its own numeric `productId`. If you need barcodes, this site can't
  supply them.
- Prices are the online groceries prices. Morrisons runs a single national online
  price, but in-store prices can differ.
- Promotions are captured as a description (e.g. "Price Match", "Buy 3 for £5")
  rather than an explicit was-price.
- Data is scraped from the public website and may change without notice.
- Respect the website's terms of service and use responsibly.

### 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

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

Maximum number of products to return. Set to 0 for the COMPLETE catalog (~30,000 products). Leave the default for a quick sample.

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

Limit the run to one department or sub-category. Use a department name exactly as shown on the site (e.g. 'Fruit & Veg', 'Food Cupboard', 'Beer, Wines & Spirits') or a Morrisons categoryId. Leave empty to scrape all departments. Ignored when Search query is set.

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

Search the Morrisons catalog for a keyword (e.g. 'heinz baked beans') instead of browsing by category. When set, this overrides the Category filter. Leave empty for the normal category-browse behaviour.

## `enrichDetails` (type: `boolean`):

For each product, make an extra request to fetch nutrition, ingredients, allergens, country of origin and a description. Much slower on a full run (one extra request per product). Leave off for price and catalog data only.

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

Proxy settings. The Morrisons JSON API is open and needs no proxy. A GB residential proxy is prefilled so prices resolve as UK retail; the scraper rotates the proxy session automatically if an exit is blocked, so a few bad exits never drop a department. Leave empty to run direct (fastest).

## Actor input object example

```json
{
  "maxProducts": 20,
  "enrichDetails": false,
  "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 = {
    "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/morrisons-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 = {
    "maxProducts": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "GB",
    },
}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=studio-amba/morrisons-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Morrisons Scraper — Full UK Grocery Catalog & Prices",
        "description": "Scrape the complete Morrisons UK groceries catalog: product names, brands, GBP prices, price per unit, categories, images, ratings, promotions and availability. Optional nutrition, ingredients and allergens. No login or cookies required.",
        "version": "0.1",
        "x-build-id": "RDKLGygFV7eIYrE28"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/studio-amba~morrisons-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-studio-amba-morrisons-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~morrisons-scraper/runs": {
            "post": {
                "operationId": "runs-sync-studio-amba-morrisons-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~morrisons-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-studio-amba-morrisons-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": {
                    "maxProducts": {
                        "title": "Max products",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum number of products to return. Set to 0 for the COMPLETE catalog (~30,000 products). Leave the default for a quick sample.",
                        "default": 100
                    },
                    "category": {
                        "title": "Category filter (optional)",
                        "type": "string",
                        "description": "Limit the run to one department or sub-category. Use a department name exactly as shown on the site (e.g. 'Fruit & Veg', 'Food Cupboard', 'Beer, Wines & Spirits') or a Morrisons categoryId. Leave empty to scrape all departments. Ignored when Search query is set."
                    },
                    "searchQuery": {
                        "title": "Search query (optional)",
                        "type": "string",
                        "description": "Search the Morrisons catalog for a keyword (e.g. 'heinz baked beans') instead of browsing by category. When set, this overrides the Category filter. Leave empty for the normal category-browse behaviour."
                    },
                    "enrichDetails": {
                        "title": "Fetch nutrition, ingredients & allergens",
                        "type": "boolean",
                        "description": "For each product, make an extra request to fetch nutrition, ingredients, allergens, country of origin and a description. Much slower on a full run (one extra request per product). Leave off for price and catalog data only.",
                        "default": false
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings. The Morrisons JSON API is open and needs no proxy. A GB residential proxy is prefilled so prices resolve as UK retail; the scraper rotates the proxy session automatically if an exit is blocked, so a few bad exits never drop a department. Leave empty to run direct (fastest)."
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
