# Morrisons Scraper (`solidcode/morrisons-scraper`) Actor

\[💰 $2.5 / 1K] Extract Morrisons grocery products by keyword, category, or product URL — name, brand, price, unit price, size, offers, ratings, images, and availability. Optional customer reviews.

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

## Pricing

from $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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## Morrisons Scraper

Pull live grocery data from Morrisons — prices, unit prices, pack sizes, multibuy and "Price Match" offers, star ratings, availability, product images, and customer reviews — by keyword search, category page, or individual product URL. Built for retail and CPG analysts, price-comparison platforms, and FMCG brands who need to track Morrisons shelf prices and promotions at scale without building and maintaining their own data pipeline.

### Why This Scraper?

- **Three ways to target products** — keyword search (e.g. "baked beans"), a Morrisons category page URL, or a list of specific product URLs. Mix all three in one run.
- **Up to 300 products per search or category** — captures the full Morrisons listing depth for any term or aisle in a single query, ad placements stripped out automatically.
- **Normalized unit pricing** — every product carries a comparable unit price plus its measure (per kg, per litre, or per item), always quoted in pounds, so you can sort and compare value across pack sizes without manual maths.
- **Promotion and "Price Match" capture** — detects whether a product is on offer, the offer wording (e.g. "Buy 3 for £10", "Price Match"), and pairs the discounted price with the pre-offer "was" price so you can measure the size of every price cut.
- **Optional customer reviews** — pull individual reviews per product with author, star rating, headline, full review text, and date, newest first.
- **Star rating plus review count** — the summary overall rating and total number of ratings come on every product, even with reviews turned off.
- **Pack size and brand on every row** — pack/size description (e.g. "4 x 410g") and brand name for clean catalog building and matching.
- **Direct product image and link** — a full-resolution product image URL and a canonical Morrisons product URL for every result.
- **Availability flag** — a true/false in-stock signal so you can filter out delisted or temporarily unavailable lines.

### Use Cases

**Price Intelligence**

- Track Morrisons prices for a basket of products on a recurring schedule
- Compare unit prices (per kg, per litre, per item) across competing pack sizes
- Benchmark Morrisons against other supermarkets in price-comparison tools
- Detect price changes and "was/now" movements over time

**Promotion Monitoring**

- Spot which products are on offer and capture the exact offer wording
- Monitor "Price Match" lines and multibuy deals as they launch
- Measure promotion frequency and depth by brand or category
- Alert when a tracked product goes on or comes off offer

**Product Catalog Building**

- Build a structured Morrisons catalog with name, brand, size, and image
- Map products to category paths for taxonomy and assortment analysis
- Keep an enriched product feed fresh for marketplaces and apps
- Match Morrisons SKUs to your own product master by name and pack size

**Brand & Review Analysis**

- Collect customer reviews to mine sentiment and recurring complaints
- Track star ratings and review volume for your own and rival brands
- Identify highly rated products in a category for merchandising
- Measure review growth on newly launched lines

**Competitive Research**

- Audit a competitor brand's full range across Morrisons categories
- Track availability to detect range changes and delistings
- Quantify shelf presence by brand within a category
- Feed assortment and pricing data into market dashboards

### Getting Started

#### Simple Keyword Search

The quickest way to start — one search term:

```json
{
    "searchQueries": ["baked beans"]
}
```

#### Search Multiple Terms with Reviews

```json
{
    "searchQueries": ["semi skimmed milk", "sourdough"],
    "maxResultsPerInput": 100,
    "includeReviews": true,
    "maxReviewsPerProduct": 20
}
```

#### Scrape a Category and Specific Products

```json
{
    "categoryUrls": [
        "https://groceries.morrisons.com/categories/fruit-veg/176738"
    ],
    "productUrls": [
        "https://groceries.morrisons.com/products/morrisons-bananas-loose/108001234"
    ],
    "maxResultsPerInput": 0
}
```

### Input Reference

#### What to Scrape

