# MR PORTER Scraper — Menswear Prices, Stock & Style Codes (`studio-amba/mrporter-scraper`) Actor

Scrape MR PORTER for designer menswear: clothing, shoes and accessories with live prices, real warehouse stock counts, category tree, images and the manufacturer style code (mfPartNumber) you can join on across retailers. 20+ markets, no login, no proxy needed.

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

## Pricing

from $1.20 / 1,000 result scrapeds

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

## MR PORTER Scraper

Browse MR PORTER by category or designer and get back structured menswear
product data: designer, price, live warehouse stock count, category tree,
images, and the manufacturer's own style code.

### Why use this actor

Most luxury scrapers hand you a name and a price. The useful part of this one
is `mfPartNumber`, the brand's own style code, which lets you recognise the
same physical garment when it appears on a different retailer's site.

If you are price-monitoring, building a resale pricing tool, or maintaining a
product database across several luxury retailers, that code is the difference
between "two listings that look similar" and "the same item, two prices".

### What makes the style code matter

Luxury has no EAN. Groceries and electronics have a barcode that every
retailer shares, so joining catalogues is trivial. Fashion does not. Each
retailer invents its own SKU, and the same jacket has a different internal ID
at every shop. There is nothing to join on.

MR PORTER publishes the manufacturer's code. Example: `RV19T0P55.02KK02KK` on
a CELINE bomber jacket. That is CELINE's own style code, the one printed on
the hangtag, not a MR PORTER-internal number.

### How to scrape MR PORTER data

1. Put one or more category or designer paths in **Categories**:
   `/clothing`, `/shoes`, `/accessories`, `/designer/gucci`,
   `/designer/tom-ford`.
2. Pick a **Market**. `mrp_be` is Belgium in EUR; there are 21 markets and
   each has its own prices and stock.
3. Set **Max Items** and run it.

No login, no cookies to supply, and no proxy. The actor does the anti-bot
work itself.

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `categories` | Array | No | One or more category/designer paths. Default `["/clothing"]` |
| `category` | String | No | Single-path alternative to `categories` |
| `storeCode` | String | No | Market to read, default `mrp_be`. See the market list below |
| `maxItems` | Integer | No | Stop after this many products across all categories. Default 50 |
| `pageSize` | Integer | No | Products per category request, default 60. 200 is the verified ceiling |
| `proxyConfiguration` | Object | No | Ignored on purpose, see Limitations |

#### Markets

`mrp_be` (Belgium), `mrp_nl`, `mrp_fr`, `mrp_de`, `mrp_it`, `mrp_es`,
`mrp_at`, `mrp_ie`, `mrp_dk`, `mrp_se`, `mrp_ch`, `mrp_pl`, `mrp_cz`,
`mrp_gb` (GBP), `mrp_us` (USD), `mrp_ca`, `mrp_au`, `mrp_hk`, `mrp_jp`,
`mrp_kr`, `mrp_ae`.

#### Category and designer paths

Top-level categories: `/clothing`, `/shoes`, `/accessories`, `/sport`,
`/home-and-tech`, `/grooming`, `/luxury-watches`, `/fine-jewelry`,
`/kidswear`, `/sale`. Designer pages follow `/designer/<brand-slug>`, e.g.
`/designer/gucci`, `/designer/tom-ford`, `/designer/the-row`. Sub-categories
work too, e.g. `/clothing/blazers`, `/shoes/sneakers`.

### Output

