# Morrisons Groceries Scraper - Prices & Nutrition (`vulnv/morrisons-scraper`) Actor

Scrape Morrisons groceries by category or URL. Get price, was-price, unit price, offers, size, availability, category taxonomy, images, ratings, ingredients, allergens and nutrition. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/vulnv/morrisons-scraper.md
- **Developed by:** [VulnV](https://apify.com/vulnv) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.50 / 1,000 products

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

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

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.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/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 Groceries Scraper

Extract **Morrisons** grocery product data at scale - price, **was-price**, unit price, offers,
size, availability, full category taxonomy, images and ratings, plus **ingredients, allergens
and nutrition** from each product page - as clean, structured JSON.

Scrape by **category name** or by pasting **Morrisons URLs**.

### 🚀 How to use

1. Click **Try for free** / **Start**.
2. Type the **categories** you want (e.g. `Breakfast Cereal`, `Fresh Fruit`) - or paste
   **Morrisons URLs**.
3. Set **Max products**, and **Max sub-categories** if you named a broad department.
4. Run the Actor and download results as **JSON, CSV, Excel, HTML or via API**.

### ✨ Features

- 💷 **Full pricing** - current price, the **was-price on markdowns**, and unit price
  (per kg / litre / each), so you can see the discount, not just the shelf price.
- 🎟️ **Offers** - the offer wording Morrisons displays, plus a flag and a count.
- 📦 **Availability and size** - stock status and pack size on every record.
- 🗂️ **Category taxonomy** - the product's full path (department → aisle → shelf), plus the
  category it was found under.
- 🥗 **Back-of-pack label data** - **ingredients**, **allergens**, the **nutrition table**,
  kcal per 100 g/ml, storage, dietary information, country of origin and manufacturer.
- ⚠️ **Allergens, properly** - Morrisons emphasises allergens *inside* the ingredient list, so
  the Actor captures those highlighted terms as well as the printed allergen statement.
- ⭐ **Ratings** - average rating (as a number) and review count.
- 🌳 **Automatic category expansion** - name a whole department and the Actor walks its
  sub-categories for you.

### 🧭 Input

Provide at least one of `categories` or `urls`.

| Field | Type | Description |
|-------|------|-------------|
| `categories` | array | Category names as shown on the site, e.g. `Breakfast Cereal`. Slugs like `breakfast-cereal` work too. A broad department is expanded into its sub-categories. |
| `urls` | array | Morrisons category or product URLs. |
| `maxItems` | integer | Stop after this many products across the run. Default `200`. |
| `maxCategories` | integer | How many sub-categories to walk per named department. Default `25`. |
| `includeProductDetails` | boolean | Open each product page for ingredients, allergens and nutrition. One extra request per product. Default `true`. |
| `onlyUnique` | boolean | Drop repeats across categories. Default `true`. |

#### Example input

```json
{
  "categories": ["Breakfast Cereal", "Biscuits"],
  "maxItems": 500,
  "maxCategories": 25,
  "includeProductDetails": true
}
````

A fast price-and-stock sweep across a whole department:

```json
{
  "categories": ["Food Cupboard"],
  "maxItems": 5000,
  "maxCategories": 200,
  "includeProductDetails": false
}
```

### 📤 Output

One record per product. Abridged example (a marked-down item, with details on):

```json
{
  "productId": "112986628",
  "name": "Kellogg's Rice Krispies Breakfast Cereal",
  "brand": "Kelloggs",
  "size": "660g",
  "url": "https://groceries.morrisons.com/products/kellogg-s-rice-krispies-breakfast-cereal/112986628",

  "currency": "GBP",
  "price": 3.85,
  "priceText": "£3.85",
  "wasPrice": 4.35,
  "unitPrice": 5.83,
  "unitOfMeasure": "kg",
  "unitPriceText": "£5.83/kg",

  "onPromotion": true,
  "promotionCount": 1,
  "promotionText": "Now £3.85, Was £4.35",

  "available": true,
  "isNew": false,
  "category": "Breakfast Cereal",
  "categoryPath": "Food Cupboard > Breakfast Cereal > Everyday Family Cereals > Classic Cereal",

  "imageUrl": "https://groceries.morrisons.com/images-v3/.../500x500.jpg",
  "imageCount": 2,
  "averageRating": 4.8,
  "reviewCount": 16,

  "ingredients": "Rice Flour, Sugar, Barley Malt Extract, Wheat Gluten, Salt, Niacin, Iron, Vitamin B6, Riboflavin, Thiamin, Folic Acid, Vitamin D, Vitamin B12",
  "allergens": "Contains Barley, May Contain Cereals Containing Gluten, May Contain Soya, Contains Wheat",
  "highlightedAllergens": ["Barley", "Wheat"],
  "nutritionText": "Energy: 1649 kJ; Energy: 389 kcal; Fat: 1.2 g; Carbohydrate: 86 g",
  "energyKcalPer100": 389,
  "storage": "Best before: see top. Store in a cool, dry place.",
  "manufacturer": "UK: Kellogg Marketing and Sales Company (UK) Limited, ...",
  "nutritionTable": [
    { "name": "Energy", "/100g": "1649 kJ", "/30g": "495 kJ" },
    { "name": "Fat", "/100g": "1.2 g", "/30g": "0.4 g", "%RI*": "1 %" }
  ],

  "scrapedAt": "2026-07-26T18:20:00+00:00"
}
```

The flat fields are what the dataset table and CSV/Excel exports show; `nutritionTable`,
`images` and `highlightedAllergens` are nested for programmatic use.

#### Dataset views

- **Products** - price, offers, availability and taxonomy for every product.
- **Ingredients & nutrition** - the label data, when `includeProductDetails` is on.

### 💡 Use cases

- **Grocery price monitoring** - track prices and markdowns across categories daily.
- **Competitor analysis** - compare range and pricing against other supermarkets.
- **Product catalogue enrichment** - pull images, sizes, descriptions and nutrition.
- **Food and nutrition research** - ingredients, allergens and nutrition at scale.
- **Availability tracking** - spot delistings, new lines and out-of-stock items.

### 📋 Notes and limits

- **Naming a broad department walks its sub-categories.** Morrisons serves only the first page
  of any one category to anonymous visitors, so the Actor covers a department by walking its
  sub-categories instead, where a single page usually holds everything. Raise `maxCategories`
  to widen the sweep.
- **Product URLs return label data but no price.** A Morrisons product page publishes the
  back-of-pack panel but not a machine-readable price, so products reached directly by URL come
  back with ingredients, allergens and nutrition, and `price` left empty rather than guessed.
  Scrape by category to get pricing.
- **Allergens** are taken from the printed statement where Morrisons publishes one, and
  otherwise from the allergens it emphasises inside the ingredient list
  (`highlightedAllergens`). Not every product publishes either.
- Fields Morrisons does not publish for a product come back empty - not every item has
  ingredients or a nutrition table.
- `includeProductDetails` costs one extra request per product. Turn it off for the largest
  price sweeps; listing data alone still carries price, was-price, unit price, offers,
  availability, size, taxonomy, image and rating.

### 💳 Pricing

This Actor is monetized **pay-per-event**: a flat rate per product returned, and nothing else -
no Actor start fee and no platform usage charges. Free-plan users can try it with a capped
sample per run. See the **Pricing** tab for the current rate.

### ❓ FAQ

**Is scraping Morrisons data legal?**
The Actor collects only publicly available product information published on Morrisons'
storefront. It reads no personal data and needs no login. As with any scraping, use the data
responsibly and in line with applicable laws and the site's terms.

**Is this an official Morrisons product?**
No. This is an unofficial Actor and is not affiliated with, endorsed by, or sponsored by
Morrisons. "Morrisons" is a trademark of its respective owner.

**How do I scrape a whole department?**
Name it (e.g. `Food Cupboard`) and raise `maxCategories` - the Actor expands it into
sub-categories automatically. Turn `includeProductDetails` off for the widest sweeps.

**Why did I get fewer products than the category says it holds?**
Morrisons serves one page per category to anonymous visitors. The Actor reports both numbers in
the run log ("300 listed, 49 on this page") and covers the rest by walking sub-categories.

**How do I test cheaply?**
Set `maxItems` to a small number, e.g. 5.

### 🛠️ Our Other Scrapers

Explore our full suite of scraping tools to power your data needs:

- **[Tesco UK & Ireland Scraper](https://apify.com/vulnv/tesco-scraper)** — Tesco groceries with GTIN barcodes, Clubcard prices and nutrition.
- **[Amazon Product Scraper](https://apify.com/vulnv/amazon-product-scraper)** — Extract Amazon product data, pricing and reviews.
- **[1688 Wholesale Scraper](https://apify.com/vulnv/1688-wholesale-scraper)** — Wholesale products, tiered pricing and suppliers from 1688.com.
- **[Email Validator](https://apify.com/vulnv/email-validator)** — Validate and verify email addresses at scale.

📬 **Need help or have a feature request?** Contact us at <support@vulnv.com> or visit our [Apify profile](https://apify.com/vulnv).

# Actor input Schema

## `categories` (type: `array`):

Category names as they appear on the site, e.g. 'Breakfast Cereal', 'Fresh Fruit', 'Food Cupboard'. A broad department is expanded into its sub-categories automatically. Slugs like 'breakfast-cereal' work too.

## `urls` (type: `array`):

Category or product URLs to scrape, e.g. https://groceries.morrisons.com/categories/food-cupboard/breakfast-cereal/180005

## `maxItems` (type: `integer`):

Stop after this many products across the whole run. Free-plan users are capped at 50 per run.

## `maxCategories` (type: `integer`):

How many sub-categories to walk when you name a broad department. Each sub-category costs one request and typically returns all of its products.

## `includeProductDetails` (type: `boolean`):

Open each product page to add ingredients, allergens, the nutrition table, storage, country of origin and manufacturer. This costs one extra request per product, so turn it off for fast price-and-stock sweeps.

## `onlyUnique` (type: `boolean`):

Drop repeats when the same product appears in more than one category.

## Actor input object example

```json
{
  "categories": [
    "Breakfast Cereal"
  ],
  "maxItems": 200,
  "maxCategories": 25,
  "includeProductDetails": true,
  "onlyUnique": 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 = {
    "categories": [
        "Breakfast Cereal"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("vulnv/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 = { "categories": ["Breakfast Cereal"] }

# Run the Actor and wait for it to finish
run = client.actor("vulnv/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 '{
  "categories": [
    "Breakfast Cereal"
  ]
}' |
apify call vulnv/morrisons-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Morrisons Groceries Scraper - Prices & Nutrition",
        "description": "Scrape Morrisons groceries by category or URL. Get price, was-price, unit price, offers, size, availability, category taxonomy, images, ratings, ingredients, allergens and nutrition. Export to JSON, CSV or Excel.",
        "version": "1.0",
        "x-build-id": "DWCKfGovGThjxF7Mf"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/vulnv~morrisons-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-vulnv-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/vulnv~morrisons-scraper/runs": {
            "post": {
                "operationId": "runs-sync-vulnv-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/vulnv~morrisons-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-vulnv-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": {
                    "categories": {
                        "title": "Categories",
                        "type": "array",
                        "description": "Category names as they appear on the site, e.g. 'Breakfast Cereal', 'Fresh Fruit', 'Food Cupboard'. A broad department is expanded into its sub-categories automatically. Slugs like 'breakfast-cereal' work too.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "urls": {
                        "title": "Morrisons URLs",
                        "type": "array",
                        "description": "Category or product URLs to scrape, e.g. https://groceries.morrisons.com/categories/food-cupboard/breakfast-cereal/180005",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxItems": {
                        "title": "Max products",
                        "minimum": 1,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Stop after this many products across the whole run. Free-plan users are capped at 50 per run.",
                        "default": 200
                    },
                    "maxCategories": {
                        "title": "Max sub-categories per category",
                        "minimum": 1,
                        "maximum": 2000,
                        "type": "integer",
                        "description": "How many sub-categories to walk when you name a broad department. Each sub-category costs one request and typically returns all of its products.",
                        "default": 25
                    },
                    "includeProductDetails": {
                        "title": "Include product page details",
                        "type": "boolean",
                        "description": "Open each product page to add ingredients, allergens, the nutrition table, storage, country of origin and manufacturer. This costs one extra request per product, so turn it off for fast price-and-stock sweeps.",
                        "default": true
                    },
                    "onlyUnique": {
                        "title": "Only unique products",
                        "type": "boolean",
                        "description": "Drop repeats when the same product appears in more than one category.",
                        "default": true
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
