# Trustpilot & Capterra Reviews Scraper: One Schema (`dottti/business-reviews`) Actor

Trustpilot and Capterra reviews in one table: rating, title, text, pros and cons, date, language, reviewer name and country, verified flag, company reply. Star, date and language filters, past the 200-review limit, new-reviews-only mode. Free status row per target.

- **URL**: https://apify.com/dottti/business-reviews.md
- **Developed by:** [Dottti](https://apify.com/dottti) (community)
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 reviews

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

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Trustpilot Reviews Scraper: Company Reviews, Replies, Alerts

Get public Trustpilot reviews for any company, as clean JSON, CSV or Excel. Filter by stars, date and language, read past the first 200 reviews, and get only new reviews on each scheduled run.

**Capterra (experimental):** Capterra blocks most automated access, including from Apify's servers in our tests on 21 October 2026. Capterra targets may return a free `blocked` status instead of reviews. You are never charged for a blocked target.

You give it a list of companies or products. You get back:

- One row per review: rating, title, text, pros and cons, date, language, reviewer display name and country, verified flag, company reply and helpful votes.
- One free profile row per company: overall rating, review count, star distribution, categories, country and how often the company replies.
- One free status row per target, so you can see what worked and what did not.

No login. No captcha solving.

### Who uses it

- **Marketing teams** that track what customers say about their brand and about competitors.
- **Product teams** that read Capterra pros and cons to plan features.
- **Reputation agencies** that need new reviews for many clients every day, with the company reply next to each review.
- **AI and sentiment pipelines** that need clean review text, a star label and a language code, in one shape for every source.

### What you can put in

One target per line. Mix them freely.

| You type | What it means |
|---|---|
| `https://www.trustpilot.com/review/careem.com` | Trustpilot company page |
| `careem.com` | Same thing, shorter |
| `Talabat` | A company name. The actor searches Trustpilot and takes the best match. The status row says which company it matched. |
| `https://www.capterra.com/p/135003/Slack/reviews/` | Capterra product page |
| `https://www.g2.com/products/slack/reviews` | Accepted, returns a free `not_supported` status row. See Limits. |

Other options:

- **Max reviews per target.** You pay only for reviews delivered.
- **Star ratings.** For example only 1 and 2 stars.
- **Reviews published on or after.** A date like `2026-01-01`, or `30 days`, or `6 months`.
- **Review language.** `all`, or a code like `en`, `ar`, `de`.
- **Order.** Newest first, or Trustpilot's "most relevant".
- **Only new reviews since the last run.** Turn this on for scheduled alerts. Give each watch list its own state name.
- **Company profile row.** On by default, and free.

### Output

#### Review row (Trustpilot, real run 2026-10-21)

```json
{
  "type": "review",
  "source": "trustpilot",
  "targetName": "Careem",
  "targetUrl": "https://www.trustpilot.com/review/careem.com",
  "targetRating": 1.1,
  "targetReviewCount": 1066,
  "reviewId": "69946cff1d678738ce2084f7",
  "reviewUrl": "https://www.trustpilot.com/reviews/69946cff1d678738ce2084f7",
  "rating": 5,
  "title": "Amazing  very very good",
  "text": "Amazing  very very good ",
  "pros": null,
  "cons": null,
  "date": "2026-02-17T15:28:31.000Z",
  "experienceDate": "2026-02-05T00:00:00.000Z",
  "updatedDate": null,
  "language": "en",
  "languageSource": "site",
  "reviewLength": 46,
  "reviewerName": "Sura",
  "reviewerCountry": "AE",
  "verified": false,
  "companyReplyText": "Wow! Thank you so much for the amazing feedback. We’re thrilled you had such a good experience!",
  "companyReplyDate": "2026-02-20T12:05:38.000Z",
  "helpfulVotes": 0,
  "subRatings": null,
  "input": "careem.com",
  "scrapedAt": "2026-10-21T14:32:17.326Z"
}
```

Arabic reviews come through as written, with `"language": "ar"`:

```json
{"targetName": "Careem", "rating": 3, "title": "السواق محترم والسيارة نظيفة", "text": "السواق محترم والسيارة نظيفة، بس اتأخر شوي بسبب الزحمة.", "language": "ar", "date": "2025-11-06T23:35:33.000Z", "reviewerCountry": "IQ"}
```

#### Review row (Capterra, parsed from a real page saved 2026-10-21)

Capterra rows add `pros`, `cons` and `subRatings`. Text is shortened here.

```json
{
  "type": "review",
  "source": "capterra",
  "targetName": "Slack",
  "targetUrl": "https://www.capterra.com/p/135003/Slack/reviews/",
  "targetRating": 4.7,
  "targetReviewCount": 24230,
  "reviewId": "7209002",
  "rating": 5,
  "title": "Slack: Essential for remote work productivity...and employee engagement ",
  "text": "Slack has been part of the corporate toolkits of the companies I've worked for since 2017 ...",
  "pros": "Since 2020, I've worked remotely as a business developer across two digital services companies. ...",
  "cons": "Honestly, not much. The only time I get frustrated with it is when I need to invite partners ...",
  "date": "2026-09-03T00:00:00.000Z",
  "language": "en",
  "languageSource": "detected",
  "reviewerName": "Alicia R.",
  "reviewerCountry": null,
  "verified": true,
  "companyReplyText": null,
  "subRatings": { "easeOfUse": 5, "customerSupport": 5, "functionality": 5, "valueForMoney": 5, "likelihoodToRecommend": 10 }
}
```

#### Field notes

- `date` is the publish date in ISO format. Capterra gives only a day, so its time is midnight UTC.
- `language`: Trustpilot sets its own language tag (`languageSource: "site"`). For Capterra the actor checks the text with a simple built-in rule: alphabet first, then common words (`languageSource: "detected"`). It returns `null` when it is not sure.
- `reviewLength` counts the characters of title, text, pros and cons together.
- `verified`: Trustpilot's "verified" label, or Capterra's "validated reviewer" check.
- `helpfulVotes`: Trustpilot "useful" votes. Capterra does not show a count, so it is `null`.
- `subRatings`: Capterra only. A sub-rating the reviewer skipped is `null`.
- Personal data: the actor keeps only the reviewer's display name and country. It drops profile photos, user IDs, job titles and employer details.

#### Company profile row (free)

```json
{
  "type": "company",
  "source": "trustpilot",
  "name": "Careem",
  "domain": "careem.com",
  "url": "https://www.trustpilot.com/review/careem.com",
  "website": "https://careem.com",
  "trustScore": 1.1,
  "reviewCount": 1066,
  "reviewCountLast12Months": 209,
  "ratingDistribution": { "1": 992, "2": 29, "3": 10, "4": 8, "5": 27 },
  "categories": ["Courier and Delivery Service", "Taxi Service", "Payment Service"],
  "country": "AE",
  "isClaimed": true,
  "averageDaysToReply": 6.31,
  "replyPercentage": 73.89162561576354,
  "charged": false
}
```

#### Status row per target (free)

```json
{"type": "status", "source": "trustpilot", "input": "careem.com", "targetName": "Careem", "status": "ok", "reviews": 250, "pages": 15, "newestReviewDate": "2026-10-20T12:51:45.000Z", "oldestReviewDate": "2017-08-05T07:57:32.000Z", "seconds": 18.9, "charged": false}
```

Status values:

- `ok`: reviews delivered.
- `no_reviews`: the page exists but nothing matched your filters.
- `no_new_reviews`: "only new" mode found nothing since the last run.
- `not_found`: no company page for this domain or name.
- `blocked`: the site refused every retry on every proxy tier.
- `blocked_captcha`: the site asked for a captcha. The actor does not solve captchas.
- `not_supported`: G2 (see Limits).
- `bad_input`: the line is not a URL, domain or name the actor understands.
- `skipped_cost_limit` or `cut_by_cost_limit`: your maximum cost for the run was reached.

The `RUN_SUMMARY` record in the key-value store has every status row, reviews per source, requests, bytes per 1,000 reviews and the proxy tier used.

### How it works

- **Trustpilot.** Each company page holds its reviews as JSON inside the page. The actor reads that JSON. Some networks get Trustpilot's automatic "Verifying your connection" page first. In that case the actor opens that one page in a headless browser, the site sets its normal cookie, and the actor goes on with plain HTTP. The browser loads only the check page and closes after a few seconds.
- **More than 200 Trustpilot reviews.** Logged-out visitors can open 10 pages (200 reviews) for each filter. When you ask for more, the actor reads each star rating separately, then each language, and removes duplicates. In a test it read 450 Amazon reviews in 22 seconds this way.
- **Capterra.** The reviews sit inside the page data. The actor reads 25 reviews per page and follows `?page=2`, `?page=3` and so on.
- **Proxies.** The actor starts without a proxy. If a site keeps refusing, it moves to Apify datacenter proxy, then residential. You can turn residential off, or set your own proxy.

### Pricing

$1.00 per 1,000 reviews ($0.001 per review). Each run also has a start fee of $0.00005 (per GB of memory). Company profile rows and status rows are free. Blocked and unsupported targets cost nothing. Set a maximum cost on the run and the actor stops cleanly when it is reached.

Measured on 2026-10-21 for Trustpilot (local runs, 1,450 reviews): about 7.7 MB of compressed traffic and 59 requests per 1,000 reviews, about 25 seconds of run time per 1,000 reviews at 256 MB.

### Limits

- **G2 is not supported.** G2 shows a captcha to every logged-out visitor, including a normal desktop browser. This actor does not solve captchas. G2 URLs return a free `not_supported` row.
- **Capterra is protected by a Cloudflare check.** From our test network every automated request got the check page, so Capterra live runs were not confirmed. The parser is tested on a real saved page. If Capterra blocks every proxy tier, you get a free `blocked` row and pay nothing.
- **Capterra order.** Capterra pages come in its "most helpful" order. Date, star and language filters are applied to the pages the actor reads, so a narrow filter can need many pages.
- **Capterra has no reviewer country and no helpful count.**
- **Trustpilot depth.** Even with splitting, each star and language combination gives at most 200 reviews. Companies with very many reviews in one language and one star rating cannot be read in full while logged out. The status row says so.
- **Trustpilot browser check.** The actor passes the automatic check page only. If Trustpilot shows a captcha instead, the target ends as `blocked_captcha`.
- Name search is Trustpilot only. For Capterra, paste the product URL.

### Tips

- For daily alerts, schedule the actor with "Only new reviews" on, newest first, and a small max. The first run sets the baseline.
- To compare competitors, put all of them in one run. Filter the dataset by `targetName`.
- To feed an AI pipeline, keep `text`, `pros`, `cons`, `rating` and `language`. They are filled the same way for both sites.

# Actor input Schema

## `targets` (type: `array`):

One per line. Trustpilot company page URL (https://www.trustpilot.com/review/careem.com), a bare domain (careem.com), a company name to look up on Trustpilot (Careem), or a Capterra product URL (https://www.capterra.com/p/135003/Slack/reviews/). G2 URLs are accepted but return a free 'not_supported' status row (see README).

## `maxReviewsPerTarget` (type: `integer`):

You pay only for reviews delivered. Trustpilot: above 200, the actor splits the reviews by star rating and language to go past the 10-page limit for logged-out visitors.

## `stars` (type: `array`):

Only reviews with these ratings. Leave empty for all.

## `dateFrom` (type: `string`):

YYYY-MM-DD, or relative like '30 days' or '6 months'. Leave empty for no date limit.

## `language` (type: `string`):

'all', or a 2-letter code such as en, ar, de, fr. Trustpilot filters on its own language tag. Capterra reviews are matched with a simple built-in language check.

## `sort` (type: `string`):

Newest first, or Trustpilot's 'most relevant' order. Capterra pages always come in Capterra's 'most helpful' order.

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

One free row per target: overall rating, review count, star distribution, categories, country and reply behaviour (Trustpilot) or sub-ratings (Capterra).

## `onlyNew` (type: `boolean`):

For scheduled alerts. Each run remembers the reviews it delivered, by target, under the state name below. The first run is the baseline and delivers up to the max.

## `stateName` (type: `string`):

Separate names keep separate memories, for example 'brand-a' and 'competitors'.

## `deepPagination` (type: `boolean`):

Splits the request by star rating, then by language, when more than 200 reviews are needed. Turn off to read only the first 10 pages.

## `proxyConfiguration` (type: `object`):

Leave off to use the automatic fallback below. If you set a proxy here, it is used for every request and the fallback is off.

## `autoProxyFallback` (type: `boolean`):

Starts without a proxy. If a site keeps refusing, switches to Apify datacenter proxy, then residential.

## `allowResidentialFallback` (type: `boolean`):

Turn off to never use residential proxy.

## `useBrowserCheck` (type: `boolean`):

When Trustpilot shows its 'Verifying your connection' page, open it once in a headless browser so the site sets its normal cookie, then continue over plain HTTP. Captchas are never solved.

## `maxConcurrency` (type: `integer`):

Requests to each site are still spaced out.

## `maxRetries` (type: `integer`):

Retries with growing waits, fresh sessions and the next proxy tier before a target is marked blocked.

## Actor input object example

```json
{
  "targets": [
    "careem.com",
    "https://www.trustpilot.com/review/www.noon.com"
  ],
  "maxReviewsPerTarget": 20,
  "stars": [],
  "dateFrom": "",
  "language": "all",
  "sort": "recency",
  "includeCompanyProfile": true,
  "onlyNew": false,
  "stateName": "default",
  "deepPagination": true,
  "autoProxyFallback": true,
  "allowResidentialFallback": true,
  "useBrowserCheck": true,
  "maxConcurrency": 3,
  "maxRetries": 4
}
```

# Actor output Schema

## `results` (type: `string`):

All rows. Filter by the type field.

## `runSummary` (type: `string`):

Status per target, reviews per source, requests, bytes per 1,000 reviews, browser checks and proxy tier used.

# 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 = {
    "targets": [
        "careem.com",
        "https://www.trustpilot.com/review/www.noon.com"
    ],
    "maxReviewsPerTarget": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/business-reviews").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 = {
    "targets": [
        "careem.com",
        "https://www.trustpilot.com/review/www.noon.com",
    ],
    "maxReviewsPerTarget": 20,
}

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

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

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

```

## CLI example

```bash
echo '{
  "targets": [
    "careem.com",
    "https://www.trustpilot.com/review/www.noon.com"
  ],
  "maxReviewsPerTarget": 20
}' |
apify call dottti/business-reviews --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dottti/business-reviews"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/kZUGUl5zVz2FOxfcd/builds/j048mrsCUYo4JA7nu/openapi.json
