# Marks & Spencer Scraper - Products, Prices, Stock & Reviews (`abotapi/marksandspencer-scraper`) Actor

Collect marksandspencer.com (M\&S) products and individual reviews from keyword searches, categories or product links. Get prices, all colour galleries, size-level inventory, composition, care, ratings, and review responses, with filters, sorting, resume and recurring updates.

- **URL**: https://apify.com/abotapi/marksandspencer-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 product or review records

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Marks & Spencer Products and Reviews Scraper

Collect public product and review data from Marks & Spencer (marksandspencer.com).
Search by keywords, browse a category, or paste product and search links.
Save product prices, colour galleries, size-level inventory, composition and care information.
Choose individual reviews when you need review text, ratings, recommendations and retailer responses.

### Why This Scraper?

- Search terms, category browsing and multiple pasted links.
- Department, brand, colour, size, price and additional store filters.
- Six product sorts and four review sorts.
- Full descriptions and galleries across available colour variants.
- Individual size SKUs with the inventory and prices published by the store.
- Resume an earlier collection or collect recurring changes.
- Export saved results to your connected apps.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live product.

| Field | Example |
|---|---|
| `recordType` | `product` or `review` |
| `id` | `P00000001` |
| `title` | `Sample cashmere jumper` |
| `brand` | `Sample Brand` |
| `price` | `50` |
| `previousPrice` | `60` when published |
| `currency` | `GBP` |
| `unitPrice` | Price per published quantity and unit |
| `averageRating` | `4.5` |
| `reviewCount` | `10` |
| `images` | Gallery links from available colours |
| `variants` | Colour variants, images, size SKUs and inventory |
| `colours` | `["Navy", "Black"]` |
| `sizes` | `["Small", "Medium"]` |
| `composition` | `Sample fabric composition` |
| `careInstructions` | `["Sample care instruction"]` |
| `attributes` | Complete published product attributes |
| `promotions` | Published offer text and terms |
| `reviewText` | `Sample review text` |
| `secondaryRatings` | Published fit, quality and value scores |
| `clientResponses` | Published retailer review responses |
| `productId` | Product identifier shared by product and review rows |
| `changeType` | `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED` or `EXPIRED` |

Fields vary by product category and output type. Missing source values remain empty or null.
Product details add the complete published attributes and colour/size structures.
A separate sub-brand is not inferred from the main brand.
Review rows retain the public reviewer nickname and published syndication and verification information.

### How to Use

#### Search for products

The default search is cashmere jumper. Max items counts saved rows across the whole run.

```json
{
  "mode": "search",
  "searchTerms": ["cashmere jumper"],
  "maxItems": 20
}
```

#### Apply product filters

Use exact store filter values. Additional filters accept one `Property=value` pair per entry.

```json
{
  "mode": "search",
  "searchTerms": ["cashmere jumper"],
  "department": "Womens",
  "brands": ["Autograph"],
  "minPrice": 50,
  "maxPrice": 100,
  "sortBy": "price_low",
  "maxItems": 20
}
```

#### Browse a category

A filled category replaces search terms. Product filters and sorting still apply.

```json
{
  "mode": "search",
  "category": "/l/women/knitwear",
  "sortBy": "new",
  "fetchDetails": true,
  "maxItems": 20
}
```

#### Collect individual reviews from links

URL mode uses only pasted links for discovery. Search-mode fields are ignored.
Products, category links and keyword search links can share the same URL list.
A search or category link preserves its filters, ordering and starting page.

```json
{
  "mode": "url",
  "urls": ["https://www.marksandspencer.com/pure-cashmere-crew-neck-jumper/p/clp61223565"],
  "outputType": "reviews",
  "reviewSort": "recent",
  "reviewRatings": ["4", "5"],
  "maxReviews": 20
}
```

#### Limits and charges

Every saved product or individual review is one result item.
Products and reviews use separate budgets: Max items caps product rows, Max reviews caps review rows.
With Products and reviews selected, both record types are saved and billed independently.
Reviews output saves review rows without a separate product row.
`maxReviewsPerProduct` can limit reviews for each product; blank or 0 has no separate limit.
`maxPages` is optional; blank or 0 walks until the source ends or Max reviews is reached.

Enriched product rows add one `detail-enrichment` event per successfully saved product.
Individual reviews add one `review-scraped` event per saved review row, in addition to the saved-item event.
Skipped products, failed enrichment and suppressed recurring rows do not add a detail charge.
Expired rows are result items but do not add a detail charge.
See the actor pricing panel for the active prices.

