§Use caseOne operator

Find the complaint that repeats

Anyone can hand you 500 Trustpilot reviews as a spreadsheet. Then you are holding 500 rows and still asking the question you started with: what are these people actually angry about, and how often? Below is how I answer that, what my tool does when Trustpilot blocks it, and the case where I would send you to a cheaper scraper instead.

The output row, counts and quotes included
The output row, counts and quotes included
A themes row from a real run, with complaint labels, counts and the verbatim quotes under each one.

The questionHow do I find out what customers complain about most in a company's Trustpilot reviews?

How the working version is put together

  1. Get past the challenge, honestly

    Trustpilot puts an AWS WAF JavaScript challenge in front of every page. My first version used a plain HTTP fetch and collected nothing at all: a 403 from an Apify datacentre IP, the same through residential proxy, and the same from my own home line with a full set of browser headers. Not the IP then, and not the headers. That interstitial runs a script and earns a token before the real page will load, which no HTTP client can do, so I drive a real Chromium and wait for it to clear. The wait costs a second or two on the first page of each company and nothing after that, because the token is reused for the rest of the run.

  2. Read the JSON, not the HTML

    The review pages ship their reviews as a JSON payload, so that is what I read. Scraping the rendered HTML means re-fixing the scraper every time Trustpilot moves a div. Reading the payload the page was served means the star rating, both dates, the reviewer country, the verified flag and the company's public reply all arrive as fields rather than as guesses about markup.

  3. Group what repeats

    I make one model call per company, under a strict schema, and it comes back with complaint themes and praise themes. Each theme carries a short label, a count and up to three quotes. Filter to one and two stars and the themes are complaints only, which is the setting I use most. Leave the filter off and the row shows you the balance between the two.

  4. Verify every quote against its review

    The model returns a quote and the id of the review it says the quote came from. I check that the text appears as a verbatim substring of that review before it is written, and anything failing that check is dropped with the number of drops recorded on the row. A paraphrase cannot survive it, so a quote in the output is text a real person typed and you can open the review and see it.

  5. Say when it failed

    If the challenge does not clear, the company still gets a row, and that row says blocked with the HTTP status of every page touched. A company with no Trustpilot page comes back the same way. I would rather hand you an honest gap than a clean-looking dataset with a silent hole in it.

Why I built it

This came out of my own pre-sales work. Before I pitch a fix for someone's support queue I want to know what their customers say is broken, in their own words, with a number beside it. Ten minutes reading a themes row beats an afternoon in a spreadsheet, and it beats walking into a call with a theory.

It also runs both ways. Point it at your own Trustpilot page and I give it the same treatment: your top complaints, counted, quoted and dated, without anyone on your team having to volunteer for the reading.

The reviews come back with or without a key

I only use the OpenAI key for the theme step. Leave it out and you still get every review row, and the themes row records that the step was skipped and why. The model cost lands on your own OpenAI account rather than mine. For scale, a 20-review test on gpt-5-mini used roughly 1,600 input tokens and 6,200 output tokens.

Run it with no domains at all and you get a demo dataset of five sample reviews and one sample themes row, charged nothing, so you can see the output shape before pointing it at anything real.

What to trust in the row, and what to check

The quotes are verified word for word. The labels and the count estimates are the model's judgement over the reviews I handed it, not a database query, so read them next to the counts. A theme with a count of one and a single quote is one annoyed person, not a trend.

Mixed languages make the themes worse, so I would filter to one language unless you have a reason not to. And these are public reviews; Trustpilot's terms apply to what you do with them afterwards, which is your call to make and not one I can make for you.

Read this before you spend anything

When I would tell you not to bother

If all you want is the raw reviews, I would buy a cheaper one. Trustpilot scraping is a crowded commodity, larger scrapers exist and several of them do the collection perfectly well for less. The only part of mine that is not commodity is the theme step, so if you are going to read the rows yourself anyway, you are paying for something you do not need.

Thirty reviews are a coffee, not a project. If that is what the company has, read them, because I would hold thirty reviews in my head better than any summary would put them there. This earns its keep at a few hundred, or across a list of competitors, or on the same company month after month.

What lands on your desk

  • Every review as a row, with the rating, both dates, the verified flag and the company's public reply
  • A themes row per company: complaint themes and praise themes, each with a count and up to three quotes
  • Every quote checked verbatim against its source review, with failed ones dropped and counted
  • A one-paragraph plain-English readout you can drop into a report
  • An honest blocked or not-found row when Trustpilot says no, never a silent gap
  • If you want the themes wired into a weekly report or a fix list for your support team, that is the work I do

Questions this page answers

Do I need an OpenAI key?
Only for the theme step. Without one you still get every review row, and the themes row records that the step was skipped and why. The model cost sits on your own key rather than mine.
How do I know the quotes are real?
I check each quote as a verbatim substring of the review it claims to come from before it is written. Anything failing that check is dropped, and the number of drops sits on the row, so a paraphrase cannot get through.
Why does it need a browser?
Trustpilot fronts every page with an AWS WAF JavaScript challenge. My first version used a plain HTTP client and collected nothing at all from any IP with any headers, so it now drives a real Chromium and waits for the challenge to clear, with no residential proxy behind it.
What happens if Trustpilot blocks the run?
You get a row that says blocked, with the HTTP status of every page it touched. A company with no Trustpilot page comes back marked not found the same way.
Is this worth it if I only want the reviews?
Probably not, if the raw reviews are the whole ask. Trustpilot scraping is a commodity and cheaper scrapers do it well, so what I charge for here is the theme step sitting on top of the collection.

Want this built and handed over working?

One line is enough to start. You get an honest answer on fit, and a number rather than a discovery call.

Start a brief