| Field | Type | Example |
|-------|------|---------|
| `name` | String | `"Shell Bomber Jacket"` |
| `brand` | String | `"CELINE"` |
| `mfPartNumber` | String | `"RV19T0P55.02KK02KK"` |
| `price` | Number | `2500` |
| `currency` | String | `"EUR"` |
| `originalPrice` | Number | Only when the item is actually reduced |
| `discount` | String | `"-30%"`, only when reduced |
| `sizes` | Array | The brand's size scale for this colourway (schema, not per-size stock — see Limitations) |
| `inStock` | Boolean or null | `true`/`false` from a live stock count, `null` when unreadable |
| `stockLevel` | Number | Live unit count, when the listing carries one |
| `stockBadges` | Array | `["Sold out"]` when `inStock` is `false`, empty otherwise |
| `category` | Array | `["Clothing", "Coats and Jackets", "Bomber jackets"]` |
| `categoryPath` | String | `"Clothing > Coats and Jackets > Bomber jackets"` |
| `colour` | String | `"Green"` |
| `imageUrl` | String | Main image at 1000px |
| `imageUrls` | Array | All available views |
| `url` | String | Product page on the chosen market |
| `productId` | String | MR PORTER's internal ID |
| `partNumber` | String | Variant code from the product URL |
| `storeCode` | String | `"mrp_be"` |
| `categorySlug` | String | The requested path that returned this product |
| `scrapedAt` | String | ISO 8601 timestamp |

#### Prices are converted for you

MR PORTER's own API quotes money in minor units with a divisor
(`{"amount": 250000, "divisor": 100}`). The `price` and `originalPrice`
fields are already converted to major units, so `2500` means EUR 2500.00.
The divisor is read from the response rather than assumed, so markets that
do not use cents come out right too.

#### Stock is a live count, not a per-size ladder

MR PORTER's listing API always reports `buyable: false` on every row
regardless of real availability — that flag only means something on a
detail record, and the detail endpoint is blocked (see Limitations). What
the listing DOES carry, reliably, is a live warehouse inventory field.
`inStock` and `stockLevel` are read from that field: `true`/a number when a
count exists, `null` when the row carries no readable count (never coerced
to `false` — an unknown is reported as unknown, not as sold out).

### Example output

```json
{
    "name": "Shell Bomber Jacket",
    "brand": "CELINE",
    "mfPartNumber": "RV19T0P55.02KK02KK",
    "price": 2500,
    "currency": "EUR",
    "sizes": [
        { "size": "40" },
        { "size": "42" },
        { "size": "44" },
        { "size": "46" }
    ],
    "inStock": true,
    "stockLevel": 5,
    "stockBadges": [],
    "category": ["Clothing", "Coats and Jackets", "Bomber jackets"],
    "categoryPath": "Clothing > Coats and Jackets > Bomber jackets",
    "colour": "Green",
    "imageUrl": "https://www.mrporter.com/variants/images/46376663163121003/in/w1000.jpg",
    "imageUrls": [
        "https://www.mrporter.com/variants/images/46376663163121003/in/w1000.jpg",
        "https://www.mrporter.com/variants/images/46376663163121003/ou/w1000.jpg"
    ],
    "url": "https://www.mrporter.com/en-be/mens/celine/clothing/bomber-jackets/shell-bomber-jacket/46376663163121003",
    "productId": "3074457345626529346",
    "partNumber": "46376663163121003",
    "storeCode": "mrp_be",
    "categorySlug": "/clothing",
    "scrapedAt": "2026-09-08T19:44:48.637Z"
}
```

### Speed and cost

The run starts with a one-off browser warm-up of about 7-10 seconds. After
that, one category page is one request regardless of how many products it
returns (up to 200 per request), so a run is fast: a measured 12 products in
10.6 seconds including warm-up, and a 200-item single-category run is one
API call plus warm-up.

No proxy is used, so there is no proxy cost. Cost per result on the Store
is charged per item written to the dataset (see the actor's pricing tab for
the current rate) — a `maxItems: 50` run costs roughly 50x one result, plus
one flat per-run start fee. Usage cost only settles once a run reports
SUCCEEDED; reading the dataset mid-run will undercount what the run will
end up costing.

### Limitations

- **No free-text search.** MR PORTER's search API path is blocked at the
  network edge (measured HTTP 403 "Access Denied" with a fully warmed cookie
  jar and valid client credentials — this is a WAF rule, not a missing
  cookie). Browse by category or designer path instead of a search term.