### Input Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | Choose keyword search or pasted links. Only the inputs in the selected mode are used. |
| `searchTerms` | array | `["cashmere jumper"]` | Search each term separately. Defaults to cashmere jumper. Ignored when category is filled or mode is url. |
| `department` | string | `Optional` | Optional clothing department: Womens or Mens. Applies the store department filter in search mode only. |
| `category` | string | `Optional` | Optional category path, category link or SubCategory identifier. Browse this category instead of search terms. Search filters and sort still apply. |
| `brands` | array | `Optional` | Optional exact brand names shown in the store filters, such as Autograph or JAEGER. Search mode only. |
| `colours` | array | `Optional` | Optional colour filter names, such as Navy or Black. Search mode only. |
| `sizes` | array | `Optional` | Optional exact size filter labels, such as S, M or 10. Search mode only. |
| `facet` | array | `Optional` | Optional native filter pairs, one per line, such as Fit=Regular fit or Neck Type=Crew neck. Use the exact filter name and value shown for your search. Search mode only. |
| `minPrice` | integer | `Optional` | Optional minimum price in GBP. Search mode only. |
| `maxPrice` | integer | `Optional` | Optional maximum price in GBP. Search mode only. |
| `minRating` | integer | `Optional` | Optional minimum average product rating from 0 to 5. Search mode only. |
| `sortBy` | string | `relevance` | Use the store ordering in search mode. URL mode preserves the ordering specified in each link. |
| `urls` | array | `["https://www.marksandspencer.com/pure-cashmere-crew-neck-jumper/p/clp61223565"]` | Paste one or more M\&S product, category or search links. Defaults to an example product. Search-mode fields are ignored. Results begin at the page specified in each link. |
| `outputType` | string | `products` | Products saves product rows. Reviews saves individual review rows. Products and reviews saves both, joined by productId. Saved products count toward Max items; saved reviews count toward Max reviews, a separate budget. |
| `fetchDetails` | boolean | `true` | Add full descriptions, composition, care, all colour galleries and size-level inventory. Each successfully saved enriched product adds one detail-enrichment charge. Reviews are separate saved items. Suppressed, skipped and failed rows do not add this charge. |
| `reviewSort` | string | `recent` | Ordering for individual review rows in Reviews or Products and reviews output. |
| `reviewRatings` | array | `Optional` | Optional exact star ratings to include, from 1 to 5. Empty includes all ratings. Applies to review output only. |
| `maxReviewsPerProduct` | integer | `Optional` | Optional limit on individual review rows per product. Blank or 0 collects all available reviews for that product until Max reviews is reached. No separate default per-product cap. |
| `maxPages` | integer | `Optional` | Optional limit on result or review pages per source. Blank or 0 means unlimited pages until the source ends or the relevant budget is reached. No default page cap. |
| `maxItems` | integer | `20` | Maximum saved product rows across the run, including recurring update rows. Default 20. Set 0 for unlimited products. Reviews use the separate Max reviews budget and never count against this. |
| `maxReviews` | integer | `20` | Maximum saved review rows across the run, independent of Max items. Default 20. Set 0 for unlimited reviews (still bounded by Maximum reviews per product and Maximum pages per source, if set). |
| `resumeFromRunId` | string | `Optional` | Optional previous run or dataset ID. Skip previously saved product and review IDs. Combine datasets to continue a collection. Resumed runs do not emit expired rows. For recurring collection, use incrementalMode instead. |
| `incrementalMode` | boolean | `false` | Remember this exact collection scope between runs. Return NEW, UPDATED or REAPPEARED rows. Use stateKey to name the collection, emitUnchanged to include unchanged rows and emitExpired for missing rows. |
| `stateKey` | string | `Optional` | Optional name for a recurring collection. The selected mode, terms or links, filters, detail setting and review options also identify its baseline. Different scopes never share a baseline. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also save unchanged products or reviews marked UNCHANGED. Each saved row is charged as an item; saved enriched products can add the detail charge. |
| `emitExpired` | boolean | `false` | Incremental mode only. Save EXPIRED rows after a complete collection. No expiration after a cap, resumed run or incomplete source. Expired rows count as saved items and do not add a detail charge. |
| `proxy` | object | `Apify proxy` | Standard Apify connection configuration. |
| `mcpConnectors` | array | `Optional` | Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a page per record; other connectors get a best-effort write or digest. Each connector receives a condensed summary per record, not the full record; the complete record always stays in the dataset. Leave empty to skip; this never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com). |
| `notionParentPageUrl` | string | `Optional` | URL or id of the Notion page under which record pages are created. Required to enable the Notion export; ignored by other connectors. |
| `maxNotifyListings` | integer | `50` | Maximum saved items to send to each selected connector. Does not limit or change the dataset. |

