# Fragrantica Reviews Scraper (`thescrapelab/fragrantica-reviews-scraper`) Actor

Collect raw perfume reviews from Fragrantica with source dates, authors and reviewer votes. Private anonymous-access pilot; historical coverage can require login.

- **URL**: https://apify.com/thescrapelab/fragrantica-reviews-scraper.md
- **Developed by:** [Inus Grobler](https://apify.com/thescrapelab) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 reviews

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Fragrantica Reviews Scraper

Export raw perfume reviews from Fragrantica: text, author, dates, perfume details and the reviewer’s source-published votes. Use the records for fragrance watchlists, launch research and your own analysis.

**Private pilot. Recent reviews have been collected in manual Apify tests. Anonymous access does not establish complete historical coverage.**

### Quick start

1. Paste one perfume page or review link per line in **Perfume URLs**.
2. Choose **Maximum reviews**, or keep the default of 10.
3. Optionally enter **From date**, such as `2026-10-01`.

```json
{
  "startUrls": ["https://www.fragrantica.com/perfume/Dior/Sauvage-31861.html"],
  "maxReviews": 10
}
```

The limit applies across all URLs, processed in their input order. The date filter is inclusive and compares `publishedDate`, whose source is explained below. Original review links select that particular review, including older reviews when available.

### Coverage

The Actor reads the recent reviews embedded in a perfume page and follows the source’s real anonymous continuation when more records are needed. Cloud verification collected 200 recent reviews plus 100 older reviews on a large perfume, followed by an explicit login requirement. Other perfume pages can fit entirely inside this anonymous window. This does not establish complete historical coverage. Some source pages have also returned access challenges; this pilot remains private while archive access and reliability are being resolved.

Set `maxReviews` to `0` to attempt full history. If Fragrantica requires login, the run reports incomplete coverage and retains saved reviews. A guest-limited window is never labelled a complete archive. The pilot uses anonymous requests and an automatic browser fallback. It does not configure paid proxies or accept account cookies.

Supported inputs are English-edition perfume pages and their original review links. Review text can be in any language. Brand/search pages, other editions and the site-wide feed are outside this private pilot.

### Output

One flat row per unique `(sourceEdition, perfumeId, reviewId)`. Fields include review text, perfume name/brand/URL, review ID and source link, public author/profile, publication date, raw display time, available reviewer votes and collection time. JSON and tabular exports are available from the dataset.

Selected fields from a source-verified sample:

```json
{
  "reviewId": "3267087",
  "perfumeId": "31861",
  "perfumeName": "Sauvage",
  "brand": "Dior",
  "reviewText": "at cebe lazım olur",
  "publishedDate": "2026-11-02",
  "publishedAt": "2026-11-03T07:08:52Z",
  "reviewerRating": 5,
  "reviewerLongevity": 0,
  "reviewerSillage": 0
}
```

`publishedDate` preserves the source calendar date for perfume-page reviews. For records returned by the archive endpoint (`collectionPath: "perfume-archive"`), it uses the UTC day derived from the source Unix timestamp because that response does not supply the HTML calendar date. `publishedAt` is UTC only where an explicit Unix timestamp is available; otherwise it stays null. An HTML calendar date can differ from the UTC date. `publishedAtRaw` preserves the source timestamp or display time, with its precision in `datePrecision`. The optional From date filter compares these documented `publishedDate` values; it does not convert them to a guessed reviewer timezone.

Reviewer votes describe the source’s current values beside the review, not perfume-wide averages or verified votes at publication. Source zero values remain zero; unavailable values remain null. Helpful counts are included only where published. Original text is preserved without translation, AI sentiment or inferred demographics.

### Partial results and errors

The **Collection summary** output records confirmed dataset writes, source errors and per-target stopping reasons. A requested result cap is not a historical-completeness claim. A confirmed empty date filter differs from an inaccessible page.

Blocked pages, rate limits, missing requested reviews and login requirements are reported explicitly. Any successfully saved reviews remain in the dataset; error objects are not emitted as reviews. Start with ten results to check a target before requesting more.

### Pricing

Pricing is being configured for this private beta: **$0.002 per saved review** ($2 per 1,000), plus **$0.01 per allocated GB when a run starts**. At the supported 2 GB setting, the start charge is $0.02. Platform usage is included. There is no subscription fee.

For example, 10 saved reviews cost $0.04; 100 cost $0.22; 1,000 would cost $2.02 if that many source reviews are accessible. These are price examples, not complete-history availability claims. The start charge also applies when a run returns no reviews or fails; saved partial results incur their normal review charges.

The minimum maximum-charge setting is $0.03, which is a spending allowance rather than a minimum invoice. Increase the run’s maximum charge for larger collections. Collection stops at the requested review count or spending limit and reports partial output when the budget prevents more results. A spending limit never proves archive completeness.

### Python API

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("thescrapelab/fragrantica-reviews-scraper").call(run_input={
    "startUrls": ["https://www.fragrantica.com/perfume/Dior/Sauvage-31861.html"],
    "maxReviews": 10,
})
for review in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(review)
```

The Actor is private and accessible only to authorized account users. Public publication and official Apify daily QA have not been established.

# Actor input Schema

## `startUrls` (type: `array`):

Paste one perfume page or review link per line, such as fragrantica.com/perfume/Dior/Sauvage-31861.html. English edition only.

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

Maximum reviews across all URLs. Default: 10. Use 0 to attempt full history; if Fragrantica requires login the run reports incomplete output.

## `sinceDate` (type: `string`):

Only include reviews dated on or after this day, for example 2026-10-01. Leave blank for any date.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.fragrantica.com/perfume/Dior/Sauvage-31861.html"
  ],
  "maxReviews": 10
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "startUrls": [
        "https://www.fragrantica.com/perfume/Dior/Sauvage-31861.html"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thescrapelab/fragrantica-reviews-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 = { "startUrls": ["https://www.fragrantica.com/perfume/Dior/Sauvage-31861.html"] }

# Run the Actor and wait for it to finish
run = client.actor("thescrapelab/fragrantica-reviews-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 '{
  "startUrls": [
    "https://www.fragrantica.com/perfume/Dior/Sauvage-31861.html"
  ]
}' |
apify call thescrapelab/fragrantica-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thescrapelab/fragrantica-reviews-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/cE9fYs9bXz82Rmzcx/builds/FixYqh3kvROdhdI8S/openapi.json
