# Trustpilot Reviews Scraper (`devil_port369-owner/trustpilot-reviews`) Actor

Scrape Trustpilot company profiles, customer reviews, keyword search results, and category listings. Filter by stars, date, language, topics, verified reviews, and replies. Bulk domains and queries. Structured JSON for reputation monitoring, VoC, lead generation, and AI agents.

- **URL**: https://apify.com/devil_port369-owner/trustpilot-reviews.md
- **Developed by:** [DataFusionX](https://apify.com/devil_port369-owner) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.45 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 & Sentiment Analytics – Reviews, TrustScore, Company Data

**Trustpilot Reviews Scraper & Sentiment Analytics** extracts public Trustpilot data without a Trustpilot account or API key: **customer reviews, company profiles (TrustScore, contacts, categories), keyword search results, and category listings** as clean JSON, CSV, or Excel. Turn on **review sentiment analysis** to get an investor-grade **insight report per company**: rating confidence interval, 90-day trend, complaint and praise themes with evidence quotes, reply behaviour, and review-integrity flags. No LLM and no extra API keys.

> **In one sentence:** give it a company domain (`apify.com`), a keyword (`insurance agency in United States`), or a category (`car_dealer`) and get structured rows tagged `business`, `review`, `search_result`, `insight`, or `benchmark`.

***

### Quick start (60 seconds)

1. Open the Actor and choose a **Mode**.
2. Add `companyUrls`, `searchQueries`, or `categories`.
3. (Optional) Tick **Analyze reviews** for sentiment and insight rows.
4. Click **Start** and download the dataset as JSON, CSV, Excel, or XML, or read it through the API.

```json
{
  "mode": "companyReviews",
  "companyUrls": ["apify.com"],
  "maxReviews": 100,
  "languages": "en",
  "sort": "recency"
}
```

Add sentiment and insight analysis:

```json
{
  "mode": "companyReviews",
  "companyUrls": ["apify.com"],
  "maxReviews": 0,
  "sort": "recency",
  "analyzeReviews": true
}
```

***

### What can I scrape from Trustpilot?

| Mode | Best for | Input | Output row types |
|------|----------|-------|------------------|
| **`companyReviews`** | Reviews and TrustScore for known companies | `companyUrls` | `business` (profile) + `review` (+ `insight` with analysis) |
| **`search`** | Finding companies by keyword or niche | `searchQueries`, `country` | `search_result` (+ `review` and `insight` if `scrapeReviews` is on) |
| **`category`** | Listing companies in a Trustpilot category | `categories` | `search_result` (+ `review` and `insight` if `scrapeReviews` is on) |

#### Data you get

- **Reviews:** title, full text, 1–5 star rating, published and experience dates, language, helpful votes, verification level and source, reviewer name/country/review count, company reply text and date. With analysis on, each review also gets `sentiment`, `sentimentScore`, and `textSentimentScore`.
- **Company profiles:** TrustScore, total reviews, reviews in the last 12 months, claimed/closed status, website, categories and breadcrumbs, address, city, country, and email/phone/websites when Trustpilot publishes them.
- **Search & category hits:** name, slug, TrustScore, review count, categories, location, contacts when shown, and profile URL.
- **Insight rows (analysis on):** one per company with ratings, trend, themes, responsiveness, integrity flags, red/green signals, and a plain-English executive summary.
- **Benchmark row (analysis on, 2+ companies):** companies ranked against each other on shared metrics.

***

### Why use this Trustpilot scraper?

- **No Trustpilot login or API key:** works from public profile, search, and category pages.
- **Three modes in one Actor:** reviews, discovery, and category mining without juggling tools.
- **Built-in sentiment and insight analytics:** statistics, not just a score. Every theme links back to real review quotes, so analysts can audit it.
- **Honest about sample limits:** trend verdicts say `insufficient_data` when the sample cannot support them, and filtered runs are flagged automatically.
- **Review-integrity signals:** invited-vs-organic rating gap, rating bursts, duplicate text, and first-time reviewer share.
- **Server-side review filters:** stars, date range, language, topics, verified only, with replies only, in-review keyword, and sort order.
- **Discovery to insight in one run:** `scrapeReviews: true` plus `analyzeReviews: true` analyzes every company found by search or category.
- **Typed, flat rows:** every item has a `type` field, so there are no nested surprises for BI tools or agents.
- **Automation-ready:** Apify API, schedules, webhooks, and integrations (Make, n8n, Zapier, Google Sheets), plus MCP for AI agents.

***

### Use cases

- **Investor and VC due diligence:** check whether a company's headline rating is organic, stable, and well managed before you rely on it.
- **Reputation and brand monitoring:** track new reviews, TrustScore, and rising complaint themes on a schedule.
- **Voice of Customer (VoC) and sentiment analysis:** feed review text and sentiment fields to dashboards or NLP pipelines.
- **Competitor benchmarking:** compare rating, net star score, reply behaviour, and integrity signals across rivals in one run.
- **Review authenticity checks:** spot invitation-driven rating inflation, one-day 5-star bursts, and duplicated text.
- **Churn and support insights:** find churn language, refund friction, and slow-support themes in negative reviews.
- **Lead generation:** find companies in a niche or country and capture published contact details.
- **AI agents and RAG:** give an agent live, structured customer-sentiment data on demand.

***

### How do I scrape Trustpilot reviews for a company?

Use `companyReviews` mode and pass a **domain** or a **Trustpilot profile URL**. All of these are valid:

- `apify.com`
- `www.adorama.com`
- `https://www.trustpilot.com/review/webuyanycarusa.com`

Set `maxReviews` to the number of reviews you want **per company**. Use `0` for as many as public pages allow (Trustpilot limits how deep pagination goes, so very large review sets may be only partly available).

### How do I find companies on Trustpilot by keyword?

Use `search` mode with `searchQueries` (for example `"Travel agency in New York"`) and a 2-letter `country`. Add `scrapeReviews: true` to also collect reviews for each company found.

### How do I scrape a whole Trustpilot category?

Use `category` mode with category slugs (`car_dealer`, `technical_service`, `insurance_agency`) or full URLs such as `https://www.trustpilot.com/categories/car_dealer`.

***

### How does Trustpilot review sentiment analysis work?

Set **`analyzeReviews: true`**. The Actor then does two things:

1. **Enriches every review row** with `sentiment` (`positive`, `neutral`, `negative`), `sentimentScore` (−1 to 1), and `textSentimentScore`.
2. **Pushes one `type: "insight"` row per company**, summarizing the whole review sample. With 2+ companies it also pushes one `type: "benchmark"` row.

#### How sentiment is scored

- The **star rating** is mapped to −1…+1 (1★ = −1, 3★ = 0, 5★ = +1).
- **English review text** is scored with VADER, a rule-based sentiment model. It is not an LLM.
- The final `sentimentScore` blends both 50/50. Labels: at or above +0.10 is positive, at or below −0.10 is negative, otherwise neutral.
- If the text is not English, `textSentimentScore` is `null` and the score uses the star rating alone.

#### What an insight row tells you

| Section | What it answers |
|---------|-----------------|
| `sample` | How many reviews were analyzed, coverage of the profile, **confidence** (`low` / `medium` / `high`), and **warnings** you should read first |
| `ratings` | Average rating with **95% confidence interval**, star distribution, **net star score**, polarization, TrustScore |
| `sentimentSummary` | Share of positive / neutral / negative reviews and average sentiment |
| `momentum` | 90-day rating trend and review-volume trend, plus monthly and quarterly series |
| `responsiveness` | Reply rate, **reply rate to 1–2 star reviews**, median and 90th-percentile reply time |
| `themes` | **Complaint**, **praise**, and **latent friction** themes with prevalence, trend status, and evidence quotes |
| `distinctiveTerms` | Words and phrases that statistically separate negative from positive reviews |
| `integrity` | Invited share, invited-vs-organic rating gap, bursts, duplicate text, first-time reviewers |
| `audience` | Reviewer countries, languages, and business/team/developer usage signals |
| `signals` | **Red** and **green** signals, each with a code, severity, and detail |
| `keyTakeaways`, `executiveSummary` | Plain-English summary for people who won't read the JSON |

#### Key metrics explained

- **Net star score:** share of 4–5 star reviews minus share of 1–2 star reviews, from −100 to +100.
- **Polarization:** share of reviews that are exactly 1 or 5 stars. High values mean a split customer base that an average hides.
- **Rating trend (`momentum.ratingTrend`):** compares the last 90 days with the prior 90. A verdict of `improving` or `declining` needs at least 20 reviews in each window, a change of at least 0.15 stars, and statistical significance (Welch t-test). Otherwise you get `stable` or `insufficient_data`, with a `reason`.
- **Volume trend (`momentum.volume`):** `accelerating`, `stable`, or `slowing`. Burst days are excluded when they fall in the windows, because invitation campaigns and brigading would otherwise distort the ratio. The raw counts stay visible.
- **Themes:** matched on whole words and phrases (so "no bugs" or "never had an outage" are not counted as complaints). Each theme shows its share of the group, a trend status (`rising`, `stable`, `fading`, or `insufficient_data`), and up to two evidence quotes with `reviewId`, rating, date, and snippet. Reviewer names are not included in quotes.
- **Latent friction:** complaint themes found inside 4–5 star reviews. Even happy customers can flag pricing, setup, or limits, and this is where it shows up.
- **Small samples:** each theme reports `groupSize` and `lowSample`. High-severity flags use a confidence bound, so 13 fraud mentions among 18 negative reviews can raise a flag while 1 mention among 18 cannot.

#### Review-integrity signals

These are **heuristics for a human to investigate, not proof of manipulation.**

| Signal | What triggers it |
|--------|------------------|
| `reviews_mostly_invited` | 80% or more of reviews were collected through company invitations |
| `invited_rating_inflation` | Invitations make up at least half the sample and invited reviews rate at least 0.5 stars higher than organic ones |
| `coordinated_praise_burst` / `coordinated_attack_burst` | A day with an unusual spike of reviews that are 80% or more 5-star (or 1-star) |
| `duplicate_review_text` | 3% or more of reviews share identical text |
| `mostly_first_time_reviewers` | 60% or more of reviewers have only ever written one review (common with invitations) |
| `many_contentless_5_star` | 30% or more of 5-star reviews are four words or fewer |
| `rating_text_mismatch` | 5% or more of reviews have a star rating contradicting their text |

Other red signals: `rating_declining`, `rising_complaint`, `fraud_allegations`, `churn_or_warning_language`, `ignores_negative_reviews`, `slow_replies_to_negatives`, `polarized_ratings`. Green signals: `rating_improving`, `strong_net_star_score`, `responsive_to_negatives`, `review_volume_accelerating`.

#### Get reliable results

- **Set `maxReviews` to `0`**, or at least 500 for large companies. A small sample cannot support trend claims.
- **Sort by `recency`** and **do not use** `stars`, `topics`, `verifiedOnly`, `withReplies`, or `reviewSearch`. These filters change which reviews exist in the sample. When any is set, the insight row is marked `filtered`, confidence drops to `low`, trend verdicts are disabled, and only integrity signals are kept.
- **Read `sample.warnings` and `sample.confidence` first.** They list the limits of that run, such as low coverage, few negative reviews, or sentiment running on ratings only.
- **Use English reviews for text sentiment.** Other languages are scored from the star rating alone.

***

### Input parameters

| Parameter | Type | Default | Applies to | Description |
|-----------|------|---------|------------|-------------|
| `mode` | string | `companyReviews` | all (**required**) | `companyReviews`, `search`, or `category` |
| `companyUrls` | string\[] | – | company | Domains or profile URLs |
| `searchQueries` | string\[] | – | search | Keywords or business descriptions |
| `categories` | string\[] | – | category | Category slugs or URLs |
| `maxReviews` | integer | `100` | company; search/category with `scrapeReviews` | Max reviews **per company** (0–50,000). `0` = all available public pages. Use `0` or 500+ with `analyzeReviews` |
| `maxResults` | integer | `50` | search, category | Max business rows (0–10,000). `0` = unlimited within `maxPages` |
| `maxPages` | integer | `5` | search, category | Pagination depth (1–50) |
| `country` | string | `US` | search | 2-letter country code for search ranking (`US`, `GB`, `DE`) |
| `scrapeReviews` | boolean | `false` | search, category | Also scrape reviews for each discovered company |
| `analyzeReviews` | boolean | `false` | all modes that scrape reviews | Add sentiment fields to reviews, push one `insight` row per company, and a `benchmark` row for 2+ companies |
| `stars` | string\[] | all | reviews | Only these ratings: `"1"` to `"5"` |
| `date` | string | `all` | reviews | `all`, `last30days`, `last3months`, `last6months`, `last12months` |
| `languages` | string | `en` | reviews | ISO language code (`en`, `es`, `de`) |
| `topics` | string\[] | – | reviews | Trustpilot topic IDs (see below) |
| `verifiedOnly` | boolean | `false` | reviews | Keep only verified reviews |
| `withReplies` | boolean | `false` | reviews | Keep only reviews with a company reply |
| `reviewSearch` | string | `""` | reviews | Keyword filter inside review text |
| `sort` | string | `recency` | reviews | `recency`, `relevance`, or `rating` |
| `includeBusinessProfile` | boolean | `true` | reviews | Emit one `business` row before each company's reviews |

> **Note:** `scrapeReviews` defaults to `false`, so search and category runs return company listings only. Set it to `true` to also collect reviews for every company found (uses `maxReviews` and the review filters).

**Topic IDs:** `price`, `service`, `staff`, `value_for_money`, `customer_service`, `recommendation`, `location`, `ethics`, `booking_process`, `payment`, `credibility`, `website`, `user_experience`, `delivery_service`

***

### Input examples

#### Recent verified 1–2 star reviews for several companies

```json
{
  "mode": "companyReviews",
  "companyUrls": [
    "www.adorama.com",
    "https://www.trustpilot.com/review/webuyanycarusa.com"
  ],
  "maxReviews": 200,
  "stars": ["1", "2"],
  "date": "last12months",
  "languages": "en",
  "verifiedOnly": true,
  "sort": "recency"
}
```

#### Reviews mentioning a keyword, with company replies only

```json
{
  "mode": "companyReviews",
  "companyUrls": ["apify.com"],
  "reviewSearch": "support",
  "withReplies": true,
  "maxReviews": 50
}
```

#### Investor-style insight on one company

```json
{
  "mode": "companyReviews",
  "companyUrls": ["apify.com"],
  "maxReviews": 0,
  "sort": "recency",
  "languages": "en",
  "analyzeReviews": true
}
```

#### Competitor benchmark (insight rows plus a benchmark row)

```json
{
  "mode": "companyReviews",
  "companyUrls": ["www.adorama.com", "webuyanycarusa.com", "apify.com"],
  "maxReviews": 0,
  "sort": "recency",
  "analyzeReviews": true
}
```

#### Company discovery only (no reviews)

```json
{
  "mode": "search",
  "searchQueries": ["Insurance agency in United States"],
  "country": "US",
  "maxResults": 40,
  "maxPages": 2,
  "scrapeReviews": false
}
```

#### Discovery plus reviews and insight in one run

```json
{
  "mode": "search",
  "searchQueries": ["Travel agency in New York"],
  "country": "US",
  "maxResults": 10,
  "maxPages": 1,
  "scrapeReviews": true,
  "maxReviews": 200,
  "sort": "recency",
  "analyzeReviews": true
}
```

#### Category listings

```json
{
  "mode": "category",
  "categories": ["technical_service", "car_dealer"],
  "maxResults": 50,
  "maxPages": 3,
  "scrapeReviews": false
}
```

***

### Output

Every dataset item has a **`type`** field. Filter on it in the Apify console, API, or your code.

| `type` | Meaning | Produced by |
|--------|---------|-------------|
| `business` | Full company profile | `companyReviews` (when `includeBusinessProfile` is true) |
| `review` | One customer review | all modes (company reviews, or discovery with `scrapeReviews`) |
| `search_result` | Lighter company card from a listing | `search`, `category` |
| `insight` | Per-company sentiment and investor analytics | any run with `analyzeReviews: true` |
| `benchmark` | Peer ranking across companies | `analyzeReviews: true` with 2+ companies |

#### Example: `business` row

```json
{
  "type": "business",
  "businessUnitId": "62ac9c3177fbadc00785d7fe",
  "displayName": "Apify",
  "identifyingName": "apify.com",
  "trustScore": 4.8,
  "stars": 5,
  "numberOfReviews": 811,
  "numberOfReviewsLast12Months": 536,
  "websiteUrl": "https://apify.com",
  "profileUrl": "https://www.trustpilot.com/review/apify.com",
  "countryCode": "CZ",
  "isClaimed": true,
  "isClosed": false,
  "isTemporarilyClosed": false,
  "isCollectingReviews": false,
  "categoryId": "software_company",
  "categoryName": "Software Company",
  "categories": [
    { "id": "software_company", "name": "Software Company", "isPrimary": true, "rank": null },
    { "id": "software_vendor", "name": "Software Vendor", "isPrimary": false, "rank": null }
  ],
  "breadcrumbTop": "Electronics & Technology",
  "breadcrumbMid": "Internet & Software",
  "breadcrumbBottom": "Software Company",
  "address": "Vodičkova 704/36, Nové Město",
  "city": "Prague",
  "zipCode": "110 00",
  "country": "CZ",
  "email": "hello@apify.com",
  "emails": ["hello@apify.com"],
  "phone": "",
  "phones": [],
  "websites": ["https://apify.com"],
  "verifiedPaymentMethod": false,
  "verifiedByGoogle": false,
  "verifiedUserIdentity": false,
  "scrapedAt": "2026-10-23T08:56:10.853755+00:00"
}
```

*(Category list shortened for readability.)*

#### Example: `review` row (with analysis on)

```json
{
  "type": "review",
  "reviewId": "6a4e86e661133d86dacdfa28",
  "businessUnitId": "62ac9c3177fbadc00785d7fe",
  "businessName": "Apify",
  "identifyingName": "apify.com",
  "profileUrl": "https://www.trustpilot.com/review/apify.com",
  "title": "Helped me a lot with scraping data…",
  "text": "Helped me a lot with scraping data reliably with ai for my market intelligence dashboards.",
  "rating": 5,
  "likes": 0,
  "language": "en",
  "source": "BasicLink",
  "isVerified": false,
  "verificationLevel": "invited",
  "verificationSource": "invitation",
  "publishedDate": "2026-07-08T19:20:38.000Z",
  "experiencedDate": "2026-07-08T00:00:00.000Z",
  "consumerName": "Example Reviewer",
  "consumerCountry": "HU",
  "consumerReviewsCount": 2,
  "hasReply": false,
  "replyMessage": "",
  "replyPublishedDate": "",
  "isPending": false,
  "filtered": false,
  "scrapedAt": "2026-10-23T08:57:04.821191+00:00",
  "textSentimentScore": 0.4767,
  "sentimentScore": 0.7384,
  "sentiment": "positive"
}
```

*(Reviewer name replaced and text lightly edited for privacy. The three sentiment fields appear only when `analyzeReviews` is on.)*

#### Example: `insight` row

An illustrative insight row for an anonymized company, trimmed for readability. Values are rounded.

```json
{
  "type": "insight",
  "schemaVersion": 3,
  "identifyingName": "example.com",
  "displayName": "Example Co",
  "asOf": "2026-10-23T09:36:01+00:00",
  "sample": {
    "reviewCountAnalyzed": 100,
    "profileReviewCount": 108,
    "coverage": 0.9259,
    "oldestReview": "2025-11-14T11:48:04+00:00",
    "newestReview": "2026-10-22T09:52:44+00:00",
    "reachesPriorWindow": true,
    "englishTextShare": 1,
    "confidence": "medium",
    "filtered": false,
    "scope": { "filtersApplied": [], "date": "all", "language": "all", "sort": "recency" },
    "sentimentEngine": "vader",
    "warnings": ["Only 18 negative reviews in the sample; complaint themes are anecdotal."]
  },
  "ratings": {
    "avgRating": 4.23,
    "ci95": [3.93, 4.53],
    "distribution": { "5": 0.78, "4": 0.02, "3": 0.02, "2": 0.01, "1": 0.17 },
    "netStarScore": 62,
    "polarization": 0.95,
    "trustScore": 4.2
  },
  "sentimentSummary": {
    "distribution": { "positive": 0.8, "neutral": 0.03, "negative": 0.17 },
    "avgScore": 0.5389
  },
  "momentum": {
    "ratingTrend": {
      "recent90d": { "reviews": 19, "avgRating": 4.316 },
      "prior90d": { "reviews": 56, "avgRating": 4.286 },
      "delta": null,
      "tStat": null,
      "verdict": "insufficient_data",
      "reason": "fewer_than_20_reviews_in_a_window"
    },
    "volume": {
      "recent90d": 19,
      "prior90d": 56,
      "recent90dExBursts": 19,
      "prior90dExBursts": 28,
      "burstAdjusted": true,
      "ratio": 0.68,
      "verdict": "slowing"
    },
    "monthly": [
      { "month": "2026-09", "reviews": 2, "avgRating": 3.5 },
      { "month": "2026-10", "reviews": 17, "avgRating": 4.412 }
    ]
  },
  "responsiveness": {
    "replyRate": 0.17,
    "negativeReviews": 18,
    "negativeReplyRate": 0.3333,
    "medianReplyHours": 111.8,
    "p90ReplyHours": 977,
    "negativeMedianReplyHours": 476
  },
  "themes": {
    "complaints": [
      {
        "theme": "refund / money-back friction",
        "mentions": 4,
        "groupSize": 18,
        "lowSample": false,
        "shareOfGroup": 0.2222,
        "status": "insufficient_data",
        "evidence": [
          {
            "reviewId": "6a00000000000000000000aa",
            "rating": 1,
            "publishedDate": "2026-08-14T10:02:11+00:00",
            "quote": "…asked for a refund three times and nobody answered…"
          }
        ]
      }
    ],
    "praise": [
      { "theme": "easy to use", "mentions": 23, "groupSize": 80, "shareOfGroup": 0.2875, "status": "stable" }
    ],
    "latentFriction": []
  },
  "integrity": {
    "invitedShare": 0.54,
    "sourceMix": [
      { "value": "BasicLink", "share": 0.54 },
      { "value": "Organic", "share": 0.46 }
    ],
    "invitedVsOrganicRatingGap": 1.513,
    "firstTimeReviewerShare": 0.79,
    "duplicateTextShare": 0,
    "burstDays": [
      { "date": "2026-06-18", "reviews": 18, "avgRating": 4.94, "share5Star": 0.944, "share1Star": 0 }
    ]
  },
  "signals": {
    "red": [
      { "code": "coordinated_praise_burst", "severity": "high", "detail": "18 reviews on 2026-06-18, overwhelmingly 5-star." },
      { "code": "invited_rating_inflation", "severity": "medium", "detail": "Invited reviews average +1.51 stars vs organic; headline rating may be flattered." }
    ],
    "green": [
      { "code": "strong_net_star_score", "severity": "medium", "detail": "Net star score +62 (share 4-5 star minus share 1-2 star)." }
    ]
  },
  "executiveSummary": "Example Co: 100 reviews analyzed (93% of 108 on profile), confidence medium. Average rating 4.23/5 (95% CI 3.93-4.53), net star score +62. Profile TrustScore 4.2. Rating trend: insufficient data; review volume: slowing. Replies to 33% of negative reviews. 4 red flag(s), 1 positive signal(s). Caveat: Only 18 negative reviews in the sample; complaint themes are anecdotal. Main concern: 18 reviews on 2026-06-18, overwhelmingly 5-star."
}
```

The real row also contains `distinctiveTerms`, `topicShares`, `audience`, `keyTakeaways`, `quarterly`, and a few legacy summary fields (`reviewCountAnalyzed`, `sentimentDistribution`, `avgSentimentScore`, `ratingDistribution`, `verifiedShare`, `replyRate`, `avgReplyHours`, `topTopics`, `risingComplaints`, `topPraise`, `trendLast90Days`) kept for backward compatibility.

#### Example: `benchmark` row (shape)

```json
{
  "type": "benchmark",
  "schemaVersion": 3,
  "peerCount": 3,
  "note": "Percentiles are relative to this peer set only (100 = best). Low-confidence companies are ranked but should be read cautiously.",
  "companies": [
    {
      "identifyingName": "example-a.com",
      "displayName": "Example A",
      "reviewCountAnalyzed": 480,
      "confidence": "high",
      "metrics": { "avgRating": 4.41, "netStarScore": 74, "negativeReplyRate": 0.82, "invitedShare": 0.12 },
      "percentiles": { "avgRating": 100, "netStarScore": 100, "negativeReplyRate": 100, "invitedShare": 100 },
      "overallPercentile": 91.7
    }
  ]
}
```

*(Trimmed to one company and four metrics. Benchmarked metrics: `avgRating`, `netStarScore`, `ratingDelta90d`, `negativeReplyRate`, `negativeMedianReplyHours`, `invitedShare`, `duplicateTextShare`, `redFlagCount`.)*

#### Field reference

<details>
<summary><strong><code>business</code> fields</strong></summary>

| Field | Type | Description |
|-------|------|-------------|
| `type` | string | Always `"business"` |
| `businessUnitId` | string | Trustpilot internal company ID |
| `displayName` | string | Public company name |
| `identifyingName` | string | Slug used in `/review/{identifyingName}` |
| `trustScore` | number | TrustScore (e.g. 4.8) |
| `stars` | number | Star display value |
| `numberOfReviews` | integer | Total public reviews |
| `numberOfReviewsLast12Months` | integer | Rolling 12-month count when available |
| `websiteUrl`, `websites` | string, array | Company website(s) |
| `profileUrl`, `profileImageUrl` | string | Trustpilot profile URL and logo |
| `countryCode` | string | ISO country of the business |
| `isClaimed` | boolean | Profile claimed by the business |
| `isClosed`, `isTemporarilyClosed` | boolean | Status flags |
| `isCollectingReviews` | boolean | Whether the company is actively collecting reviews |
| `locationsCount` | integer | Number of locations when present |
| `categoryId`, `categoryName`, `categories` | string, array | Primary category and full list (`id`, `name`, `isPrimary`, `rank`) |
| `breadcrumbTop`, `breadcrumbMid`, `breadcrumbBottom` | string | Category breadcrumb |
| `address`, `city`, `zipCode`, `country` | string | Location when published |
| `email`, `emails`, `phone`, `phones` | string, array | Contacts when published |
| `verifiedPaymentMethod`, `verifiedByGoogle`, `verifiedUserIdentity` | boolean | Verification badges |
| `scrapedAt` | string | ISO timestamp |

</details>

<details>
<summary><strong><code>review</code> fields</strong></summary>

| Field | Type | Description |
|-------|------|-------------|
| `type` | string | Always `"review"` |
| `reviewId` | string | Unique review ID |
| `businessUnitId`, `businessName`, `identifyingName`, `profileUrl` | string | Parent company reference |
| `title`, `text` | string | Review title and body |
| `rating` | integer | 1–5 stars |
| `likes` | integer | Helpful votes |
| `language` | string | Review language code |
| `source` | string | How the review was created, e.g. `Organic` or `BasicLink` (company invitation link) |
| `isVerified`, `verificationLevel`, `verificationSource` | boolean, string | Verification details. `invited` means the company requested the review |
| `publishedDate`, `experiencedDate`, `updatedDate` | string | ISO dates |
| `consumerId`, `consumerName`, `consumerCountry` | string | Reviewer identity (public) and country |
| `consumerReviewsCount`, `consumerIsVerified`, `consumerImageUrl` | integer, boolean, string | Reviewer stats and avatar |
| `hasReply`, `replyMessage`, `replyPublishedDate` | boolean, string | Company reply |
| `isPending`, `filtered` | boolean | Status flags |
| `sentiment` | string | `positive`, `neutral`, or `negative` (analysis on) |
| `sentimentScore` | number | Blended star-and-text sentiment in −1…1 (analysis on) |
| `textSentimentScore` | number or null | Text-only sentiment in −1…1. `null` for non-English text (analysis on) |
| `scrapedAt` | string | ISO timestamp |

</details>

<details>
<summary><strong><code>search_result</code> fields</strong></summary>

| Field | Type | Description |
|-------|------|-------------|
| `type` | string | Always `"search_result"` |
| `source` | string | `search_api`, `search_html`, or `category` |
| `query` | string | Search query or category slug used |
| `businessUnitId`, `displayName`, `identifyingName` | string | Company ID, name, slug |
| `trustScore`, `stars`, `numberOfReviews` | number | Scores and counts |
| `profileUrl`, `websiteUrl` | string | Links |
| `email`, `emails`, `phone`, `phones`, `websites` | string, array | Contacts when Trustpilot exposes them |
| `address`, `city`, `zipCode`, `country`, `countryCode` | string | Location when present |
| `categoryId`, `categoryName`, `categories` | string, array | Category data |
| `isClaimed`, `verified` | boolean | Claimed / verified flags when present |
| `scrapedAt` | string | ISO timestamp |

Many search hits omit email and phone. They are filled only when Trustpilot publishes them.

</details>

<details>
<summary><strong><code>insight</code> fields</strong></summary>

| Field | Type | Description |
|-------|------|-------------|
| `type`, `schemaVersion` | string, integer | `"insight"` and the analytics schema version (currently 3) |
| `identifyingName`, `displayName`, `businessUnitId` | string | Company reference |
| `asOf` | string | Reference time for the 90-day windows |
| `sample` | object | `reviewCountAnalyzed`, `profileReviewCount`, `coverage`, `oldestReview`, `newestReview`, `reachesPriorWindow`, `englishTextShare`, `confidence`, `filtered`, `scope`, `sentimentEngine` (`vader` or `rating_only`), `warnings` |
| `ratings` | object | `avgRating`, `ci95`, `distribution`, `netStarScore`, `polarization`, `trustScore` |
| `sentimentSummary` | object | `distribution` (positive/neutral/negative shares) and `avgScore` |
| `momentum` | object | `ratingTrend`, `volume`, `monthly`, `quarterly` |
| `responsiveness` | object | `replyRate`, `negativeReviews`, `negativeReplyRate`, `negativeReplyRateLast90d`, `medianReplyHours`, `p90ReplyHours`, `negativeMedianReplyHours`, `avgReplyHours` |
| `themes` | object | `complaints`, `praise`, `latentFriction`, each a list of themes with `mentions`, `groupSize`, `lowSample`, `shareOfGroup`, `recentShare`, `priorShare`, `status`, `evidence` |
| `distinctiveTerms` | object | `complaints` and `praise` term lists with review counts and a z-score |
| `topicShares` | object | Share of all reviews mentioning common topics (service, payment, pricing, onboarding, and more) |
| `integrity` | object | `invitedShare`, `sourceMix`, `verifiedShare`, `invitedVsOrganicRatingGap`, `firstTimeReviewerShare`, `duplicateTextShare`, `contentless5StarShare`, `ratingTextMismatchShare`, `burstDays`, `flags` |
| `audience` | object | `countries`, `languages`, `usageSignals` (business/team use, scale, developer) |
| `signals` | object | `red` and `green` lists of `{code, severity, detail}` |
| `keyTakeaways` | array | Top takeaways as plain sentences |
| `executiveSummary` | string | One-paragraph summary including caveats |
| `scrapedAt` | string | ISO timestamp |

</details>

#### Dataset views in the Apify console

- **All rows:** mixed `business`, `review`, `search_result`, and `insight` rows
- **Reviews:** review columns including sentiment
- **Businesses & search hits:** profiles and discovery results
- **Insights:** one line per company with rating, trend, reply rate, integrity and signals
- **Insight details:** themes with evidence, distinctive terms, momentum series, audience
- **Benchmark:** peer ranking when 2+ companies are analyzed

***

### Run it with the API, Python, or JavaScript

#### cURL

```bash
curl "https://api.apify.com/v2/acts/devil_port369-owner~trustpilot-reviews/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"mode": "companyReviews", "companyUrls": ["apify.com"], "maxReviews": 50}'
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("devil_port369-owner/trustpilot-reviews").call(run_input={
    "mode": "companyReviews",
    "companyUrls": ["apify.com"],
    "maxReviews": 0,
    "sort": "recency",
    "analyzeReviews": True,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["type"] == "insight":
        print(item["executiveSummary"])
        for s in item["signals"]["red"]:
            print(s["severity"], s["code"], s["detail"])
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });
const run = await client.actor('devil_port369-owner/trustpilot-reviews').call({
  mode: 'companyReviews',
  companyUrls: ['apify.com'],
  maxReviews: 0,
  sort: 'recency',
  analyzeReviews: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const insight = items.find((i) => i.type === 'insight');
const reviews = items.filter((i) => i.type === 'review');
```

Schedule recurring runs under **Schedules** and connect results to Google Sheets, Slack, Make, n8n, Zapier, or webhooks for continuous reputation tracking.

***

### Use with AI agents and MCP

This Actor is built to be called by LLM agents: flat typed output, a small set of enumerated inputs, predictable defaults, and an insight row that already carries the caveats an agent needs to avoid over-claiming.

#### Connect through the Apify MCP server

Add this Actor to your MCP client (Claude, Cursor, VS Code, and others) through the [Apify MCP server](https://mcp.apify.com):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=devil_port369-owner/trustpilot-reviews",
      "headers": { "Authorization": "Bearer YOUR_API_TOKEN" }
    }
  }
}
```

#### Agent decision guide

| User intent | Set | Required field |
|-------------|-----|----------------|
| "What do customers say about *company X*?" | `mode: companyReviews` | `companyUrls` |
| "What's the TrustScore of *company X*?" | `companyReviews`, `maxReviews: 1` | `companyUrls` |
| "Is *company X*'s rating trustworthy?" / "Reputation risk?" | `companyReviews`, `analyzeReviews: true`, `maxReviews: 0`, `sort: recency` | `companyUrls` |
| "Compare *A*, *B*, *C*" | as above with several `companyUrls` | `companyUrls` (read the `benchmark` row) |
| "Find companies that do *Y* in *country Z*" | `mode: search` | `searchQueries`, `country` |
| "List companies in category *C*" | `mode: category` | `categories` |
| "Find companies and analyze their reviews" | `search`, `scrapeReviews: true`, `analyzeReviews: true` | `searchQueries` |
| "Only complaints" | add `stars: ["1","2"]` (analysis is then disabled for trends) | – |
| "Only recent" | add `date: "last30days"` | – |

#### Agent rules of thumb

1. **Always set `mode` first**, then the matching array. Each array is a list of strings, even for a single value.
2. **Start small, then expand:** `maxReviews` of 20–50 for exploration; use `0` or 500+ for any trend or risk claim.
3. **Branch on `item.type`:** `business` for profile facts, `review` for review text, `insight` for analysis, `benchmark` for rankings, `search_result` for lead lists.
4. **Read `sample.confidence` and `sample.warnings` before quoting any insight number.** Do not state a trend when `momentum.ratingTrend.verdict` is `insufficient_data`.
5. **Do not combine `analyzeReviews` with review filters if you want company-level conclusions.** Filtered runs are marked `sample.filtered: true` and trend verdicts are disabled.
6. **Treat `integrity` flags as leads to investigate, not accusations.** Say "flagged for" rather than "fake".
7. **Cite evidence:** theme `evidence` entries carry `reviewId`, date, and a snippet. Every row has `profileUrl` and `scrapedAt` for attribution and freshness.
8. **`stars` takes strings** (`["1","2"]`), not integers.
9. **Empty `email`/`phone` is normal.** It means Trustpilot does not publish them for that company.

#### Minimal agent payloads

```json
{ "mode": "companyReviews", "companyUrls": ["apify.com"], "maxReviews": 50, "includeBusinessProfile": true }
```

```json
{ "mode": "companyReviews", "companyUrls": ["apify.com"], "maxReviews": 0, "sort": "recency", "analyzeReviews": true }
```

```json
{ "mode": "search", "searchQueries": ["software company Czech Republic"], "country": "CZ", "maxResults": 25, "scrapeReviews": false }
```

***

### Frequently asked questions

#### Is scraping Trustpilot legal?

This Actor collects publicly visible data. Legality depends on your use case, jurisdiction, and Trustpilot's terms. Review the [Compliance](#compliance) section, avoid using personal data beyond what the law allows, and consult a legal professional for your situation.

#### Do I need a Trustpilot account or API key?

No. The Actor reads public profile, search, and category pages.

#### Is this a Trustpilot API alternative?

Yes. It gives you structured review and company data without a Trustpilot business account or API approval process.

#### How does the Trustpilot sentiment analysis work?

Each review gets a score that blends its star rating with the sentiment of its English text (scored with VADER). The Actor then summarizes the whole sample into an `insight` row with ratings, trends, themes, reply behaviour, and integrity signals. See [How does Trustpilot review sentiment analysis work?](#how-does-trustpilot-review-sentiment-analysis-work).

#### Does the analysis use an LLM or send my data to an AI service?

No. The analysis runs inside the Actor using rule-based sentiment and statistics. No review text is sent to an external AI service, and no extra API keys are needed.

#### Which languages does sentiment analysis support?

Text sentiment is English-only. Reviews in other languages are scored from the star rating alone (`textSentimentScore` is `null`), and `sample.englishTextShare` shows how much of the sample had text scored.

#### What does `insufficient_data` mean?

The sample cannot support a reliable trend, for example it does not reach back 180 days, has fewer than 20 reviews in a 90-day window, or was filtered. The `reason` field says which. Raise `maxReviews` (use `0` for large companies) and remove review filters to get a verdict.

#### Why were trend verdicts disabled on my run?

Either you used review filters (`stars`, `topics`, `verifiedOnly`, `withReplies`, `reviewSearch`), sorted by something other than recency, or the sample was too short. The insight row says which in `sample.warnings`.

#### What is the "net star score"?

The share of 4–5 star reviews minus the share of 1–2 star reviews, from −100 to +100. It is more sensitive than the average because it shows how many customers are clearly unhappy.

#### What is the difference between invited and organic reviews?

Invited reviews are requested by the company (for example via an email link, shown as `source: BasicLink` and `verificationLevel: invited`). Organic reviews are written without an invitation. Invited reviews tend to be more positive, so a large gap between them is worth investigating.

#### Does a red flag mean the reviews are fake?

No. Integrity signals are heuristics, such as an unusual one-day spike or many first-time reviewers. They show where a human should look, and they can have innocent causes like a marketing campaign.

#### How many reviews do I need for a good analysis?

At least a few hundred for trends, and the more the better. For small companies, the whole profile may be only 100 reviews, and `sample.coverage` shows how much of the profile you captured. Confidence is `low` below 100 reviews, `medium` from 100, and `high` from 400.

#### How many reviews can I get per company?

Up to what Trustpilot exposes publicly. Set `maxReviews` to `0` for the maximum, or use `stars`, `date`, and `topics` filters to cover different slices of a large review set (note that filters disable company-level analysis).

#### Can I get only negative reviews?

Yes. Use `"stars": ["1", "2"]`. Combine with `date`, `verifiedOnly`, or `topics` to narrow further.

#### Can I scrape reviews in other languages?

Yes. Set `languages` to an ISO code such as `es`, `de`, or `fr`. The default is `en`.

#### Why are email and phone empty on some rows?

They are returned only when Trustpilot publishes them for that company. Many businesses hide them.

#### Why did my search return companies from other countries?

Search follows Trustpilot's index and your `country` value. Use a more specific query and set `country` to the market you care about.

#### What is `identifyingName`?

The company slug in `https://www.trustpilot.com/review/{identifyingName}`, usually the domain (`apify.com`, `www.adorama.com`).

