# Trustpilot Reviews Scraper — Company Reviews & Trust Scores (`mfapitools/trustpilot-reviews-scraper`) Actor

Scrape public reviews from Trustpilot by company domain. Parses the **NEXT\_DATA** JSON blob for reliability. Includes company profile (trust score, category, review count). Pay-per-event pricing — see the Pricing section for current per-review rates.

- **URL**: https://apify.com/mfapitools/trustpilot-reviews-scraper.md
- **Developed by:** [Mariano Ferreras](https://apify.com/mfapitools) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 80.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 review scrapeds

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/platform/actors/running/actors-in-store#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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## Trustpilot Reviews Scraper — Pay-Per-Review

> **Pay-per-event pricing.** This is a competitive market — top PPE Trustpilot scrapers charge $0.0005–$0.00075/review with 400–1,400 users each. We charge $0.003/review. What you get: PlaywrightCrawler-based extraction (works through Trustpilot's AWS WAF), post-parse filters (star rating, language, verified, replies), `sinceDate` incremental runs, and `__NEXT_DATA__` parsing that survives minor layout changes. We are not the cheapest option — pick us if you value the filter features and the approach over raw price.

The most important limitation: Trustpilot's public UI caps pagination at 20 pages, and robots.txt disallows query-parameter filtering — we filter reviews post-parse within this bound.

---

### What it does

This is a **Trustpilot reviews scraper** that extracts public customer reviews from `www.trustpilot.com/review/<domain>` for any list of company domains. It uses a **real Chromium browser** (PlaywrightCrawler) to render Trustpilot's AWS WAF JS challenge, then parses the `__NEXT_DATA__` JSON blob embedded in Trustpilot's Next.js SSR pages — a more robust approach than CSS selectors that survives minor HTML changes.

For each company you get:
- All publicly visible reviews (up to the pagination cap — see Limitations)
- Ratings, review text, verified status, reply detection, reviewer display name (as publicly shown)
- Optional company profile record with trust score, review count, and category
- `sinceDate` filter for incremental runs — fetch only new reviews since your last run
- Two record types unified in one dataset: `review` and `company-profile`

If you're looking for a **Trustpilot API** without Trustpilot's rate limits and auth requirements, this gives you structured JSON with the same fields — ratings, review bodies, replies, verification flags, trust scores — for any public company profile on Trustpilot, scheduled or on-demand.

---

### Competitive landscape (July 2026)

The Trustpilot scraper market is a crowded field (700+ actors). Top PPE competitors:

| Actor | Users | Price/review | Features |
|-------|-------|-------------|----------|
| memo23/trustpilot-scraper-ppe | 1,365 | $0.00075 (tiered) — but a **$0.45 per-run minimum charge** | AI pain-point analysis, no pagination cap |
| automation-lab/trustpilot | 1,444 | $0.000575 (tiered) + $0.005 start | Highest user count |
| agents/trustpilot-reviews | 412 | $0.0005/review | 5★ rating |
| zen-studio/trustpilot-review-scraper | 427 | $0.00199/review | Star rating, language, verified filters |

Compare total run cost, not just the headline per-review rate. memo23's $0.45 per-run minimum means a 100-review run costs $0.45 there — more than it costs here — even though its per-review price is lower than ours. Its price advantage only materialises on large runs.

**What they do better:** Lower per-review price, far higher user counts and review volume.
**What we do better:** Playwright-based extraction (survives AWS WAF changes), post-parse filters without robots.txt violations, `sinceDate` incremental mode.

---

### Use cases

- **Reputation monitoring** — track your brand's Trustpilot reviews in real time; alert when new 1-star reviews appear or when reply rate drops below a threshold.
- **Competitive intelligence** — monitor competitors' review trends, rating trajectories, and response rates across any time window.
- **CX analytics** — aggregate customer sentiment data from Trustpilot into your NLP/sentiment pipeline, CRM, or BI dashboard without manual exports.
- **Review benchmarking** — compare trust scores, review volumes, and star distributions across an entire industry or product category.
- **Vendor due diligence** — screen suppliers, SaaS vendors, or partners against their Trustpilot reputation before procurement decisions.
- **Automated alerting** — run on a schedule with `sinceDate` set to yesterday; pipe new reviews to Slack, email, or a ticketing system.

---

### Input

```json
{
  "companyDomains": ["amazon.com", "apple.com"],
  "maxReviewsPerCompany": 100,
  "sinceDate": "2026-07-01",
  "sortBy": "recency",
  "starRatings": [1, 2],
  "languages": ["en"],
  "verifiedOnly": false,
  "withRepliesOnly": false,
  "includeCompanyProfile": true,
  "useResidentialProxy": true
}
````

| Field | Type | Default | Description |
|---|---|---|---|
| `companyDomains` | string\[] | `[]` | Company domains to scrape. Protocols and `www.` are stripped automatically. |
| `startUrls` | URL\[] | `[]` | Direct Trustpilot review page URLs (alternative or supplement to `companyDomains`). |
| `maxReviewsPerCompany` | integer | `100` | Max reviews per company **that pass all active filters**. `0` = all available (subject to public cap). |
| `sinceDate` | string | *(none)* | ISO date `YYYY-MM-DD`. Skip reviews older than this date. Ideal for incremental runs. |
| `sortBy` | enum | `"recency"` | `"recency"` (newest first) or `"relevance"` (Trustpilot's algorithm). |
| `starRatings` | integer\[] | `[]` | Only collect reviews with these star ratings (1–5), e.g. `[1, 2]`. Empty = no filtering. See [Filters](#filters). |
| `languages` | string\[] | `[]` | Only collect reviews in these language codes, e.g. `["en", "es"]` (case-insensitive). Empty = no filtering. See [Filters](#filters). |
| `verifiedOnly` | boolean | `false` | Only collect reviews Trustpilot marks as "verified". See [Filters](#filters). |
| `withRepliesOnly` | boolean | `false` | Only collect reviews that have a public company reply. See [Filters](#filters). |
| `includeCompanyProfile` | boolean | `true` | Push a company-profile record with trust score, review count, and category. |
| `useResidentialProxy` | boolean | `false` | **Strongly recommended.** Trustpilot's AWS WAF blocks datacenter IPs. Residential proxies are required for reliable production volume. |
| `zeroYieldAbortThreshold` | integer | `3` | Fast-fail circuit breaker — see [Zero-yield guard](#zero-yield-guard-fast-failure-detection) below. `0` disables it. |
| `maxRunSeconds` | integer | `0` (disabled) | Optional soft deadline (seconds) checked between pages — see below. Leave `0` for complete results. |

***

### Filters

Four independent filters narrow which reviews are collected. All are **off by default** (no filtering — every review that Trustpilot returns is collected, matching prior versions of this actor exactly). Active filters **compose with AND**: a review must pass every filter you set, plus `sinceDate`, to be pushed and charged for.

| Filter | Type | Matches | How it's applied |
|---|---|---|---|
| `starRatings` | integer\[] (1–5) | Review's star rating is in the list | Client-side, after each page is fetched |
| `languages` | string\[] (e.g. `"en"`) | Review's language code is in the list (case-insensitive) | Client-side, after each page is fetched |
| `verifiedOnly` | boolean | Review is marked "verified" by Trustpilot | Client-side, after each page is fetched |
| `withRepliesOnly` | boolean | Review has a public company reply | Client-side, after each page is fetched |

**Why client-side, not `?stars=`/`?languages=`/`?verified=` URL params:** Trustpilot's review pages do support these as real query parameters — confirmed by fetching `trustpilot.com/robots.txt` directly (no browser needed, returns HTTP 200), which explicitly lists `Disallow: /*?*stars=`, `/*?*languages=`, `/*?*verified=`, and `/*?*replies=` under every named crawler section (Googlebot, bingbot, GPTBot, ClaudeBot, etc.) — robots.txt wouldn't bother disallowing query parameters that don't exist. This actor already avoids every robots.txt-disallowed query param — that's why `sinceDate` above has always been a post-fetch filter rather than the also-disallowed `?date=` param. The new filters follow the same rule: only `?page=N` and `?sort=` are ever sent; `starRatings`/`languages`/`verifiedOnly`/`withRepliesOnly` are all applied after each page is parsed.

**Cost implication:** narrow filters (e.g. `starRatings: [1]` on a mostly 5-star company) still page through every review to find matches, so a filtered run can visit more pages — and cost more in `apify-actor-start` memory-time — than an unfiltered run for the same `maxReviewsPerCompany`. You are only ever **charged `review-scraped` for reviews that actually pass your filters** and get pushed to the dataset; filtered-out reviews are never charged.

**Example — 1★ and 2★ English reviews only:**

```json
{
  "companyDomains": ["amazon.com"],
  "maxReviewsPerCompany": 50,
  "starRatings": [1, 2],
  "languages": ["en"]
}
```

**Example — verified reviews with a company reply, incremental since last run:**

```json
{
  "companyDomains": ["amazon.com"],
  "sinceDate": "2026-07-01",
  "verifiedOnly": true,
  "withRepliesOnly": true
}
```

***

### Output

The dataset contains two record types, distinguished by the `type` field.

#### Review record (`type: "review"`)

```json
{
  "type": "review",
  "companyDomain": "amazon.com",
  "reviewId": "review_abc123",
  "rating": 5,
  "title": "Excellent service and fast delivery",
  "text": "I ordered a laptop and it arrived the next day...",
  "language": "en",
  "reviewDate": "2026-07-20",
  "replyPresent": false,
  "replyText": null,
  "reviewerDisplayName": "John S.",
  "verified": true,
  "sourceUrl": "https://www.trustpilot.com/review/amazon.com"
}
```

| Field | Type | Notes |
|---|---|---|
| `type` | `"review"` | Record type discriminator |
| `companyDomain` | string | Domain used to look up this company |
| `reviewId` | string | Trustpilot internal review ID |
| `rating` | integer (1–5) | Star rating |
| `title` | string | Review title |
| `text` | string | Full review body text |
| `language` | string|null | Language code as reported by Trustpilot for this review (e.g. `"en"`), or `null` if not provided. Used by the `languages` filter — see [Filters](#filters). |
| `reviewDate` | string | ISO date `YYYY-MM-DD` of when the review was published |
| `replyPresent` | boolean | Whether the company posted a public reply |
| `replyText` | string|null | Company reply text when present and public |
| `reviewerDisplayName` | string | Reviewer's display name **as shown publicly** on Trustpilot (e.g. `"John S."`) |
| `verified` | boolean | Trustpilot verified flag |
| `sourceUrl` | string | URL of the review page scraped |

#### Company profile record (`type: "company-profile"`)

Pushed once per domain when `includeCompanyProfile: true`.

```json
{
  "type": "company-profile",
  "companyDomain": "amazon.com",
  "businessUnitId": "46d804110000ff00059b07fc",
  "displayName": "Amazon",
  "identifyingName": "amazon.com",
  "websiteUrl": "https://www.amazon.com",
  "trustScore": 2.8,
  "stars": 2.8,
  "totalReviews": 58432,
  "category": "shopping_and_fashion-other",
  "claimed": true,
  "sourceUrl": "https://www.trustpilot.com/review/amazon.com"
}
```

| Field | Type | Notes |
|---|---|---|
| `type` | `"company-profile"` | Record type discriminator |
| `companyDomain` | string | Domain used to look up this company |
| `businessUnitId` | string | Trustpilot internal business unit ID |
| `displayName` | string | Company name as shown on Trustpilot |
| `identifyingName` | string | Trustpilot's canonical domain identifier |
| `websiteUrl` | string|null | Company's official website URL |
| `trustScore` | number|null | Trust score (1.0–5.0) |
| `stars` | number|null | Star rating (same scale) |
| `totalReviews` | integer | Total number of reviews on Trustpilot |
| `category` | string|null | Trustpilot category/subcategory ID |
| `claimed` | boolean | Whether the business has claimed its profile |
| `sourceUrl` | string | URL of the company's Trustpilot review page |

***

### Pricing

This actor uses **pay-per-event** pricing — no rental, no subscription. You are never charged for failed requests, WAF-blocked pages, or items that fail schema validation.

| Event | Price | What triggers it |
|---|---|---|
| `review-scraped` | $3.00 / 1,000 ($0.003/ea) | Each schema-valid review pushed to the dataset |
| `company-profile` | $0.10 / 1,000 ($0.0001/ea) | Each company-profile record (opt-in, 1 per company) |
| Actor start | $0.02 | One-time per run (covers 2048 MB memory) |

**Real-world cost examples:**

| Scenario | Reviews | Companies | Est. cost |
|---|---|---|---|
| Quick check (1 company, 50 reviews) | 50 | 1 | ~$0.19 |
| Weekly monitor (3 companies, 100 each) | 300 | 3 | ~$0.92 |
| Monthly deep scan (5 companies, 200 each) | 1,000 | 5 | ~$3.02 |
| Large brand (full history, 200-review cap) | ~200 | 1 | ~$0.62 |

**Measured platform cost:** browser-based scraping + residential proxy costs ~$0.031/run for 5 reviews. The actor-start fee covers this for runs of ~6+ reviews; tiny one-off test runs (1–5 reviews) are slightly loss-making for the developer, covered by larger runs.

**No rental comparison:** rental-priced incumbents charge $20/month base whether you scrape 0 reviews or 10,000. At $0.003/review you'd pay $30 to scrape 10,000 reviews — no base fee, pay only when you actually get data.

***

### Use with AI agents

This actor is available as a tool for AI assistants via the Apify MCP server. Agents can call it to **scrape Trustpilot reviews** directly — no Apify console needed, just a single function call.

#### MCP setup

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp.apify.com?tools=mfapitools/trustpilot-reviews-scraper",
        "--header",
        "Authorization: Bearer <YOUR_API_TOKEN>"
      ]
    }
  }
}
```

#### Example agent prompts

- "Get the most recent 100 reviews for notion.so from Trustpilot"
- "Scrape all 1-star Trustpilot reviews for this company from the last month"
- "Pull Trustpilot trust scores and review counts for these five competitors"

The actor returns structured JSON with the same fields shown above. Agentic workflows (Claude Code, Cursor, any MCP-compatible AI) can fetch Trustpilot reviews in the same run they're analyzing them — no separate tool, no manual export.

***

### Limitations

#### Pagination cap (~200 reviews)

Trustpilot caps public review pagination at approximately **page 20** (at ~20 reviews/page, this yields ~200–400 reviews per company). Beyond page 20, the site either returns empty results or blocks the request — this is consistent with what existing incumbents document in their issue trackers (reported as the "200-review cap"). Companies with millions of reviews can only be partially sampled via the public web interface. This is a platform limitation, not a scraper bug.

**To maximize coverage:** Use `sinceDate` with `sortBy: "recency"` to collect incremental batches across scheduled runs rather than one large pull.

#### AWS WAF / bot protection (browser-based scraping required)

Trustpilot uses AWS WAF that issues JS challenges to all requests that don't pass browser fingerprint verification. Plain HTTP fetchers (curl, CheerioCrawler, any non-browser fetcher) receive "Verifying Connection" pages (HTTP 403). This actor uses **PlaywrightCrawler with headless Chromium** which executes the WAF JS challenge, after which the real Next.js page with `__NEXT_DATA__` is rendered.

**Mitigation for platform runs:** Set `useResidentialProxy: true`. Residential proxies present as end-user IPs. From a local/residential IP, headless Chromium passes the WAF reliably. From Apify datacenter IPs, residential proxies via Apify's RESIDENTIAL proxy group are required. The actor requires ≥2048 MB memory on the platform to run Chrome.

#### Zero-yield guard (fast failure detection)

A page can produce "no data" for three different reasons, and this actor handles each one differently instead of treating them as the same outcome:

| Case | What happened | Counts as a guard failure? | Run outcome |
|---|---|---|---|
| **WAF/challenge block** | The page loaded but no `__NEXT_DATA__` element ever appeared in the DOM (challenge page rendered instead of the real one) | Yes | Contributes to the zero-yield guard below |
| **Parse failure** | `__NEXT_DATA__` was present but the JSON was malformed, or `businessUnit` was missing (Trustpilot changed its page structure) | Yes | Contributes to the zero-yield guard below |
| **Company genuinely has 0 reviews** | The page parsed fine and a real company profile was extracted — it just has zero reviews | **No** | Legitimate result: the company-profile record (if `includeCompanyProfile: true`) is pushed normally and the run exits `0` |

If the **first `zeroYieldAbortThreshold` processed pages** (default `3`) are *all* WAF blocks or parse failures, the actor concludes the target is fully blocked or its page structure changed, aborts the remaining crawl immediately (rather than continuing to burn Playwright/proxy compute against a target that clearly isn't returning data), and exits with `exitCode: 1` and a clear `statusMessage`. This never fires because of a company that legitimately has 0 reviews — a single successful page anywhere in the first N proves the pipeline is working. It also only ever looks at the *start* of the run: a failure streak that begins after an initial run of successful pages does not trip it.

Optionally, `maxRunSeconds` (default `0` = disabled) adds a soft deadline checked between pages. If set and the crawl is still running past it — including the slower case where pages occasionally succeed so the fast guard above never trips — the run stops cleanly with whatever was already collected: exits `0` (with a partial-result `statusMessage`) if any reviews or profiles were pushed, or `1` if nothing was collected at all. It ships disabled so large runs are never silently truncated; enable it on scheduled runs where a strict per-run time budget matters more than completeness.

Additionally, any run that ends with **zero yielded pages and zero items** — even a single-domain run that never reaches the abort threshold — exits `1` with a clear `statusMessage` instead of reporting success with an empty dataset.

Both guards exist specifically so a fully-blocked or structurally-changed target fails fast and loud instead of running to completion and silently returning `SUCCEEDED` with zero items and full compute cost.

#### robots.txt status

Trustpilot's `robots.txt` has `User-agent: * / Disallow: /` (entire site) but allows Googlebot explicitly on `/review/*` paths. This actor scrapes only `/review/<domain>?page=N&sort=` paths — no disallowed paths (`/api/*`, `/users/*`, `/reviews/`, `/search`, `/sessions`) and no disallowed query parameters (`?stars=`, `?languages=`, `?verified=`, `?topics=`, `?replies=`, `?date=`, etc.) are used. Public reviews are factual data published voluntarily by consumers and indexed by search engines.

This is also why the `starRatings`, `languages`, `verifiedOnly`, and `withRepliesOnly` [filters](#filters) above are all applied client-side after fetching rather than via Trustpilot's own `?stars=`/`?languages=`/`?verified=`/`?replies=` query parameters — those parameters work, but they're on the robots.txt disallow list, same as `?date=` (which is why `sinceDate` has always worked the same way).

#### `__NEXT_DATA__` structure changes

Trustpilot is a Next.js SSR application. The `__NEXT_DATA__` JSON structure can change with Trustpilot deployments. Parsers were **validated against real pages** (notion.so, asana.com, 2026-07-23). Key real-world field names that differ from naive API docs:

- `review.rating` (not `.stars`), `review.dates.publishedDate` (not `.createdAt`)
- `review.isPending` (not `.pending`), `review.labels.verification.isVerified` (not `.isVerified`)
- `review.reply` (not `.companyReply`), `businessUnit.isClaimed` (not `.claimed`)
- `businessUnit.numberOfReviews` is a plain number (not `{total: N}`)
- Pagination at `pageProps.filters.pagination` (not `pageProps.pagination` directly)

If the actor starts returning 0 reviews with 0 quarantined items, the `__NEXT_DATA__` structure likely changed. CSS-selector fallback is not implemented.

#### Review count accuracy

The `totalReviews` in company profiles comes from the `__NEXT_DATA__` blob and reflects Trustpilot's displayed count. This may differ slightly from the number of reviews actually accessible via pagination.

***

### Compliance

- **Public data only**: Reviews scraped by this actor are publicly visible on Trustpilot without authentication. No login-gated content is accessed.
- **Reviewer data**: Only the reviewer's **display name as publicly shown** (e.g. `"John S."`) is collected. No consumer IDs, emails, profile URLs, or any other personal data beyond what is visible on the public review page.
- **No profile scraping**: Reviewer profile pages (`/users/*`) are explicitly in Trustpilot's disallow list and are never accessed by this actor.
- **Drop policy**: Per project policy, this actor will be suspended on credible legal complaint from Trustpilot or any party.

***

### Changelog

See [CHANGELOG.md](./CHANGELOG.md).

# Actor input Schema

## `companyDomains` (type: `array`):

List of company domains to scrape reviews for. Example: \["amazon.com", "apple.com"]. Protocols (https://) and www. prefixes are stripped automatically.

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

Direct Trustpilot review page URLs (alternative or supplement to companyDomains). Example: \["https://www.trustpilot.com/review/amazon.com"]

## `maxReviewsPerCompany` (type: `integer`):

Maximum number of reviews to collect per company. 0 = collect all available (subject to the public pagination cap of ~200 reviews). Default: 100.

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

Skip reviews older than this date. ISO format: YYYY-MM-DD. Useful for incremental runs — set to your last run date to fetch only new reviews. Leave blank to collect all available reviews.

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

Sort order for reviews. 'recency' (default) returns newest reviews first — recommended for incremental runs. 'relevance' uses Trustpilot's ranking algorithm.

## `starRatings` (type: `array`):

Only collect reviews with these star ratings. Leave empty to collect all ratings (default). Filtered client-side after fetching, not via Trustpilot's stars= query param — see README for why.

## `languages` (type: `array`):

Only collect reviews written in these language codes, e.g. \["en", "es"]. Matched case-insensitively against Trustpilot's per-review language field. Leave empty to collect all languages (default). Filtered client-side after fetching — see README.

## `verifiedOnly` (type: `boolean`):

Only collect reviews Trustpilot marks as "verified". Default: false (collect all reviews regardless of verification status).

## `withRepliesOnly` (type: `boolean`):

Only collect reviews that have a public company reply. Default: false (collect all reviews).

## `includeCompanyProfile` (type: `boolean`):

Push a company-profile record for each domain with: display name, trust score, total review count, and category. Adds one record per company at the start of the dataset. Default: true.

## `useResidentialProxy` (type: `boolean`):

Use residential proxies (strongly recommended). Trustpilot uses AWS WAF that blocks datacenter IPs. Residential proxies significantly increase success rates. Datacenter proxies may work for small test runs but will be challenged under volume.

## `zeroYieldAbortThreshold` (type: `integer`):

If the first N processed pages ALL fail to produce any parseable data (WAF/challenge block or **NEXT\_DATA** parse failure), the run aborts immediately instead of burning further browser/proxy time. A company with genuinely 0 reviews does NOT count as a failure here. Set to 0 to disable. Default: 3.

## `maxRunSeconds` (type: `integer`):

Optional soft deadline checked between pages. 0 (default) = disabled — runs complete fully. If set, the run stops cleanly at the deadline with whatever was already collected (exit 0 with partial data, exit 1 if nothing). Use for scheduled runs with a strict per-run time budget; leave 0 when you need complete results.

## Actor input object example

```json
{
  "companyDomains": [
    "amazon.com"
  ],
  "maxReviewsPerCompany": 100,
  "sortBy": "recency",
  "starRatings": [],
  "verifiedOnly": false,
  "withRepliesOnly": false,
  "includeCompanyProfile": true,
  "useResidentialProxy": false,
  "zeroYieldAbortThreshold": 3,
  "maxRunSeconds": 0
}
```

# Actor output Schema

## `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 = {
    "companyDomains": [
        "amazon.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mfapitools/trustpilot-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 = { "companyDomains": ["amazon.com"] }

# Run the Actor and wait for it to finish
run = client.actor("mfapitools/trustpilot-reviews-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companyDomains": [
    "amazon.com"
  ]
}' |
apify call mfapitools/trustpilot-reviews-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mfapitools/trustpilot-reviews-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Trustpilot Reviews Scraper — Company Reviews & Trust Scores",
        "description": "Scrape public reviews from Trustpilot by company domain. Parses the __NEXT_DATA__ JSON blob for reliability. Includes company profile (trust score, category, review count). Pay-per-event pricing — see the Pricing section for current per-review rates.",
        "version": "1.1",
        "x-build-id": "Zea9Vr84Mmg7BCzLy"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/mfapitools~trustpilot-reviews-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-mfapitools-trustpilot-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/mfapitools~trustpilot-reviews-scraper/runs": {
            "post": {
                "operationId": "runs-sync-mfapitools-trustpilot-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/mfapitools~trustpilot-reviews-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-mfapitools-trustpilot-reviews-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "companyDomains": {
                        "title": "Company domains",
                        "type": "array",
                        "description": "List of company domains to scrape reviews for. Example: [\"amazon.com\", \"apple.com\"]. Protocols (https://) and www. prefixes are stripped automatically.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "Direct Trustpilot review page URLs (alternative or supplement to companyDomains). Example: [\"https://www.trustpilot.com/review/amazon.com\"]",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "maxReviewsPerCompany": {
                        "title": "Max reviews per company",
                        "minimum": 0,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Maximum number of reviews to collect per company. 0 = collect all available (subject to the public pagination cap of ~200 reviews). Default: 100.",
                        "default": 100
                    },
                    "sinceDate": {
                        "title": "Since date (YYYY-MM-DD)",
                        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                        "type": "string",
                        "description": "Skip reviews older than this date. ISO format: YYYY-MM-DD. Useful for incremental runs — set to your last run date to fetch only new reviews. Leave blank to collect all available reviews."
                    },
                    "sortBy": {
                        "title": "Sort order",
                        "enum": [
                            "recency",
                            "relevance"
                        ],
                        "type": "string",
                        "description": "Sort order for reviews. 'recency' (default) returns newest reviews first — recommended for incremental runs. 'relevance' uses Trustpilot's ranking algorithm.",
                        "default": "recency"
                    },
                    "starRatings": {
                        "title": "Star ratings",
                        "type": "array",
                        "description": "Only collect reviews with these star ratings. Leave empty to collect all ratings (default). Filtered client-side after fetching, not via Trustpilot's stars= query param — see README for why.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "1",
                                "2",
                                "3",
                                "4",
                                "5"
                            ],
                            "enumTitles": [
                                "1 star",
                                "2 stars",
                                "3 stars",
                                "4 stars",
                                "5 stars"
                            ]
                        },
                        "default": []
                    },
                    "languages": {
                        "title": "Languages",
                        "type": "array",
                        "description": "Only collect reviews written in these language codes, e.g. [\"en\", \"es\"]. Matched case-insensitively against Trustpilot's per-review language field. Leave empty to collect all languages (default). Filtered client-side after fetching — see README.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "verifiedOnly": {
                        "title": "Verified reviews only",
                        "type": "boolean",
                        "description": "Only collect reviews Trustpilot marks as \"verified\". Default: false (collect all reviews regardless of verification status).",
                        "default": false
                    },
                    "withRepliesOnly": {
                        "title": "Reviews with a company reply only",
                        "type": "boolean",
                        "description": "Only collect reviews that have a public company reply. Default: false (collect all reviews).",
                        "default": false
                    },
                    "includeCompanyProfile": {
                        "title": "Include company profile",
                        "type": "boolean",
                        "description": "Push a company-profile record for each domain with: display name, trust score, total review count, and category. Adds one record per company at the start of the dataset. Default: true.",
                        "default": true
                    },
                    "useResidentialProxy": {
                        "title": "Use residential proxies",
                        "type": "boolean",
                        "description": "Use residential proxies (strongly recommended). Trustpilot uses AWS WAF that blocks datacenter IPs. Residential proxies significantly increase success rates. Datacenter proxies may work for small test runs but will be challenged under volume.",
                        "default": false
                    },
                    "zeroYieldAbortThreshold": {
                        "title": "Zero-yield abort threshold",
                        "minimum": 0,
                        "maximum": 50,
                        "type": "integer",
                        "description": "If the first N processed pages ALL fail to produce any parseable data (WAF/challenge block or __NEXT_DATA__ parse failure), the run aborts immediately instead of burning further browser/proxy time. A company with genuinely 0 reviews does NOT count as a failure here. Set to 0 to disable. Default: 3.",
                        "default": 3
                    },
                    "maxRunSeconds": {
                        "title": "Max run duration (seconds)",
                        "minimum": 0,
                        "maximum": 3600,
                        "type": "integer",
                        "description": "Optional soft deadline checked between pages. 0 (default) = disabled — runs complete fully. If set, the run stops cleanly at the deadline with whatever was already collected (exit 0 with partial data, exit 1 if nothing). Use for scheduled runs with a strict per-run time budget; leave 0 when you need complete results.",
                        "default": 0
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
