# Morrisons UK Grocery Scraper (`bobcodgodkid.1/morrisons-grocery-scraper`) Actor

Scrape Morrisons UK groceries by search term: export prices, availability, unit prices, pack sizes, promotions, ratings, categories and images as JSON, CSV or Excel. Fast JSON-API scraper for price monitoring, competitor intelligence and product data.

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

## Pricing

from $2.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are 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 – UK Grocery Prices, Offers & Product Data

**Scrape Morrisons groceries at scale.** This **Morrisons scraper** turns
[groceries.morrisons.com](https://groceries.morrisons.com) into clean, structured data —
**prices, offers, unit prices, pack sizes, ratings, categories, product URLs and every
product image** — for any search term or category. It's fast, accurate, and export-ready, with
no setup.

It's more than a price scraper. Turn on enrichment for a full **product-intelligence feed**:
**nutrition (per 100 g/ml and per serving), allergens, ingredients**, storage and description —
plus optional **customer reviews** and **related products**. Track prices over time with
built-in **price-change monitoring**, or pull the site-wide **offers/promotions** feed in one
run.

Use it for grocery **price monitoring**, competitor price intelligence, diet & allergen apps,
FMCG/CPG market research, grocery-inflation studies, deal/cashback feeds, or to power a
price-comparison app.

### What can the Morrisons scraper extract?

Every product comes back as one flat JSON record. Core fields (always included):

| Field | Description |
|-------|-------------|
| `sku` | Morrisons retailer product ID (stable identifier) |
| `name` | Product name |
| `url` | Canonical product-page URL |
| `brand` | Brand (e.g. Morrisons, Cravendale, Arla) |
| `price` | Current price in GBP (£) |
| `available` | Whether the product is in stock |
| `pack_size` | Pack size description (e.g. `2.272L`, `6 x 1L`) |
| `unit_price` | Price per unit — `amount` + `basis` (e.g. £0.726 "per litre") |
| `category_path` | Full category breadcrumb |
| `promotions` / `promo_price` | Active offers and the "now" price on deals |
| `rating` | Average rating + review count |
| `image_url` / `images` | Primary image, plus **all** product images |
| `image_resolutions` | Every image size available (e.g. `100x100` … `1280x1280`) |
| `product_id` | Internal product UUID |

**Optional add-ons:** **enrichment** (`enrichProducts`) adds a `details` object with Morrisons
**nutrition, allergens, ingredients**, storage and description; **reviews** (`fetchReviews`) and
**related products** (`fetchRelated`) add their own fields. See the sections below.

### Run modes — search, categories & offers

- **Search** (default) — give one or more **search terms** and/or **category URLs**; the
  scraper collects every matching product.
- **Offers** — set `mode` to `offers` for the **site-wide Morrisons promotions feed**: every
  product currently on offer, with was/now prices.

### How to scrape Morrisons – step by step

1. Click **Try for free** (or add the actor to your Apify account).
2. Enter **search terms** (e.g. `milk`, `bread`) and/or paste **category URLs** from the
   Morrisons site — or switch **Mode** to **Offers** for all current promotions.
3. *(Optional)* tick **Enrich with product detail** for nutrition/allergens/ingredients,
   **Fetch customer reviews**, **Fetch related products**, or **Monitor price changes**.
4. *(Optional)* set **Max items**; choose your **proxy** (Apify datacenter works by default).
5. Click **Start**, then download results as **JSON, CSV, Excel** or via the **API**.

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `mode` | string | `search` | `search` (terms/categories) or `offers` (all promotions) |
| `searchTerms` | string[] | `["milk","bread"]` | Terms to search; all matching products collected |
| `categoryUrls` | string[] | — | Morrisons category page URLs to crawl in full |
| `offerCategories` | string[] | — | *Offers mode:* restrict the feed to named categories |
| `enrichProducts` | boolean | `false` | Add nutrition, allergens, ingredients, storage & description |
| `fetchReviews` | boolean | `false` | Add customer reviews (rating histogram + up to 25 newest) |
| `fetchRelated` | boolean | `false` | Add similar/related product IDs |
| `monitorPrices` | boolean | `false` | Emit a price-change record when a price moves vs the previous run |
| `maxItems` | integer | `0` (unlimited) | Stop after this many unique products |
| `proxyConfiguration` | object | Apify datacenter | Proxy settings |

```json
{
  "searchTerms": ["milk", "free range eggs"],
  "categoryUrls": ["https://groceries.morrisons.com/categories/bakery-cakes/5998e059-ce69-48d4-b0db-2f773459dcdf"],
  "enrichProducts": true,
  "fetchReviews": true,
  "maxItems": 1000,
  "proxyConfiguration": { "useApifyProxy": true }
}
````

### Output example

One row per product, deduplicated by SKU across all your search terms and categories. With
`enrichProducts` + `fetchReviews` on, a record looks like this:

```json
{
  "sku": "113240422",
  "name": "Morrisons British Semi Skimmed Milk 4 Pint",
  "url": "https://groceries.morrisons.com/products/morrisons-british-semi-skimmed-milk-4-pint/113240422",
  "price": "1.65",
  "available": true,
  "brand": "Morrisons",
  "pack_size": "2.272L",
  "unit_price": { "amount": "0.726", "basis": "per litre" },
  "category_path": ["Fresh & Chilled", "Milk, Eggs & Butter", "Fresh Milk"],
  "rating": { "value": "3.2", "count": 20 },
  "images": ["https://groceries.morrisons.com/images-v3/.../500x500.jpg"],
  "image_resolutions": ["100x100", "300x300", "500x500", "1280x1280"],
  "details": {
    "ingredients": "Semi Skimmed Milk",
    "allergens": ["Milk"],
    "nutrition_per_100": [
      { "name": "Energy", "value": "209kJ/50kcal" },
      { "name": "Fat", "value": "1.8", "unit": "g" },
      { "name": "Protein", "value": "3.6", "unit": "g" }
    ],
    "storage": "Keep refrigerated. Use within 3 days of opening."
  },
  "reviews": {
    "count": 17,
    "rating_histogram": [6, 2, 3, 0, 9],
    "items": [
      { "rating": 5, "headline": "Great milk", "nickname": "Sam",
        "created_date": "2026-05-08T21:47:16Z", "comments": "Always fresh", "is_verified_buyer": true }
    ]
  }
}
```

### Morrisons price monitoring & tracking

Turn on **Monitor price changes** and schedule the actor as a daily task. Each run compares
every product's price to the previous run and emits a **price-change record** (old price, new
price, delta, % change, direction) into the same dataset — so you build a running history of
**Morrisons price changes** automatically. The first run sets a baseline; later runs surface
only what moved. Ideal for repricing tools, deal alerts and grocery-inflation tracking.

### Nutrition, allergens & ingredients (product enrichment)

Tick **Enrich with product detail** to add a `details` object to every product with the
deeper data: **nutrition** (per 100 g/ml and, where available, per serving), **allergens**,
**ingredients**, storage instructions, full description and brand/manufacturer information.
Perfect for diet, allergen and meal-planning apps, and for compliance or nutrition research.

> **Tip:** enrichment works best with the **Residential** proxy — set
> `proxyConfiguration` to Residential when you turn it on. *(Note: Morrisons does not provide
> EAN/GTIN barcodes, so those are not available.)*

### Customer reviews & related products

- **Fetch customer reviews** adds a `reviews` object: total review count, a 1★–5★ histogram,
  and up to 25 of the newest reviews (text, rating, author, date, verified-buyer flag).
- **Fetch related products** adds `related_product_ids` — the similar/related products Morrisons
  shows alongside each item, for recommendation and product-graph use-cases.

### How much does it cost to scrape Morrisons?

This actor is **pay-per-result**: you're charged a small amount per product delivered, and
nothing for empty searches. Runs are fast and efficient — typically thousands of products in
seconds. Optional enrichment and reviews are priced as add-ons (only charged when data is
actually returned), so price-only runs stay on the cheapest tier. You also pay Apify's standard
platform usage. See the **Pricing** tab for current rates.

### Why this scraper

- **Fast & cost-efficient** — thousands of products in seconds, billed only for results.
- **Product-intelligence, not just prices** — nutrition, allergens, ingredients, reviews,
  related products, offers and price history, all from one actor.
- **Complete, accurate records** — clean, deduplicated data with no half-filled or duplicate rows.
- **Reliable runs** — resumable, with a clear summary every run.
- **Actively maintained** — updated promptly when Morrisons changes their site.

### Use cases

- **Price comparison & cheapest-basket apps** — power a price-comparison site or extension.
- **Price monitoring & competitor price intelligence** — track Morrisons prices and offers daily.
- **Grocery inflation / CPI research** — build a price index over time.
- **Diet, allergen & meal-planning apps** — nutrition, allergens and ingredients at scale.
- **FMCG / CPG market research & catalogue building** — structured category and brand datasets.
- **Deal, promotion & cashback feeds** — the site-wide offers feed with was/now prices.
- **Reviews & sentiment analysis** — customer reviews per product.

### Frequently asked questions

**Is it legal to scrape Morrisons?** You are responsible for using scraped data in line with
Morrisons' terms, applicable laws and data-protection rules. This actor collects only public
product information — no personal data.

**Does Morrisons have a public API?** No — Morrisons offers no official developer API, so this
actor is the simplest way to get Morrisons product data in a structured, export-ready form.

**Can I get Morrisons nutrition, allergens and ingredients?** Yes — enable **Enrich with
product detail**. Note that EAN/GTIN barcodes are not available from Morrisons.

**Can I scrape Morrisons offers, promotions and deals?** Yes — set **Mode** to **Offers** for
the site-wide promotions feed with was/now prices.

**Can I monitor Morrisons price changes?** Yes — enable **Monitor price changes** and schedule
the actor; it emits a price-change record whenever a price moves between runs.

**Can I scrape Morrisons product reviews?** Yes — enable **Fetch customer reviews**.

**Can I scrape a whole Morrisons category?** Yes — paste one or more **category URLs** to collect
every product in those categories.

**Does it need a proxy?** Apify datacenter proxy works for prices, offers, categories, reviews
and related products, and is the cheapest default. **Product enrichment (nutrition/allergens/
ingredients) needs Residential proxy** — switch the proxy to Residential when you enable it.

**What output formats are supported?** Download as JSON, CSV, Excel, or pull from the Apify API
/ dataset.

### Other UK supermarket scrapers

More UK grocery scrapers (Tesco, Asda, Sainsbury's, Waitrose) are planned — check the author's
profile for the latest.

### Support

Found a bug or need a field added? Open an issue on the actor's **Issues** tab. The scraper is
actively maintained and updated when Morrisons changes their site.

# Actor input Schema

## `mode` (type: `string`):

What to scrape. 'Search' (default) runs the search terms and/or category URLs below. 'Offers' ignores those and pulls the site-wide promotions feed — every product currently on a Morrisons offer, with was/now prices.

## `searchTerms` (type: `array`):

Terms to search Morrisons groceries for (one search per term, all result pages followed). Provide this, 'categoryUrls', or both. Duplicates and blanks are ignored.

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

Morrisons category page URLs to crawl in full (every product, all pages). Paste the URL straight from the browser address bar. Provide this, 'searchTerms', or both. Duplicates and blanks are ignored.

## `offerCategories` (type: `array`):

Offers mode only. Restrict the promotions feed to named categories (e.g. 'Frozen Food', 'Meat & Fish'), matched case-insensitively against each product's category. Leave empty for every offer. Unknown names are ignored.

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

Stop after this many unique products are saved to the dataset across all terms. 0 means no limit.

## `maxPagesPerTerm` (type: `integer`):

Cap the number of result pages fetched per search term and per category (each page is up to 300 products). 0 means all pages.

## `enrichProducts` (type: `boolean`):

Fetch the full product-detail panel for every product saved — nutrition (per 100g/ml and per serving), allergens, ingredients, storage, full description and brand info. Adds one request per product, so it is slower and costs more; leave off for price-only runs. IMPORTANT: this needs Residential proxy — Morrisons product pages block datacenter IPs, so set the proxy below to Residential or 'details' will come back empty. (EAN/GTIN is not published by Morrisons, so it is not included.)

## `fetchReviews` (type: `boolean`):

For every product saved, fetch its customer reviews — rating histogram plus up to 100 newest reviews (text, author, date, verified-buyer flag). Adds one request per product, so it is slower and costs more. Off by default.

## `fetchRelated` (type: `boolean`):

For every product saved, fetch the IDs of similar and related products (for recommendation / product-graph use-cases). Adds up to two lightweight requests per product. Off by default.

## `monitorPrices` (type: `boolean`):

Compare each product's price to the previous run and emit a change record (old price, new price, delta, direction) when it moves. Prices are stored by SKU between runs in a named key-value store — this needs a saved, scheduled task to be useful (the first run only records a baseline).

## `maxProductsToDecorate` (type: `integer`):

How many of the (up to 300) results per page come back fully detailed. 300 (default) returns everything in one request — leave it unless you have a reason to lower it.

## `blockFailureThreshold` (type: `integer`):

Abort the whole run if this percentage of attempted terms is blocked (after a small sample). 100 = only stop when essentially everything is blocked.

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

Apify Proxy settings. Datacenter is the default (cheapest). If you see widespread blocking, switch to Residential.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "milk",
    "bread",
    "free range eggs"
  ],
  "categoryUrls": [
    "https://groceries.morrisons.com/categories/bakery-cakes/5998e059-ce69-48d4-b0db-2f773459dcdf"
  ],
  "offerCategories": [
    "Frozen Food",
    "Meat & Fish"
  ],
  "maxItems": 0,
  "maxPagesPerTerm": 0,
  "enrichProducts": false,
  "fetchReviews": false,
  "fetchRelated": false,
  "monitorPrices": false,
  "maxProductsToDecorate": 300,
  "blockFailureThreshold": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchTerms": [
        "milk",
        "bread"
    ],
    "categoryUrls": [],
    "offerCategories": [],
    "maxItems": 0,
    "maxPagesPerTerm": 0,
    "maxProductsToDecorate": 300,
    "blockFailureThreshold": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("bobcodgodkid.1/morrisons-grocery-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 = {
    "searchTerms": [
        "milk",
        "bread",
    ],
    "categoryUrls": [],
    "offerCategories": [],
    "maxItems": 0,
    "maxPagesPerTerm": 0,
    "maxProductsToDecorate": 300,
    "blockFailureThreshold": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("bobcodgodkid.1/morrisons-grocery-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 '{
  "searchTerms": [
    "milk",
    "bread"
  ],
  "categoryUrls": [],
  "offerCategories": [],
  "maxItems": 0,
  "maxPagesPerTerm": 0,
  "maxProductsToDecorate": 300,
  "blockFailureThreshold": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call bobcodgodkid.1/morrisons-grocery-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Morrisons UK Grocery Scraper",
        "description": "Scrape Morrisons UK groceries by search term: export prices, availability, unit prices, pack sizes, promotions, ratings, categories and images as JSON, CSV or Excel. Fast JSON-API scraper for price monitoring, competitor intelligence and product data.",
        "version": "0.1",
        "x-build-id": "3cbSE6MHeiSKuJE4f"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/bobcodgodkid.1~morrisons-grocery-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-bobcodgodkid.1-morrisons-grocery-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/bobcodgodkid.1~morrisons-grocery-scraper/runs": {
            "post": {
                "operationId": "runs-sync-bobcodgodkid.1-morrisons-grocery-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/bobcodgodkid.1~morrisons-grocery-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-bobcodgodkid.1-morrisons-grocery-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": {
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "search",
                            "offers"
                        ],
                        "type": "string",
                        "description": "What to scrape. 'Search' (default) runs the search terms and/or category URLs below. 'Offers' ignores those and pulls the site-wide promotions feed — every product currently on a Morrisons offer, with was/now prices.",
                        "default": "search"
                    },
                    "searchTerms": {
                        "title": "Search terms",
                        "type": "array",
                        "description": "Terms to search Morrisons groceries for (one search per term, all result pages followed). Provide this, 'categoryUrls', or both. Duplicates and blanks are ignored.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "categoryUrls": {
                        "title": "Category URLs",
                        "type": "array",
                        "description": "Morrisons category page URLs to crawl in full (every product, all pages). Paste the URL straight from the browser address bar. Provide this, 'searchTerms', or both. Duplicates and blanks are ignored.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "offerCategories": {
                        "title": "Offer categories",
                        "type": "array",
                        "description": "Offers mode only. Restrict the promotions feed to named categories (e.g. 'Frozen Food', 'Meat & Fish'), matched case-insensitively against each product's category. Leave empty for every offer. Unknown names are ignored.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxItems": {
                        "title": "Max items",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Stop after this many unique products are saved to the dataset across all terms. 0 means no limit.",
                        "default": 0
                    },
                    "maxPagesPerTerm": {
                        "title": "Max pages per term/category",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Cap the number of result pages fetched per search term and per category (each page is up to 300 products). 0 means all pages.",
                        "default": 0
                    },
                    "enrichProducts": {
                        "title": "Enrich with product detail",
                        "type": "boolean",
                        "description": "Fetch the full product-detail panel for every product saved — nutrition (per 100g/ml and per serving), allergens, ingredients, storage, full description and brand info. Adds one request per product, so it is slower and costs more; leave off for price-only runs. IMPORTANT: this needs Residential proxy — Morrisons product pages block datacenter IPs, so set the proxy below to Residential or 'details' will come back empty. (EAN/GTIN is not published by Morrisons, so it is not included.)",
                        "default": false
                    },
                    "fetchReviews": {
                        "title": "Fetch customer reviews",
                        "type": "boolean",
                        "description": "For every product saved, fetch its customer reviews — rating histogram plus up to 100 newest reviews (text, author, date, verified-buyer flag). Adds one request per product, so it is slower and costs more. Off by default.",
                        "default": false
                    },
                    "fetchRelated": {
                        "title": "Fetch related products",
                        "type": "boolean",
                        "description": "For every product saved, fetch the IDs of similar and related products (for recommendation / product-graph use-cases). Adds up to two lightweight requests per product. Off by default.",
                        "default": false
                    },
                    "monitorPrices": {
                        "title": "Monitor price changes",
                        "type": "boolean",
                        "description": "Compare each product's price to the previous run and emit a change record (old price, new price, delta, direction) when it moves. Prices are stored by SKU between runs in a named key-value store — this needs a saved, scheduled task to be useful (the first run only records a baseline).",
                        "default": false
                    },
                    "maxProductsToDecorate": {
                        "title": "Products decorated per page",
                        "minimum": 1,
                        "maximum": 300,
                        "type": "integer",
                        "description": "How many of the (up to 300) results per page come back fully detailed. 300 (default) returns everything in one request — leave it unless you have a reason to lower it.",
                        "default": 300
                    },
                    "blockFailureThreshold": {
                        "title": "Block failure threshold",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Abort the whole run if this percentage of attempted terms is blocked (after a small sample). 100 = only stop when essentially everything is blocked.",
                        "default": 100
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Apify Proxy settings. Datacenter is the default (cheapest). If you see widespread blocking, switch to Residential.",
                        "default": {
                            "useApifyProxy": 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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
