Trustpilot Reviews Scraper — Company Reviews & Trust Scores
Pricing
from $2.00 / 1,000 review scrapeds
Trustpilot Reviews Scraper — Company Reviews & Trust Scores
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.
Pricing
from $2.00 / 1,000 review scrapeds
Rating
0.0
(0)
Developer
Mariano Ferreras
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
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),
sinceDateincremental 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
sinceDatefilter for incremental runs — fetch only new reviews since your last run- Two record types unified in one dataset:
reviewandcompany-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
sinceDateset to yesterday; pipe new reviews to Slack, email, or a ticketing system.
Input
{"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. |
languages | string[] | [] | Only collect reviews in these language codes, e.g. ["en", "es"] (case-insensitive). Empty = no filtering. See Filters. |
verifiedOnly | boolean | false | Only collect reviews Trustpilot marks as "verified". See Filters. |
withRepliesOnly | boolean | false | Only collect reviews that have a public company reply. See 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 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:
{"companyDomains": ["amazon.com"],"maxReviewsPerCompany": 50,"starRatings": [1, 2],"languages": ["en"]}
Example — verified reviews with a company reply, incremental since last run:
{"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")
{"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. |
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.
{"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
{"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 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.numberOfReviewsis a plain number (not{total: N})- Pagination at
pageProps.filters.pagination(notpageProps.paginationdirectly)
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.