# Sephora Scraper — Beauty Prices in 6 European Countries (`studio-amba/sephora-scraper`) Actor

Scrape product data from Sephora France and Spain. Get names, brands, prices, discounts, price per unit, ratings, review counts, stock flag and listing rank for perfume, make-up, skincare and hair.

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

## Sephora Scraper

Pull beauty product data from the Sephora storefronts in France and Spain. The actor returns names, brands, prices, promotions, unit prices, ratings, review counts, a stock flag and the listing rank for any Sephora category page.

Sephora sells perfume, make-up, skincare, body care and hair products from its own Sephora Collection line and from several hundred other brands. One record shape covers every department and both countries, so the same run settings give comparable rows across markets.

### How to scrape Sephora data

Choose a `country`, then either pick `departments` or paste category page URLs into `categoryUrls`. The actor pages through each category until it reaches `maxResults` or the end of the listing.

Sephora blocks plain requests. This actor sends every request through Bright Data Web Unlocker, so you do not need your own proxy or browser setup.

Each category page carries the product data inside the page itself, 24 products per page. The actor reads that data directly, which keeps the cost per product low.

If you leave `departments` and `categoryUrls` empty, the actor scrapes the hair department of the chosen country.

To find a category URL, open the country site, go down to the category you want, and copy the address from the browser. The address ends in `-c` and a number, for example `https://www.sephora.fr/shop/cheveux-c307/`. Remove any filter, sort or page parameters after the question mark.

### Countries

| Country input | Site | Currency |
|---|---|---|
| `fr` | sephora.fr | EUR |
| `es` | sephora.es | EUR |

Department and category IDs are the same on every site. The hair department is C307 in both countries, make-up is C302 and perfume is C301. Only the URL path is translated.

### Input

| Field | Type | Description |
|---|---|---|
| `country` | select | `fr` or `es` (default `fr`) |
| `departments` | list | `perfume`, `makeup`, `face`, `body`, `hair`. Spain has no `body` department |
| `categoryUrls` | list of URLs | Category pages from the chosen country site, without filters or sorting |
| `maxResults` | number | Maximum number of products across all categories (default 100) |
| `brightDataApiKey` | text | Optional. Falls back to the actor's own key when empty |

Search pages, filter parameters and sort parameters are not supported. The actor only reads category pages.

### What you get back

Every product is one record. Fields with no value on the site are left out of the record.

| Field | Description |
|---|---|
| `productId` | Sephora master product ID, for example `P10047623` |
| `name` | Product name |
| `brand` | Brand name |
| `description` | Short descriptor shown under the name |
| `variantName` | Size or shade of the default variant |
| `price` | Current price in the local currency |
| `currency` | `EUR` |
| `originalPrice` | Price before the promotion, only on reduced products |
| `discountPercent` | Promotion percentage, only on reduced products |
| `priorPrice` | Lowest recent price when the site shows one |
| `pricePerUnit` | Unit price text such as `4,80 EUR / 100ml` |
| `available` | Whether the default variant can be ordered |
| `rating` | Average review rating out of 5 |
| `reviewCount` | Number of reviews |
| `variantsCount` | Number of sizes or shades |
| `sku` | Sephora SKU of the default variant |
| `tags` | Badges such as New, Deal or Exclusive |
| `imageUrl` | Main product image |
| `url` | Product page |
| `categoryId` | Numeric category ID from the URL |
| `categoryUrl` | Listing page the product came from |
| `rank` | Position in the category's default best-seller order, starting at 1 |
| `page` | Listing page number |
| `country` | Country code |
| `language` | Storefront language |
| `scrapedAt` | ISO timestamp |

Sephora does not publish an EAN or GTIN on its listing or product pages, and it does not name a seller, so the actor has neither field.

### Example output

```json
{
  "productId": "P1000210636",
  "name": "The Mista",
  "brand": "FENTY HAIR",
  "price": 22.95,
  "currency": "EUR",
  "originalPrice": 23.45,
  "discountPercent": 2,
  "pricePerUnit": "255,00 EUR / 1L",
  "available": true,
  "rating": 4.5,
  "reviewCount": 655,
  "rank": 17,
  "country": "fr",
  "url": "https://www.sephora.fr/p/the-mista---duftendes-haar--und-koerperspray-P1000210636.html"
}
```

### Use cases

- Compare the price of the same brand across the two Sephora countries.
- Track promotions and discount depth on a brand's range over time.
- Follow the best-seller rank of a product inside its category.
- Monitor ratings and review counts for a brand against its competitors.
- Build a price index for a beauty category.

### Pricing and cost

The actor is billed per result. A run's usage cost only settles after the run reaches SUCCEEDED, so the final charge shows up once the run has finished.

One request returns 24 products, so a 1,000 product run uses about 42 requests.

### Run time

A category page takes a few seconds to load through Bright Data. A run of 100 products takes about one minute. Sephora hair has about 1,000 products per country, which takes under ten minutes.

If a page is blocked, the actor retries it up to four times. If the page is still lost, the run fails instead of finishing with a partial catalogue.

### Limits

- Category pages only. Keyword search is not supported.
- The rank is the position in the default best-seller order of the category page, not a search rank.
- Prices are the public online prices for the chosen country. Store prices and loyalty prices are not included.
- A product that appears in several categories is returned once per run.
- Sephora can change its page format. The actor logs a warning when fewer than 80 percent of the products on a page can be read.

### Integrations

Results can be exported as JSON, CSV or Excel from the Apify console, read through the Apify API, or sent to Google Sheets, Make, Zapier and webhooks.

### FAQ

**Which country sites are covered?** France and Spain.

**Can I scrape several countries in one run?** One run covers one country. Start one run per country and merge the datasets with `country` and `productId`.

**Does the actor need a login?** No. All data comes from public pages.

**Why is `originalPrice` missing on most products?** It is only present when Sephora shows a reduction on the product.

**Is the stock level available?** The actor returns whether the default variant can be ordered. Sephora does not publish a quantity in the listing.

**Is scraping Sephora legal?** The actor only reads public pages that do not require a login, and it stays away from the paths that Sephora's robots.txt disallows. You are responsible for how you use the data.

# Actor input Schema

## `country` (type: `string`):

Which Sephora storefront to scrape. Prices are in the local currency (EUR).

## `departments` (type: `array`):

Top-level departments to scrape: perfume, makeup, face, body, hair. Spain has no body department. Leave empty together with Category URLs to scrape hair.

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

Sephora category pages on the chosen country site, copied from the browser, for example "https://www.sephora.fr/shop/cheveux-c307/". The URL must end in -c and a number. Filters and search pages are not supported.

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

Maximum number of products across all categories. A category page holds 24 products.

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

Not used. This actor reaches Sephora through Bright Data Web Unlocker. Kept for input-shape compatibility.

## `brightDataZone` (type: `string`):

Name of the Web Unlocker zone in your Bright Data account. Only needed together with your own Bright Data API key. Leave empty to use the actor's own zone.

## `brightDataApiKey` (type: `string`):

Bright Data API key used to reach Sephora. Falls back to the actor's own BRIGHT_DATA_API_KEY environment variable if left empty.

## Actor input object example

```json
{
  "country": "fr",
  "departments": [
    "hair"
  ],
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "brightDataZone": "web_unlocker1"
}
```

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

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

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

```

## MCP server setup

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