Provide at least one of these three. You can combine all of them in a single run.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `searchQueries` | string\[] | `["baked beans"]` | Product keywords to search for on Morrisons, such as 'baked beans', 'semi skimmed milk', or 'sourdough'. Each query is scraped separately. Leave empty if you only use category or product URLs. |
| `categoryUrls` | string\[] | `[]` | Morrisons category or browse page URLs (e.g. https://groceries.morrisons.com/categories/...). Every product listed under the category will be collected, up to the result limit. |
| `productUrls` | string\[] | `[]` | Direct Morrisons product page URLs (e.g. https://groceries.morrisons.com/products/...). Use this to scrape a specific list of known products. |

#### Options

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maxResultsPerInput` | integer | `50` | Maximum number of products to collect for each search query and each category URL. Morrisons returns at most 300 products per search or category, so set 0 to collect all available products up to that limit. Product URLs are always scraped in full regardless of this limit. |
| `includeReviews` | boolean | `false` | When enabled, collect individual customer reviews for each product (review text, rating, author, date). This visits each product page and increases run time and cost. When off, only the summary rating and rating count are returned. |
| `maxReviewsPerProduct` | integer | `20` | Maximum number of customer reviews to collect per product. Only applies when 'Include Customer Reviews' is enabled. Set to 0 for all available reviews — note that best-selling products can have hundreds of reviews, which makes runs noticeably slower and more expensive. The default of 20 keeps runs fast and predictable. |

### Output

Each product is one row. Here's a representative result with reviews enabled:

```json
{
    "query": "baked beans",
    "sourceUrl": null,
    "name": "Heinz Beanz In Tomato Sauce",
    "brand": "Heinz",
    "productId": "108001234",
    "price": 1.40,
    "originalPrice": 1.75,
    "unitPrice": 0.35,
    "unitPriceMeasure": "per item",
    "unitSize": "4 x 415g",
    "onOffer": true,
    "offerDescription": "Now £1.40, Was £1.75",
    "imageUrl": "https://groceries.morrisons.com/productImages/108/108001234_0_640x640.jpg",
    "productUrl": "https://groceries.morrisons.com/products/heinz-beanz-in-tomato-sauce/108001234",
    "categoryPath": ["Food Cupboard", "Tinned & Canned Food", "Baked Beans"],
    "rating": 4.6,
    "ratingCount": 512,
    "available": true,
    "attributes": ["Vegetarian", "Gluten Free"],
    "reviews": [
        {
            "author": "Sarah M",
            "rating": 5,
            "title": "Family favourite",
            "text": "Always the best beans, never buy anything else.",
            "date": "2026-05-18"
        }
    ],
    "scrapedAt": "2026-06-02T14:30:00Z"
}
```

#### Core Fields

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Full product name |
| `brand` | string | Brand name |
| `productId` | string | Morrisons product identifier |
| `unitSize` | string | Pack or size description (e.g. "4 x 415g") |
| `available` | boolean | Whether the product is currently in stock |
| `attributes` | string\[] | Product labels such as Vegetarian, Gluten Free, Vegan |
| `categoryPath` | string\[] | Category breadcrumb from department to sub-aisle |
| `imageUrl` | string | Full-resolution product image URL |
| `productUrl` | string | Canonical Morrisons product page URL |
| `scrapedAt` | string | ISO timestamp of data extraction |

#### Pricing & Offers

| Field | Type | Description |
|-------|------|-------------|
| `price` | number | Current selling price in GBP, after any price cut |
| `originalPrice` | number | Pre-offer "was" price on price-cut lines, when published |
| `unitPrice` | number | Comparable unit price in GBP, always in pounds |
| `unitPriceMeasure` | string | Unit basis: "per kg", "per litre", or "per item" |
| `onOffer` | boolean | Whether the product is currently on a promotion |
| `offerDescription` | string | Offer wording (e.g. "Buy 3 for £10", "Price Match") |

#### Ratings & Provenance

| Field | Type | Description |
|-------|------|-------------|
| `rating` | number | Overall average star rating |
| `ratingCount` | number | Total number of ratings |
| `query` | string | The search keyword that produced this row (null for URL inputs) |
| `sourceUrl` | string | The category or product URL that produced this row (null for keyword searches) |

#### Reviews

Populated only when `includeReviews` is enabled. `reviews` is an array of objects, each with these fields:

| Field | Type | Description |
|-------|------|-------------|
| `author` | string | Reviewer display name |
| `rating` | number | Star rating the reviewer gave (1-5) |
| `title` | string | Review headline |
| `text` | string | Full review text |
| `date` | string | Date the review was posted |

### Tips for Best Results

- **Start small** — set `maxResultsPerInput` to 20-50 on your first run to confirm the data matches your needs, then scale up.
- **Cap reviews on best-sellers** — popular Morrisons products can have hundreds of reviews. Keep `maxReviewsPerProduct` at 20 (the default) for fast, predictable runs; only set it to 0 when you genuinely need every review.
- **Leave reviews off for pure price tracking** — when you only need prices, unit prices, and offers, keep `includeReviews` off for the fastest, cheapest runs. The summary `rating` and `ratingCount` are always included anyway.
- **Compare on unit price, not shelf price** — use `unitPrice` with `unitPriceMeasure` to compare value across different pack sizes instead of the headline `price`.
- **Use category URLs for whole-aisle audits** — paste a category page URL to sweep up an entire aisle (up to 300 products) in one go, rather than guessing keywords.
- **Set `maxResultsPerInput` to 0 to go deep** — when you want the complete listing for a term or category, set it to 0 and the run collects everything Morrisons returns, up to its 300-product ceiling.
- **Watch `onOffer` and `originalPrice` together** — multibuy deals like "Buy 3 for £10" set `onOffer` and `offerDescription` but may have no single "was" price, so `originalPrice` can be null on those lines.

### Pricing

**From $2.50 per 1,000 products.** You pay per product row returned — no compute or time-based charges, just a small fixed start fee each time a run begins. Bronze, Silver, and Gold are Apify's subscription discount tiers; the table below shows the total cost of a run at each tier.

| Products | No discount | Bronze | Silver | Gold |
|----------|-------------|--------|--------|------|
| 100 | $0.30 | $0.28 | $0.27 | $0.25 |
| 1,000 | $3.00 | $2.80 | $2.65 | $2.50 |
| 10,000 | $30.00 | $28.00 | $26.50 | $25.00 |
| 100,000 | $300.00 | $280.00 | $265.00 | $250.00 |

Customer reviews are an optional add-on billed separately at **$0.30 per 1,000 reviews** on Gold — $0.36 with no discount, $0.34 Bronze, $0.32 Silver. Reviews come back embedded inside each product row and you only pay for the ones actually collected, so with `includeReviews` off you never pay the review rate at all. For example, 1,000 products with 20 reviews each costs $8.50 on Gold: $2.50 for the products plus $6.00 for the 20,000 reviews.

Cap `maxReviewsPerProduct` on best-sellers — or leave `includeReviews` off entirely — to keep both runs and costs predictable. Platform fees (storage and data transfer) depend on your Apify plan.

### Integrations

Export data in JSON, CSV, Excel, XML, or RSS. Connect to 1,500+ apps via:

- **Zapier** / **Make** / **n8n** — Workflow automation
- **Google Sheets** — Direct spreadsheet export
- **Slack** / **Email** — Notifications on new results
- **Webhooks** — Trigger custom APIs on run completion
- **Apify API** — Full programmatic access

### Legal & Ethical Use

This actor is designed for legitimate price intelligence, market research, and product catalog use. You are responsible for complying with applicable laws and Morrisons' Terms of Service. Collect only publicly available product information, do not use the data for spam or any illegal purpose, and handle any personal data in customer reviews responsibly and in line with privacy regulations.

# Actor input Schema

## `searchQueries` (type: `array`):

Product keywords to search for on Morrisons, such as 'baked beans', 'semi skimmed milk', or 'sourdough'. Each query is scraped separately. Leave empty if you only use category or product URLs.

## `categoryUrls` (type: `array`):

Morrisons category or browse page URLs (e.g. https://groceries.morrisons.com/categories/...). Every product listed under the category will be collected, up to the result limit.

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

Direct Morrisons product page URLs (e.g. https://groceries.morrisons.com/products/...). Use this to scrape a specific list of known products.

## `maxResultsPerInput` (type: `integer`):

Maximum number of products to collect for each search query and each category URL. Morrisons returns at most 300 products per search or category, so set 0 to collect all available products up to that limit. Product URLs are always scraped in full regardless of this limit.

## `includeReviews` (type: `boolean`):

When enabled, collect individual customer reviews for each product (review text, rating, author, date). This visits each product page and increases run time and cost. When off, only the summary rating and rating count are returned.

## `maxReviewsPerProduct` (type: `integer`):

Maximum number of customer reviews to collect per product. Only applies when 'Include Customer Reviews' is enabled. Set to 0 for all available reviews — note that best-selling products can have hundreds of reviews, which makes runs noticeably slower and more expensive. The default of 20 keeps runs fast and predictable.

## Actor input object example

```json
{
  "searchQueries": [
    "baked beans"
  ],
  "categoryUrls": [],
  "productUrls": [],
  "maxResultsPerInput": 50,
  "includeReviews": false,
  "maxReviewsPerProduct": 20
}
```

# Actor output Schema

## `overview` (type: `string`):

Table of products with key fields like name, brand, price, unit price, and offer status.

## `details` (type: `string`):

Full per-product detail rows including category path, image, ratings, attributes, and product URL.

# 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 = {
    "searchQueries": [
        "baked beans"
    ],
    "categoryUrls": [],
    "productUrls": [],
    "maxResultsPerInput": 50,
    "includeReviews": false,
    "maxReviewsPerProduct": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/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 = {
    "searchQueries": ["baked beans"],
    "categoryUrls": [],
    "productUrls": [],
    "maxResultsPerInput": 50,
    "includeReviews": False,
    "maxReviewsPerProduct": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/morrisons-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 '{
  "searchQueries": [
    "baked beans"
  ],
  "categoryUrls": [],
  "productUrls": [],
  "maxResultsPerInput": 50,
  "includeReviews": false,
  "maxReviewsPerProduct": 20
}' |
apify call solidcode/morrisons-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,solidcode/morrisons-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/WDLhMwYOeWSfCnuXJ/builds/qQ4QRetuPnTdsx6ak/openapi.json