#### Resume and recurring collections

Use `resumeFromRunId` to skip IDs already saved in an earlier run or dataset.
This is useful for collecting the next part of a large result set with another run.
The new dataset contains only the rows collected by that run.

Use `incrementalMode` for recurring collection of the same scope.
The first run saves NEW rows; later runs save UPDATED or REAPPEARED rows when relevant.
With `emitUnchanged` enabled, unchanged rows are also saved and billed as items.
Use `stateKey` to name your collection. Different filters or output settings keep separate baselines.

Missing rows are emitted only with `emitExpired` after a complete collection.
A capped, resumed or incomplete run cannot establish that an unseen product or review disappeared.
A source changing its published price, inventory, review text or other collected data can produce an update.
Collection timestamps do not themselves produce an update.

### Send results into your apps (MCP connectors)

Select optional `mcpConnectors` to send copies of saved results to your connected apps.
Authorize connectors in Apify Settings, API & Integrations, before selecting them.
Notion, Linear, Airtable and Apify connectors are supported.
For Notion, supply `notionParentPageUrl` for the destination page.
`maxNotifyListings` limits how many saved rows are sent to each connector.
This export does not alter or limit your dataset.
A connector error leaves the saved scrape results available.
Leave the connector list empty to skip export.

### Output Example

> Sample shape: values are illustrative placeholders, not from a live product.

```json
{
  "recordType": "product",
  "id": "P00000001",
  "productId": "P00000001",
  "title": "Sample cashmere jumper",
  "brand": "Sample Brand",
  "url": "https://www.marksandspencer.com/sample-product/p/clp00000001",
  "productUrl": "https://www.marksandspencer.com/sample-product/p/clp00000001",
  "description": "Sample full product description.",
  "price": 50,
  "previousPrice": 60,
  "currency": "GBP",
  "averageRating": 4.5,
  "reviewCount": 10,
  "images": ["https://assets.digitalcontent.marksandspencer.app/image/upload/00000001"],
  "colours": ["Navy"],
  "sizes": ["Small"],
  "composition": "Sample fabric composition",
  "careInstructions": ["Sample care instruction"],
  "variants": [
    {
      "id": "00000001",
      "skus": [
        {
          "id": "00000001001",
          "size": {"primarySize": "Small"},
          "inventory": {"quantity": 10, "quantityOnHand": 10, "quantityAdvised": 0}
        }
      ]
    }
  ],
  "attributes": {"sampleAttribute": "Sample value"},
  "fetchedDetails": true,
  "changeType": "NEW",
  "changedFields": [],
  "firstSeenAt": "2026-01-01T00:00:00.000Z",
  "lastSeenAt": "2026-01-01T00:00:00.000Z"
}
```

Individual review rows have `recordType: "review"`, a review ID, `productId`,
`productUrl`, review text, rating, public nickname, submission time and the other published review fields.
The default Results view includes headline fields for both record types.
Products and Reviews views offer dedicated columns. All fields remain available in dataset JSON.

### Plan Requirement

An Apify account is required. Consult the actor pricing panel before starting a run.

# Actor input Schema

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

Choose keyword search or pasted links. Only the inputs in the selected mode are used.

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

Search each term separately. Defaults to cashmere jumper. Ignored when category is filled or mode is url.

## `department` (type: `string`):

Optional clothing department: Womens or Mens. Applies the store department filter in search mode only.

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

Optional category path, category link or SubCategory identifier. Browse this category instead of search terms. Search filters and sort still apply.

## `brands` (type: `array`):

Optional exact brand names shown in the store filters, such as Autograph or JAEGER. Search mode only.

## `colours` (type: `array`):

Optional colour filter names, such as Navy or Black. Search mode only.

## `sizes` (type: `array`):

Optional exact size filter labels, such as S, M or 10. Search mode only.

## `facet` (type: `array`):

Optional native filter pairs, one per line, such as Fit=Regular fit or Neck Type=Crew neck. Use the exact filter name and value shown for your search. Search mode only.