#### What is the difference between `business` and `search_result`?

`business` is a full profile from a company's review page. `search_result` is a lighter card from a search or category listing.

#### Does `scrapeReviews` work in company mode?

No. Company mode always scrapes reviews for the URLs you give. `scrapeReviews` only extends `search` and `category` runs.

#### Can I run it on a schedule and send results to my tools?

Yes. Use Apify Schedules, webhooks, and integrations (Google Sheets, Slack, Make, n8n, Zapier) or pull data through the API.

#### I found a bug or need extra fields. What should I do?

Open an issue from the Actor's **Issues** tab. Custom fields, modifications, and private versions are available on request via the [DataFusionX contact page](https://mk1995.github.io/contact/).

***

### Compliance

- This Actor collects publicly available data. You are responsible for using it in line with Trustpilot's terms and applicable laws such as GDPR and CCPA.
- Review rows include public reviewer names and countries, which can be personal data. Minimize, secure, and retain them only as needed, and honor deletion requests. Insight rows contain no reviewer names; evidence quotes carry only a review ID.
- Insight and benchmark rows are statistical summaries of a review sample for research purposes. They are not investment, legal, or financial advice, and integrity signals are not findings of fraud.
- Not affiliated with, endorsed by, or sponsored by Trustpilot A/S. "Trustpilot" and "TrustScore" are trademarks of their owner.
- This section is general information, not legal advice.

