Reputation & Reviews

How do you get Trustpilot company reviews via API?

Call ReefAPI's trustpilot company/reviews action with a company domain and read the company profile plus review rows back as JSON. The parts that break DIY review pipelines are not the fetch: they are the counts, which are scoped to whatever filter you sent, and the verification fields, which do not mean what their names suggest.

Trustpilot engineLive JSON5 steps1,000 free credits

This guide demonstrates the real Trustpilot API engine with a captured response from . The example is only published because the engine passed the SEO snapshot gate.

Use case

Review monitoring, competitor research, reputation dashboards and customer voice analysis.

Step by step

Call the live endpoint

  1. 1

    Identify the company by domain

    company takes the website domain (gossby.com), the full Trustpilot review URL, or the Trustpilot slug. The domain is the version that stays stable when Trustpilot restyles its URLs.

  2. 2

    Call trustpilot/v1/company/reviews

    maxReviews defaults to 200, which is also what a single logged-out view holds. Ask for more and the engine assembles it from narrower views; the per-review scraped_via_filter tells you which one produced each row.

  3. 3

    Read total_reviews as filter-scoped

    If you sent rating, date_posted, query or languages, total_reviews counts the filtered set only. The lifetime figure is company.number_of_reviews, and company.rating_distribution breaks it into five buckets that sum to it.

  4. 4

    Classify with verification_level and source

    not-verified plus Organic is an unsolicited review; invited plus BasicLink is one the company requested. is_verified was false across our whole sample and should not be your filter.

  5. 5

    Check status and notes before trusting a batch

    status PARTIAL plus a notes line means you got a truncated slice on purpose. meta.summary.blocked and error_breakdown tell you whether a failure was a real block or an empty filtered view.

Code

Copy the request

These snippets use the captured request params for trustpilot/v1/company/reviews.

curl -X POST https://api.reefapi.com/trustpilot/v1/company/reviews \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"company":"amazon.com","maxReviews":40}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.trustpilot.company/reviews with {"company":"amazon.com","maxReviews":40}.
Real response

Captured output from ReefAPI