- **No per-size stock ladder.** The detail endpoint that would carry
  per-size availability is blocked the same way. `sizes` lists the brand's
  size scale (every size the item is cut in), not which sizes are actually
  in stock. Use `inStock`/`stockLevel` for real availability at the product
  level.
- **Leave the proxy off.** A direct connection is what gets past this
  site's Akamai protection. The setting is honoured if you turn it on, but
  it is an escape hatch for a burned IP range, not a default.
- **The occasional run needs a second attempt.** MR PORTER's protection
  scores the exit IP, and consecutive runs from the same range do not
  always get the same answer. The actor retries the browser warm-up up to
  four times with a fresh fingerprint each time, so this normally costs you
  10 to 20 seconds rather than a failed run.
- **One colourway is one record.** A jacket in three colours returns three
  records with three different style codes, which is correct: each
  colourway has its own manufacturer code.
- **MR PORTER is largely a full-price retailer.** `originalPrice` and
  `discount` are usually absent outside sale periods.
- Data is scraped from the public website and may change without notice.
  Respect the website's terms of service and use responsibly.

### Related scrapers

- [NET-A-PORTER Scraper](https://apify.com/studio-amba/net-a-porter-scraper) —
  the womenswear sibling site, same YNAP group and commerce platform.
- [Mytheresa Scraper](https://apify.com/studio-amba/mytheresa-scraper) —
  another big European full-price luxury retailer.
- [Vinted Scraper](https://apify.com/studio-amba/vinted-scraper) — Europe's
  largest secondhand fashion marketplace. Pair it with this actor to compare
  retail against resale.
- [Rituals Scraper](https://apify.com/studio-amba/rituals-scraper) — luxury
  cosmetics and home fragrance.
- [ICI PARIS XL Scraper](https://apify.com/studio-amba/iciparisxl-scraper) —
  Belgian beauty and perfume.

### Need this data on a schedule, or a custom version?

We run this scraper as a managed service for businesses: scheduled runs,
deduplication, delta detection, and delivery to your inbox, Google Sheets,
or API — maintenance included. We can also build a custom version with your
exact fields and filters, or combine multiple luxury sources into one feed
joined on the manufacturer style code.

# Actor input Schema

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

One or more MR PORTER category or designer paths, e.g. '/clothing', '/shoes', '/accessories', '/designer/gucci'. MR PORTER's free-text search API is blocked at the network edge (see README Limitations), so browsing runs by category/designer path instead of a search term.

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

Convenience alternative to Categories when you only want one path.

## `storeCode` (type: `string`):

Which MR PORTER storefront to read. Prices and stock are per market. Default mrp\_be (Belgium, EUR).

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

Stop after this many products across all categories.

## `pageSize` (type: `integer`):

Products per category API request. 200 is the verified ceiling; higher values are clamped.

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

Leave this OFF. A direct connection is what gets past mrporter.com's Akamai protection; measured 2026-09-08, a direct connection passed the warm-up while a residential exit is unverified and unnecessary. It is here as an escape hatch: if the actor starts failing its warm-up, Akamai may be scoring the outbound IP range badly, and routing the browser through a different exit is the one thing that can fix that.

## Actor input object example

```json
{
  "categories": [
    "/clothing",
    "/designer/gucci"
  ],
  "storeCode": "mrp_be",
  "maxItems": 10,
  "pageSize": 60,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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 = {
    "categories": [
        "/clothing",
        "/designer/gucci"
    ],
    "storeCode": "mrp_be",
    "maxItems": 10,
    "pageSize": 60,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/mrporter-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": [
        "/clothing",
        "/designer/gucci",
    ],
    "storeCode": "mrp_be",
    "maxItems": 10,
    "pageSize": 60,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/mrporter-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 '{
  "categories": [
    "/clothing",
    "/designer/gucci"
  ],
  "storeCode": "mrp_be",
  "maxItems": 10,
  "pageSize": 60,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call studio-amba/mrporter-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/mrporter-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/8vS8HtbEl7fzPzQKz/builds/4MCHSU2YlMfgffhqZ/openapi.json