## `minPrice` (type: `integer`):

Optional minimum price in GBP. Search mode only.

## `maxPrice` (type: `integer`):

Optional maximum price in GBP. Search mode only.

## `minRating` (type: `integer`):

Optional minimum average product rating from 0 to 5. Search mode only.

## `sortBy` (type: `string`):

Use the store ordering in search mode. URL mode preserves the ordering specified in each link.

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

Paste one or more M\&S product, category or search links. Defaults to an example product. Search-mode fields are ignored. Results begin at the page specified in each link.

## `outputType` (type: `string`):

Products saves product rows. Reviews saves individual review rows. Products and reviews saves both, joined by productId. Saved products count toward Max items; saved reviews count toward Max reviews, a separate budget.

## `fetchDetails` (type: `boolean`):

Add full descriptions, composition, care, all colour galleries and size-level inventory. Each successfully saved enriched product adds one detail-enrichment charge. Reviews are separate saved items. Suppressed, skipped and failed rows do not add this charge.

## `reviewSort` (type: `string`):

Ordering for individual review rows in Reviews or Products and reviews output.

## `reviewRatings` (type: `array`):

Optional exact star ratings to include, from 1 to 5. Empty includes all ratings. Applies to review output only.

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

Optional limit on individual review rows per product. Blank or 0 collects all available reviews for that product until Max reviews is reached. No separate default per-product cap.

## `maxPages` (type: `integer`):

Optional limit on result or review pages per source. Blank or 0 means unlimited pages until the source ends or Max items is reached. No default page cap.

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

Maximum saved product rows across the run, including recurring update rows. Default 20. Set 0 for unlimited products. Reviews use the separate Max reviews budget and never count against this.

## `maxReviews` (type: `integer`):

Maximum saved review rows across the run, independent of Max items. Default 20. Set 0 for unlimited reviews (still bounded by Maximum reviews per product and Maximum pages per source, if set).

## `resumeFromRunId` (type: `string`):

Optional previous run or dataset ID. Skip previously saved product and review IDs. Combine datasets to continue a collection. Resumed runs do not emit expired rows. For recurring collection, use incrementalMode instead.

## `incrementalMode` (type: `boolean`):

Remember this exact collection scope between runs. Return NEW, UPDATED or REAPPEARED rows. Use stateKey to name the collection, emitUnchanged to include unchanged rows and emitExpired for missing rows.

## `stateKey` (type: `string`):

Optional name for a recurring collection. The selected mode, terms or links, filters, detail setting and review options also identify its baseline. Different scopes never share a baseline.

## `emitUnchanged` (type: `boolean`):

Incremental mode only. Also save unchanged products or reviews marked UNCHANGED. Each saved row is charged as an item; saved enriched products can add the detail charge.

## `emitExpired` (type: `boolean`):

Incremental mode only. Save EXPIRED rows after a complete collection. No expiration after a cap, resumed run or incomplete source. Expired rows count as saved items and do not add a detail charge.

## `proxy` (type: `object`):

Standard Apify connection configuration.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a page per record; other connectors get a best-effort write or digest. Each connector receives a condensed summary per record, not the full record; the complete record always stays in the dataset. Leave empty to skip; this never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which record pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Maximum saved items to send to each selected connector. Does not limit or change the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "cashmere jumper"
  ],
  "sortBy": "relevance",
  "urls": [
    "https://www.marksandspencer.com/pure-cashmere-crew-neck-jumper/p/clp61223565"
  ],
  "outputType": "products",
  "fetchDetails": true,
  "reviewSort": "recent",
  "maxItems": 20,
  "maxReviews": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

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

No description

## `products` (type: `string`):

No description

## `reviews` (type: `string`):

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "searchTerms": [
        "cashmere jumper"
    ],
    "urls": [
        "https://www.marksandspencer.com/pure-cashmere-crew-neck-jumper/p/clp61223565"
    ],
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/marksandspencer-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": ["cashmere jumper"],
    "urls": ["https://www.marksandspencer.com/pure-cashmere-crew-neck-jumper/p/clp61223565"],
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/marksandspencer-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 '{
  "searchTerms": [
    "cashmere jumper"
  ],
  "urls": [
    "https://www.marksandspencer.com/pure-cashmere-crew-neck-jumper/p/clp61223565"
  ],
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/marksandspencer-scraper --silent --output-dataset

```

## MCP server setup

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