Captured on UTC. The response below is the committed snapshot, including the API envelope and metadata.

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/trustpilot/v1/company/reviews",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "company": "amazon.com",
    "maxReviews": 40
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "trustpilot",
    "endpoint": "company/reviews",
    "mode": "live",
    "latency_ms": 7825,
    "record_count": 40,
    "bytes": 53928,
    "cache_hit": false,
    "summary": {
      "companies": 1,
      "succeeded": 1,
      "not_found": 0,
      "blocked": 0,
      "failed": 0,
      "success_rate_pct": 100,
      "total_reviews": 40,
      "avg_completeness_pct": 0.1,
      "error_breakdown": {},
      "retries_used": 0
    },
    "charged_credits": 1,
    "version": "2.0.0"
  },
  "data": {
    "results": [
      {
        "company": {
          "domain": "www.amazon.com",
          "business_unit_id": "46ad346800006400050092d0",
          "name": "Amazon",
          "trust_score": 1.6,
          "stars": 1.5,
          "number_of_reviews": 49359,
          "number_of_reviews_last_12_months": 9333,
          "rating_distribution": {
            "one": "[trimmed-depth]",
            "two": "[trimmed-depth]",
            "three": "[trimmed-depth]",
            "four": "[trimmed-depth]",
            "five": "[trimmed-depth]",
            "total": "[trimmed-depth]"
          },
          "categories": [
            "[trimmed-depth]",
            "[trimmed-depth]",
            "[trimmed-depth]"
          ],
          "is_claimed": true,
          "is_closed": false,
          "is_temporarily_closed": false,
          "is_collecting_reviews": false,
          "claimed_date": "2015-02-18T19:08:43.000Z",
          "reply_behavior": {
            "reply_percentage": "[trimmed-depth]",
            "average_days_to_reply": "[trimmed-depth]",
            "negative_reviews_with_replies": "[trimmed-depth]",
            "total_negative_reviews": "[trimmed-depth]",
            "last_reply_to_negative": "[trimmed-depth]"
          },
          "verification": {
            "verified_payment_method": "[trimmed-depth]",
            "verified_user_identity": "[trimmed-depth]",
            "verified_by_google": "[trimmed-depth]"
          },
          "locations_count": 0,
          "website_url": "https://www.amazon.com",
          "website_title": "www.amazon.com",
          "profile_image_url": "//s3-eu-west-1.amazonaws.com/tpd/screenshots/46ad346800006400050092d0/198x149.png",
          "contact_email": null,
          "contact_phone": null,
          "contact_address": null,
          "contact_city": null,
          "contact_country": "GB",
          "contact_zip": null,
          "rankings": [],
          "breadcrumb": [
            "[trimmed-depth]",
            "[trimmed-depth]",
            "[trimmed-depth]"
          ],
          "topics": [
            "[trimmed-depth]",
            "[trimmed-depth]",
            "[trimmed-depth]"
          ],
          "description": null,
          "ai_summary": null
        },
        "reviews": [
          {
            "review_id": "[trimmed-depth]",
            "review_url": "[trimmed-depth]",
            "company_domain": "[trimmed-depth]",
            "company_name": "[trimmed-depth]",
            "rating": "[trimmed-depth]",
            "title": "[trimmed-depth]",
            "text": "[trimmed-depth]",
            "language": "[trimmed-depth]",
            "published_date": "[trimmed-depth]",
            "experience_date": "[trimmed-depth]",
            "updated_date": "[trimmed-depth]",
            "is_verified": "[trimmed-depth]",
            "verification_level": "[trimmed-depth]",
            "likes": "[trimmed-depth]",
            "source": "[trimmed-depth]",
            "reviewer_name": "[trimmed-depth]",
            "reviewer_country": "[trimmed-depth]",
            "reviewer_id": "[trimmed-depth]",
            "reviewer_review_count": "[trimmed-depth]",
            "reply_text": "[trimmed-depth]",
            "reply_published_date": "[trimmed-depth]",
            "scraped_via_filter": "[trimmed-depth]",
            "scraped_at_page": "[trimmed-depth]",
            "is_pending": "[trimmed-depth]",
            "is_flagged": "[trimmed-depth]",
            "reviewer_local_review_count": "[trimmed-depth]"
          },
          {
            "review_id": "[trimmed-depth]",
            "review_url": "[trimmed-depth]",
            "company_domain": "[trimmed-depth]",
            "company_name": "[trimmed-depth]",
            "rating": "[trimmed-depth]",
            "title": "[trimmed-depth]",
            "text": "[trimmed-depth]",
            "language": "[trimmed-depth]",
            "published_date": "[trimmed-depth]",
            "experience_date": "[trimmed-depth]",
            "updated_date": "[trimmed-depth]",
            "is_verified": "[trimmed-depth]",
            "verification_level": "[trimmed-depth]",
            "likes": "[trimmed-depth]",
            "source": "[trimmed-depth]",
            "reviewer_name": "[trimmed-depth]",
            "reviewer_country": "[trimmed-depth]",
            "reviewer_id": "[trimmed-depth]",
            "reviewer_review_count": "[trimmed-depth]",
            "reply_text": "[trimmed-depth]",
            "reply_published_date": "[trimmed-depth]",
            "scraped_via_filter": "[trimmed-depth]",
            "scraped_at_page": "[trimmed-depth]",
            "is_pending": "[trimmed-depth]",
            "is_flagged": "[trimmed-depth]",
            "reviewer_local_review_count": "[trimmed-depth]"
          },
          {
            "review_id": "[trimmed-depth]",
            "review_url": "[trimmed-depth]",
            "company_domain": "[trimmed-depth]",
            "company_name": "[trimmed-depth]",
            "rating": "[trimmed-depth]",
            "title": "[trimmed-depth]",
            "text": "[trimmed-depth]",
            "language": "[trimmed-depth]",
            "published_date": "[trimmed-depth]",
            "experience_date": "[trimmed-depth]",
            "updated_date": "[trimmed-depth]",
            "is_verified": "[trimmed-depth]",
            "verification_level": "[trimmed-depth]",
            "likes": "[trimmed-depth]",
            "source": "[trimmed-depth]",
            "reviewer_name": "[trimmed-depth]",
            "reviewer_country": "[trimmed-depth]",
            "reviewer_id": "[trimmed-depth]",
            "reviewer_review_count": "[trimmed-depth]",
            "reply_text": "[trimmed-depth]",
            "reply_published_date": "[trimmed-depth]",
            "scraped_via_filter": "[trimmed-depth]",
            "scraped_at_page": "[trimmed-depth]",
            "is_pending": "[trimmed-depth]",
            "is_flagged": "[trimmed-depth]",
            "reviewer_local_review_count": "[trimmed-depth]"
          }
        ],
        "scraped_count": 40,
        "total_reviews": 49359,
        "completeness_pct": 0.1,
        "status": "PARTIAL",
        "attempts": 1,
        "notes": "Limited by maxReviews=40; 49359 reviews exist. Raise maxReviews (up to 200) for more."
      }
    ],
    "summary": {
      "companies": 1,
      "succeeded": 1,
      "not_found": 0,
      "blocked": 0,
      "failed": 0,
      "success_rate_pct": 100,
      "total_reviews": 40,
      "avg_completeness_pct": 0.1,
      "error_breakdown": {},
      "retries_used": 0
    }
  }
}
Manual way

Why this is hard manually

