kestrel actors › Guest & customer reviews
Trustpilot Reviews Scraper — Company Reviews & TrustScore
A trustpilot reviews scraper that turns any company profile into structured company reviews: the 1–5 star rating, the headline, the body text, the language it was written in, when it was published and when the experience happened, who wrote it and from which country, whether it is verified, how many readers found it useful, and the company's own public reply. Alongside the reviews it hands you the free company block — TrustScore, the star histogram, the per-language breakdown and the public contact details — so one run returns both the customer feedback and the Trustpilot data about the profile it came from.
Run Trustpilot Reviews Scraper on Apify →
Trustpilot reviews scraper pricing: what one row costs
| Event | What it is | Price | Charged for |
|---|---|---|---|
review | Review row | $0.0007 | One Trustpilot review: 1-5 rating, title, text, language, review and experience dates, reviewer name, country and review count, verification level and source, likes and the company's reply. Company and status rows are free; rows a filter drops are never billed. |
Pay per review row. company and status rows are free, and so is any company that turns out to have no reviews or no profile at all. Filters run before billing, so the rows you filter out cost nothing.
One run pays a small fixed overhead: a headless browser solves the site's bot challenge once, which takes five to twenty seconds of compute and a megabyte or so of proxy traffic. Every page after that is fetched without a browser, so the overhead is amortised across the whole run — it is noticeable on a twenty-review test and invisible on a thousand-review harvest. The token is re-solved every four minutes because the site stops honouring it at about five.
Trustpilot reviews scraper input reference
Every field of Trustpilot Reviews, straight from the actor's input schema.
| Field | Type | Default | What it does |
|---|---|---|---|
companyDomains | array | [] | The domain a company is listed under on Trustpilot — the part after trustpilot.com/review/, e.g. www.booking.com, www.monzo.com, ryanair.com. A company's own website URL works too (the domain is taken from it), and Trustpilot's own canonical spelling is followed automatically, so monzo.com resolves to www.monzo.com. |
startUrls | array | [] | Trustpilot company pages, e.g. https://www.trustpilot.com/review/www.booking.com — any query string is ignored (set the filters below instead), and a country subdomain such as uk.trustpilot.com is read as the same profile. |
maxReviewsPerCompany | integer | 100 | 0 = as deep as Trustpilot lets an anonymous visitor go. Trustpilot stops paging after 200 reviews of any one filtered view, so this actor walks the five star bands separately and merges them: up to 1000 reviews per company per run, newest first. N = the first N in the order below. The main cost control. Range 0-1000. |
sort | string (enum) | "most_recent" | most_recent = newest first (the order a monitoring schedule wants); most_relevant = Trustpilot's own relevance order. One of: most_recent, most_relevant. |
deepPaging | boolean | true | On (the default): when more than 200 reviews are asked for, each star rating is fetched as its own filtered view — 1, 2, 3, 4 and 5 stars each have their own 200-review allowance — and the results are merged and de-duplicated, so up to 1000 reviews come back per company. Off: one view only, so never more than 200 reviews and never more than 10 page requests per company. |
languages | string | "all" | all = every language, and the default here (Trustpilot's own page shows English only, which hides most reviews of a global brand). Or one language code — en, de, fr, es, it, nl, da, sv … — applied by Trustpilot itself, so reviews in other languages are never fetched and never billed. The company row lists how many reviews each language holds. |
minRating | integer | 0 | 0 = any. 1-5 = keep only reviews rated at or above this. Combine with maxRating for a band, e.g. minRating 1 and maxRating 2 for a complaints feed. Range 0-5. |
maxRating | integer | 0 | 0 = any. 1-5 = keep only reviews rated at or below this. maxRating 2 on a 125,000-review brand fetches and bills only the 1- and 2-star rows, because the star filter is Trustpilot's own URL parameter and runs on their servers. Range 0-5. |
dateRange | string (enum) | "all" | Trustpilot's own date filter, applied server-side. all = no limit. One of: all, last30days, last3months, last6months, last12months. |
sinceDate | string | "" | Keep only reviews published on or after this date: YYYY-MM-DD, or relative so a schedule never goes stale — "7 days", "2 weeks", "1 month". With most_recent order the walk stops at the first page that is older than the cut, so a daily run reads one or two pages. |
verifiedOnly | boolean | false | Keep only reviews Trustpilot marks as verified (invited after a real transaction, or identity-checked). Applied by Trustpilot, so unverified reviews are never fetched or billed. |
keyword | string | "" | Keep only reviews mentioning this word or phrase — refund, delivery, cancellation, chargeback. This is Trustpilot's own review search, so non-matching reviews are never fetched or billed. |
requireText | boolean | false | Drop reviews that carry only a star rating and a title, before billing. |
includeCompanyRow | boolean | true | One free row per company: TrustScore, stars, total reviews, reviews in the last 12 months, the 1-5 star histogram, the per-language breakdown, categories, website, country, the public contact details Trustpilot shows, and the claimed/closed/verification flags. |
sessions | integer | 2 | How many proxy sessions (egress IPs) fetch pages in parallel. Each is paced separately. 2 is plenty for a handful of companies; raise it for long lists. Range 1-8. |
perIp | number | 1 | Pace for each session. 1 request per second per IP is what Trustpilot tolerates comfortably; higher values earn retries, not speed. Range 0.2-2. |
proxyConfiguration | object | {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]} | Apify Proxy with the RESIDENTIAL group (the default, and required: the headless browser that solves the WAF challenge runs through it). Once the token exists it is honoured from any IP, so the page fetches themselves are not fussy. |
Example input
{
"companyDomains": ["www.booking.com", "www.expedia.com", "www.agoda.com"],
"maxReviewsPerCompany": 60,
"sort": "most_recent",
"minRating": 1,
"maxRating": 2,
"sinceDate": "2 days",
"requireText": true,
"languages": "all"
}
{
"companyDomains": ["www.monzo.com"],
"maxReviewsPerCompany": 1000,
"deepPaging": true,
"requireText": true,
"languages": "en",
"includeCompanyRow": true
}
Trustpilot reviews scraper output fields
One row per Trustpilot review (charged), plus a free company context row and a free status row per company.
Dataset views in the Apify Console: Overview, Reviews, Complaints, Companies, Status.
| Field | Type | What it holds |
|---|---|---|
type | string | review | company | status |
review_id | string | Trustpilot's review id (stable across runs; also the last part of the review URL) |
company_domain | string | The domain the profile is listed under, e.g. www.booking.com |
company_name | string | Display name of the company on Trustpilot |
rating | integer | 1-5 stars |
title | string | Review headline |
text | string | Review body (null when the reviewer left only a rating and a title) |
language | string | ISO code of the language the review was written in |
review_date | string | YYYY-MM-DD the review was published |
experience_date | string | YYYY-MM-DD of the experience the reviewer is describing |
updated_date | string | YYYY-MM-DD the reviewer last edited the review |
reviewer_name | string | Display name of the reviewer |
reviewer_id | string | Trustpilot consumer id |
reviewer_country | string | Two-letter country code of the reviewer |
reviewer_reviews | integer | How many reviews that consumer has written in total |
verified | boolean | Trustpilot's verified badge |
verification_level | string | verified | not-verified | invited …, Trustpilot's own label |
source | string | Organic, Invitation, BusinessGeneratedLink … — how the review was collected |
likes | integer | How many readers found the review useful |
reply_text | string | The company's public reply |
reply_date | string | YYYY-MM-DD the reply was published |
location | string | Branch or location the review is attached to, for multi-location profiles |
url | string | Permalink: the review on a review row, the profile on a company row |
company_id | string | Trustpilot business unit id |
trust_score | number | TrustScore, 1.0-5.0 |
stars | number | The rounded star rating Trustpilot displays |
review_count | integer | Total reviews on the profile |
reviews_last_12_months | integer | Reviews published in the last twelve months |
review_count_filtered | integer | How many reviews match this run's filters, as Trustpilot counts them |
ratings | object | Star histogram: {one, two, three, four, five} counts |
review_languages | array | [{code, name, count}] — how many reviews each language holds |
categories | array | Trustpilot category names the company is listed in |
website | string | The company's website as Trustpilot has it |
country_code | string | Two-letter country code of the company |
email | string | Public contact email on the Trustpilot profile |
phone | string | Public contact phone on the Trustpilot profile |
address | string | Public postal address on the Trustpilot profile |
is_claimed | boolean | The company has claimed its profile |
is_closed | boolean | The profile is closed or temporarily closed |
is_collecting | boolean | The company actively invites customers to review it |
verified_payment | boolean | Trustpilot verified the company's payment method |
verified_identity | boolean | Trustpilot verified the company's identity |
reviews_fetched | integer | Reviews this run read for the company before the actor-side filters |
target | string | The input this status row is about |
status | string | ok | no_reviews | not_found | error |
reviews | integer | Review rows delivered for this company |
filtered | integer | Reviews fetched but dropped by requireText / minRating / maxRating / verifiedOnly (never charged) |
pages | integer | Review pages read for this company |
total | integer | Trustpilot's own count of reviews matching this run's filters |
capped | boolean | True when the walk stopped at Trustpilot's anonymous paging wall rather than at the end of the reviews |
duplicates | integer | 1 when another input already covered this company |
error | string | Why this company produced nothing |
fetched_at | string | UTC timestamp of the fetch |
Trustpilot reviews scraper in Python, JavaScript and curl
Python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("kestrel/trustpilot-reviews-scraper").call(run_input={
"companyDomains": ["www.booking.com"],
"maxReviewsPerCompany": 200,
"sort": "most_recent",
"languages": "all",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["type"] == "review":
print(row["rating"], row["review_date"], row["title"])
JavaScript (Node)
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('kestrel/trustpilot-reviews-scraper').call({
companyDomains: ['www.monzo.com'],
maxReviewsPerCompany: 100,
minRating: 1,
maxRating: 2,
requireText: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.type === 'review').length, 'reviews');
curl
curl -X POST "https://api.apify.com/v2/acts/kestrel~trustpilot-reviews-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"companyDomains":["www.booking.com"],"maxReviewsPerCompany":40,"sort":"most_recent"}'
The same three calls are runnable files in the examples repository: examples/trustpilot-reviews-scraper.py|.js|.sh.
Limits and honest notes
- 200 reviews per filtered view is the platform's wall for anonymous visitors, and 1,000 per company per run is the practical ceiling once the five star views are combined. If you need more, narrow with
dateRangeorlanguagesand run again — each combination has its own allowance. - The bot challenge is solved with a real browser once per run and refreshed every four minutes. If a run is very short, that fixed cost is a real share of it.
verifiedOnlyreflects the platform's own badge, which many companies never use; some large profiles have only a handful of verified reviews.- Reviews are returned as written, never translated.
locationis only populated for profiles that run multiple branches.
FAQ
Does it need a Trustpilot API key or login?
No. There is no account, no key and no cookie to paste. It reads the same public pages a visitor sees.
Can I download Trustpilot reviews as CSV or Excel?
Yes. Every run writes an Apify dataset, so a Trustpilot review export is one click: Trustpilot reviews CSV, Excel, JSON, XML or RSS from the Console, or straight from the API. The Reviews view is already trimmed to the fields most people want.
How do I get only Trustpilot negative reviews?
Set minRating: 1 and maxRating: 2 — that is how you pull Trustpilot reviews by rating. That becomes the site's own star filter, so the four- and five-star rows are never downloaded and never billed. Add requireText: true to drop the rating-only rows.
How far back can I go?
As far as 200 reviews per filtered view takes you — a few weeks on a very busy brand, years on a quiet one. Use dateRange to aim a window, and sinceDate to stop a scheduled run as soon as it reaches material it already has.
Which companies can I scrape?
Any profile that exists. Give the domain exactly as the profile lists it. If the profile does not exist you get a free status row with not_found — you are never charged for a miss, and it is never reported as "no reviews".
Does it include the company's replies?
Yes: reply_text and reply_date on every review that has one. Response rate and median response delay are both computable from a single run.
What is TrustScore, and where do I find it?
TrustScore is the platform's weighted score out of five, and it lives on the free company row as trust_score, next to stars, review_count and the full ratings histogram. You are never billed for it.
Can I use this for review sentiment analysis?
Yes, and requireText: true plus a single languages value is the combination to use — it gives a model clean, one-language input with no rating-only rows to skew the corpus.
How fast is it?
About one page (20 reviews) per second per session, two sessions by default, plus the one-off browser bootstrap. A 1,000-review company takes a couple of minutes; a hundred small profiles take about the same.
What does bulk work cost?
Cost scales with delivered rows, not with companies, which is what makes bulk company reviews practical. A hundred profiles at 50 recent reviews each is 5,000 rows; the same hundred profiles at minRating: 1, maxRating: 2 is a fraction of that, because the rest are never fetched.
How to use Trustpilot Reviews Scraper
Ready-made runs you can open and start
Each one is a saved input on the Apify Store — open it, press Start, and it runs with the fields already filled in. All 163 across the suite.
| Ready-made run | What it does |
|---|---|
| Trustpilot Negative Reviews for Any Company | One- and two-star Trustpilot reviews of one travel brand, newest first, with the company's reply beside each - filtered on Trustpilot's side, not after. |
| Download Trustpilot Reviews to CSV | Seven hundred newest Trustpilot reviews of one company, past the 200-review wall, with rating, text, dates, reviewer country and reply as CSV or JSON. |
| Trustpilot Reviews by Rating: Five Stars Only | Three hundred five-star Trustpilot reviews with written text for one company: what customers praise, in their own words, with country and date. |
| Trustpilot Reviews by Date: Last 30 Days | Only the last thirty days of Trustpilot reviews for one brand, filtered by Trustpilot itself, with the free company row's TrustScore as the baseline. |
| Trustpilot Competitor Reviews: Five Neobanks | Five challenger banks compared on Trustpilot: TrustScore, stars, review totals and the star histogram per brand, with a hundred newest reviews each. |
| Customer Feedback Mentioning Refunds on Trustpilot | Only Trustpilot reviews that mention a refund for one travel booking site, searched on Trustpilot's side so nothing else is billed - swap in any keyword. |
| Verified Customer Reviews Only from Trustpilot | Only Trustpilot reviews marked verified - invited after a real order or identity-checked - for one meal-kit brand; unverified rows are never billed. |
| German-Language Trustpilot Reviews Only (de) | Only German-language Trustpilot reviews of one retailer's German site, filtered by Trustpilot itself - the reviews the English page never shows you. |
| Reputation Monitoring on Trustpilot, Weekly Feed | The last seven days of Trustpilot reviews for three airlines, the walk stopping at the date cut - a weekly schedule that reads a page or two per brand. |
| TrustScore and Star Rating for Five Retailers | TrustScore, stars, review totals, histogram and contact details for five online marketplaces in one free company row each, plus twenty reviews apiece. |
| Trustpilot Review Export in Most-Relevant Order | Two hundred Trustpilot reviews of one brand in Trustpilot's own most-relevant order - the reviews a prospective customer actually reads first. |
| Review Sentiment Analysis Dataset from Trustpilot | Six hundred English Trustpilot reviews of one brand across all five star bands, text required - a star-labelled corpus for sentiment analysis. |
Related scrapers
Run Trustpilot Reviews Scraper
The actor lives on the Apify Store, where the pricing tab carries the live per-row price and the input form has every field above: apify.com/kestrel/trustpilot-reviews-scraper