# Lookfantastic Scraper - UK Beauty Prices, EAN and Stock (`studio-amba/lookfantastic-scraper`) Actor

Scrape product data from lookfantastic.com, the UK online beauty retailer. Get EAN barcodes, current and list prices, discounts, stock, ratings and review counts for every size and shade of skincare, makeup, haircare and fragrance.

- **URL**: https://apify.com/studio-amba/lookfantastic-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?

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

## Lookfantastic Scraper

Pull product data from lookfantastic.com, the UK online beauty retailer. The actor returns one row per size or shade with the EAN barcode, current price, list price, discount, stock status, rating and review count.

Lookfantastic sells skincare, makeup, haircare, fragrance, body care and men's grooming from several thousand brands. Every row carries the EAN, so you can match a Lookfantastic listing to the same product at Boots, Douglas, Notino or any other shop.

### How to scrape Lookfantastic data

There are three ways to tell the actor which products to read.

1. Paste product pages into `productUrls`, for example `https://www.lookfantastic.com/p/jean-paul-gaultier-le-male-eau-de-toilette-75ml/10021723/`.
2. Paste category or brand pages into `categoryUrls`. Only addresses starting with `/c/` work, for example `https://www.lookfantastic.com/c/brands/jean-paul-gaultier/`.
3. Leave both empty and fill `urlKeywords`. The actor reads the shop's public product list and keeps products whose name contains one of your words, for example `serum` or `armani`.

The actor stops at `maxResults`. If you leave everything empty, it reads the first 50 products in the product list.

Search result pages and filter or sort addresses are not read. The site's robots.txt does not allow them, so the actor does not request them.

### Input

| Field | Type | Description |
|---|---|---|
| `productUrls` | list of URLs | Lookfantastic product pages |
| `categoryUrls` | list of URLs | Category or brand pages starting with `/c/`, without filters or sorting |
| `urlKeywords` | list of words | Used when no URLs are given. Keeps products whose name contains one of the words |
| `maxResults` | number | Maximum number of rows (default 50) |
| `proxyConfiguration` | proxy | Apify Proxy by default |

A product with three sizes gives three rows, because each size has its own EAN and price.

### What you get back

| Field | Description |
|---|---|
| `productId` | Lookfantastic product number from the page address |
| `sku` | Variant number, also the number after `?variation=` in `url` |
| `ean` | EAN/GTIN barcode of this size or shade |
| `name` | Product name including size |
| `brand` | Brand name |
| `variant` | Size or shade, empty when the product has only one |
| `price` | Current selling price in GBP |
| `originalPrice` | Recommended retail price, only when the selling price is lower |
| `discountPercent` | Percent below the list price |
| `currency` | ISO currency code (GBP) |
| `pricePerUnit` | Price per litre or kilogram as shown on the site |
| `inStock` | Whether this variant is in stock |
| `availability` | Dispatch message from the page |
| `rating` | Average review score out of 5 |
| `reviewCount` | Number of reviews on the product |
| `category` | Category path on the site |
| `imageUrl` | Main product image |
| `url` | Product page address for this variant |
| `scrapedAt` | Time of the read |
| `country`, `language` | `uk`, `en` |

Rating and review count belong to the whole product, so all sizes of one product share them.

### Example output

```json
{
  "productId": "10021723",
  "sku": "10021723",
  "ean": "8435415012638",
  "name": "Jean Paul Gaultier Le Male Eau de Toilette 75ml",
  "brand": "Jean Paul Gaultier",
  "variant": "75ml",
  "price": 59.2,
  "originalPrice": 74,
  "discountPercent": 20,
  "currency": "GBP",
  "pricePerUnit": "£789.33 / L",
  "inStock": true,
  "availability": "In stock | Usually dispatched within 24 hours",
  "rating": 4.69,
  "reviewCount": 2365,
  "category": "Brands/Jean Paul Gaultier Fragrance",
  "imageUrl": "https://static.thcdn.com/productimg/original/10021723-3685321818541013.jpg",
  "url": "https://www.lookfantastic.com/p/jean-paul-gaultier-le-male-eau-de-toilette-75ml/10021723/?variation=10021723",
  "scrapedAt": "2026-10-29T15:04:35.605Z",
  "country": "uk",
  "language": "en"
}
```

### Use cases

- Compare Lookfantastic prices to other shops by EAN.
- Track list price and discount percent on a brand over time.
- Check which sizes of a brand are in stock.
- Build a product catalogue with barcodes, images and ratings.
- Monitor review counts and ratings for your own brand.

### Cost and speed

The actor reads one product page per product, about 0.7 MB each, with four requests at the same time. Twenty products take well under a minute. Pricing is per result, see the pricing box on this page. The usage cost of a run only settles after the run reports SUCCEEDED, so a cost checked during the run is too low.

### Notes and limits

- Prices are in GBP for the UK storefront. Other country storefronts are not covered.
- The product list on the site has about 17,800 products. A keyword filter reads only the matching ones.
- Some pages do not show variant data, such as discontinued products. These are skipped and logged.
- The actor reads publicly visible pages only. It does not log in and does not use cookies.
- You are responsible for how you use the data. Check Lookfantastic's terms and the rules that apply to your use case.

### Frequently asked questions

#### Does it return EAN barcodes?

Yes. The `ean` field holds the barcode of each size or shade, taken from the product page.

#### Can I search by keyword?

Yes, with `urlKeywords`. The actor filters the product list by name. It does not use the site's search page, because the site's robots.txt does not allow it.

#### Why are there more rows than products?

Each size or shade is its own row, because each has its own EAN and price.

#### Can I scrape a whole brand?

Paste the brand page, for example `https://www.lookfantastic.com/c/brands/armani/`, into `categoryUrls`.

#### How often can I run it?

As often as you like. Daily runs on a fixed list of product pages give a clean price history.

### Related scrapers

- [Notino Scraper](https://apify.com/studio-amba/notino-scraper) for Notino beauty prices
- [Douglas DE Scraper](https://apify.com/studio-amba/douglas-de-scraper) for Douglas Germany
- [Boots Scraper](https://apify.com/studio-amba/boots-scraper) for Boots UK
- [Sephora Scraper](https://apify.com/studio-amba/sephora-scraper) for Sephora France and Spain
- [dm Scraper](https://apify.com/studio-amba/dm-scraper) for dm drugstore products

# Actor input Schema

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

Product pages from lookfantastic.com, e.g. https://www.lookfantastic.com/p/jean-paul-gaultier-le-male-eau-de-toilette-75ml/10021723/. Each size or shade of a product becomes one row.

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

Category pages from lookfantastic.com starting with /c/, e.g. https://www.lookfantastic.com/c/brands/jean-paul-gaultier/. Paste the URL without filters or sorting. Search pages are not supported.

## `urlKeywords` (type: `array`):

Used when no product or category URLs are given. The actor reads the shop's product list and keeps products whose name contains one of these words, e.g. serum or armani. Leave empty to read the first products in the list.

## `maxResults` (type: `integer`):

Maximum number of rows (one row per size or shade).

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

Proxy used for the requests. Default is Apify Proxy.

## Actor input object example

```json
{
  "urlKeywords": [
    "serum"
  ],
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "urlKeywords": [
        "serum"
    ],
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/lookfantastic-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 = {
    "urlKeywords": ["serum"],
    "maxResults": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/lookfantastic-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 '{
  "urlKeywords": [
    "serum"
  ],
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call studio-amba/lookfantastic-scraper --silent --output-dataset

```

## MCP server setup

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