The first surprise is not a block, it is a ceiling. A logged-out Trustpilot listing serves 20 reviews per page and stops at page 10, so one view yields at most 200 reviews. A company with 37,662 reviews will never hand you 37,662 through that listing no matter how patiently you paginate. Everything past 200 has to be assembled from narrower views, and once you do that, every stored review needs to remember which view produced it or you will double-count.

The second is a counting trap that produces confidently wrong dashboards. We asked gossby.com for reviews with no filter and got total_reviews 37,662. The identical request with rating '1' returned total_reviews 3,924 - which is exactly the company's one-star bucket in rating_distribution. The field is the size of your filtered result set, not the size of the company's review history. Cache it from a filtered call and you will publish a review count that is off by an order of magnitude.

The third is 'verified', which on Trustpilot is at least two different ideas wearing one word: whether the reviewer proved a purchase, and whether the company invited the review in the first place. A single boolean cannot carry both, and the field named is_verified carries neither reliably.

ReefAPI way

Why ReefAPI solves it

Read verification_level and source, not is_verified. In our ten-review sample is_verified was false on every single row, including the rows whose verification_level read 'invited'. The two fields that actually separate them are verification_level ('not-verified' or 'invited') and source ('Organic' or 'BasicLink'), and they moved together: Organic reviews were not-verified, BasicLink reviews were invited. Organic means the reviewer found the profile themselves; BasicLink means the company sent them a link. For competitor analysis that distinction is the whole story, and it is the one is_verified hides.

Trust score and star rating are two different numbers. gossby.com returns trust_score 4.2 and stars 4.0 in the same profile - stars is the rounded badge, trust_score is the weighted figure that actually moves. Chart trust_score, display stars.

The profile carries a reply_behavior block that is normally a whole scraping project on its own: reply_percentage 99.55, average_days_to_reply 0.24, negative_reviews_with_replies 221 against total_negative_reviews 222, and the timestamp of the last reply to a negative review. For a reputation dashboard that is 'does this brand answer its critics', precomputed.

Every review row records the view it came from in scraped_via_filter - 'default' on an unfiltered pull, 'stars=1' when a rating filter was applied - so cross-view dedupe is a field comparison rather than guesswork. status reads PARTIAL whenever you asked for fewer reviews than exist, notes says so in plain English ('Limited by maxReviews=10; 3924 reviews exist'), and completeness_pct is the ratio, which is why it reads 0.3 when you take 10 of 3,924.

Our own mistake, worth knowing before you hit it: we sent verified:true together with date_posted:'last_30_days'. That combination narrows some companies to a view with no reviews in it, and our engine spent 50.5 seconds on it before returning TARGET_BLOCKED with error_breakdown {EMPTY: 1}. Nothing was blocked. The filter combination simply had no matching reviews, and we mislabelled an empty result as a wall. Apply one narrowing filter at a time and the same company answers in about two seconds.

Two smaller behaviours we measured rather than assumed: includeCompany:false still returned the company profile in our test, so do not write logic that depends on its absence; and the AI-derived fields on the profile - topics[].summary, topics[].sentiment, description and ai_summary - came back null while the topic labels themselves ('Delivery service', 'Customer service', 'Price') were populated. You get the taxonomy, not the summaries.

FAQ

Questions developers ask

Do I need a Trustpilot account or business login?

No. You send a ReefAPI key and a public company domain. No Trustpilot account, business seat or Trustpilot API key is involved.

Why did total_reviews drop from 37,662 to 3,924 between two calls?

Because the second call filtered to one-star reviews. total_reviews is the size of the filtered set, and 3,924 matched the company's one-star bucket in rating_distribution exactly. Use company.number_of_reviews for the lifetime figure.

Why is is_verified false on reviews that look verified?

In our sample it was false on all ten rows, including rows whose verification_level said 'invited'. Read verification_level and source instead: not-verified/Organic means the reviewer came on their own, invited/BasicLink means the company sent a link.

What is the difference between trust_score and stars?

stars is the rounded badge (4.0 for gossby.com), trust_score is the weighted number behind it (4.2). They disagree by design. Store trust_score if you want to see movement over time.

I got TARGET_BLOCKED but the company loads fine in a browser. What happened?

Most likely you combined verified with date_posted or query and narrowed the company to an empty view. Our engine takes about 50 seconds on that path and then reports it as blocked, which is our mislabel rather than a wall. The tell is error_breakdown EMPTY with retries_used 2. Drop one filter and it answers normally.

Can I get more than 200 reviews for one company?

Yes. maxReviews accepts up to 20,000 and the engine assembles the extra rows from narrower views, because a single logged-out listing tops out at 20 per page across 10 pages. Dedupe on review_id and keep scraped_via_filter so you can see which view each row came from.

Does the locale parameter change which reviews I get?

Not today. en-US is fully served and other locales are accepted but currently resolve to the en-US view. If you need language filtering right now, use languages with an ISO code, or 'all'.