***

### More Actors by DataFusionX

Looking for more production-grade scrapers and data pipelines? Browse the full portfolio: **[apify.com/devil_port369-owner](https://apify.com/devil_port369-owner)**

Need a custom Actor, maintenance, or integrations with Make, n8n, Zapier, CRMs, or Google Sheets? **[Submit a project request](https://mk1995.github.io/contact/)**.

***

**Keywords:** Trustpilot scraper, Trustpilot reviews scraper, Trustpilot sentiment analysis, Trustpilot review analytics, scrape Trustpilot reviews, Trustpilot API alternative, Trustpilot company data, Trustpilot category scraper, Trustpilot search scraper, TrustScore extractor, review sentiment analysis, review authenticity check, reputation monitoring, competitor benchmarking, investor due diligence, voice of customer, customer sentiment data, Trustpilot MCP, AI agent review data

# Actor input Schema

## `mode` (type: `string`):

companyReviews = reviews for company domains/URLs. search = find businesses by keyword. category = list businesses in a Trustpilot category.

## `companyUrls` (type: `array`):

Company domains or profile URLs, e.g. www.adorama.com or https://www.trustpilot.com/review/www.adorama.com

## `searchQueries` (type: `array`):

Semantic/business search queries (e.g. Insurance agency in United States).

## `categories` (type: `array`):

Category slugs or URLs, e.g. car_dealer or https://www.trustpilot.com/categories/car_dealer

## `maxReviews` (type: `integer`):

0 = all reviews available on public pages (Trustpilot limits how deep pagination goes, so very large review sets may be only partly available). For analyzeReviews use 0 or at least 500 so 90-day trends can be computed.

## `maxResults` (type: `integer`):

Max business rows for search or category mode. 0 = unlimited within maxPages.

## `maxPages` (type: `integer`):

Pagination depth for search and category modes.

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

Only reviews with these star ratings (1-5). Empty = all.

## `date` (type: `string`):

Review recency filter.

## `languages` (type: `string`):

ISO language code for reviews (e.g. en, es, de). Use all for every language when supported.

## `topics` (type: `array`):

Topic ids from Trustpilot (e.g. delivery_service, value_for_money, customer_service, price, staff).

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

Only include verified reviews.

## `withReplies` (type: `boolean`):

Filter to reviews that have a business reply.

## `reviewSearch` (type: `string`):

Keyword filter applied inside review text on the company profile.

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

Review sort order.

## `includeBusinessProfile` (type: `boolean`):

Push one business summary row before that company's reviews.

## `country` (type: `string`):

2-letter country for search ranking (US, GB, DE…). Used by search mode.

## `scrapeReviews` (type: `boolean`):

When mode is search or category, also scrape reviews for each company found in the listings. Uses maxReviews and the review filters below.

## `analyzeReviews` (type: `boolean`):

When true, every review gets sentiment fields and one type=insight row is pushed per company (rating confidence interval, 90-day trend, complaint/praise themes with evidence quotes, reply behaviour, review-integrity flags). With 2+ companies a type=benchmark row ranks them. No LLM. Best results: sort by recency, no star/verified/reply/keyword filters, and Max reviews = 0 or at least 500; filtered or short samples automatically disable trend statistics. Text sentiment is English-only.

## Actor input object example

```json
{
  "mode": "companyReviews",
  "companyUrls": [
    "www.apify.com",
    "https://www.trustpilot.com/review/cosmofeed.com"
  ],
  "searchQueries": [
    "Insurance agency in United States"
  ],
  "maxReviews": 100,
  "maxResults": 50,
  "maxPages": 5,
  "date": "all",
  "languages": "en",
  "verifiedOnly": false,
  "withReplies": false,
  "reviewSearch": "",
  "sort": "recency",
  "includeBusinessProfile": true,
  "country": "US",
  "scrapeReviews": false,
  "analyzeReviews": false
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `businesses` (type: `string`):

No description

## `insights` (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 = {
    "companyUrls": [
        "www.apify.com",
        "https://www.trustpilot.com/review/cosmofeed.com"
    ],
    "searchQueries": [
        "Insurance agency in United States"
    ],
    "country": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("devil_port369-owner/trustpilot-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 = {
    "companyUrls": [
        "www.apify.com",
        "https://www.trustpilot.com/review/cosmofeed.com",
    ],
    "searchQueries": ["Insurance agency in United States"],
    "country": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("devil_port369-owner/trustpilot-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 '{
  "companyUrls": [
    "www.apify.com",
    "https://www.trustpilot.com/review/cosmofeed.com"
  ],
  "searchQueries": [
    "Insurance agency in United States"
  ],
  "country": "US"
}' |
apify call devil_port369-owner/trustpilot-reviews --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devil_port369-owner/trustpilot-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/rH12fB6onKV7JWOgC/builds/pKc9jbQ79VEfl9nxD/openapi.json
