TrustRadius Reviews API
B2B software reviews as structured JSON, carrying the field most review data leaves out: which alternatives the buyer weighed, and why they picked the one they picked. Alongside it come per-feature numeric scores rather than one overall star, usability and support and implementation ratings, return-on-investment notes, pros and cons as discrete points, a replaced-an-incumbent flag, and the reviewer's job title, company size and industry. Two modes: point it at a product page and it discovers that product's reviews, or hand it individual review URLs. Billed per review returned, MCP ready.
MEDIAN LAGon demand
FIELDS52
Input parameters
| PARAMETER | TYPE | REQ | DEFAULT | DESCRIPTION |
|---|---|---|---|---|
| mode | enum | yes | review | product discovers every review on a product's review page. review collects individual review URLs, one review each. Both modes return the same fields and are billed the same way; product is the easier starting point. |
| productUrls | string[] | no | — | Product review pages or bare product slugs, for example https://www.trustradius.com/products/asana/reviews or just asana. Up to 50 per run. Required when mode is product, ignored otherwise. |
| reviewUrls | string[] | no | — | Individual review URLs, which follow the pattern /reviews/ |
| maxReviewsPerProduct | integer | no | 50 | How many reviews to return per product in product mode, from 1 to 2000. You are charged once per review returned, so this is the ceiling on what a run can cost. — drives your bill |
| pages | integer | no | — | How many pages of a product's review list to walk in product mode, from 1 to 25. Higher values reach older reviews. It does not raise the cap; maxReviewsPerProduct still stops the run. |
Output schema
| FIELD | TYPE | DESCRIPTION | NULLABLE |
|---|---|---|---|
| result_type | string | review or error. Error rows share the dataset with review rows, so split on this before you aggregate anything. | no |
| reviewId | string | Stable identifier for the review, the same slug that ends its URL. | no |
| productId | string | Stable identifier for the reviewed product. This is the canonical slug, which is not always the slug you asked for. | yes |
| productName | string | Display name of the reviewed product. | no |
| productUrl | string | The product review page this review belongs to. | yes |
| reviewUrl | string | Direct link to the individual review. Keep it; it is the input for a later review-mode re-pull, and the natural upsert key. | no |
| reviewTitle | string | Headline the reviewer gave the review. | yes |
| reviewRating | number | The reviewer's overall score on a 1 to 10 scale, not 1 to 5. Rescale before mixing it with a five-star source. | yes |
| reviewDate | string | ISO timestamp for when the review was posted. | yes |
| startDate | string | ISO timestamp for when the reviewer says they started using the product. The source's value is self-reported and sometimes sits after the review date, so treat it as soft. | yes |
| updatedDate | string | ISO timestamp for the last time the review record changed upstream. | yes |
| pros | string[] | What the reviewer liked, as discrete points rather than a paragraph. | yes |
| cons | string[] | What the reviewer disliked, as discrete points. | yes |
| prosAndCons | string | The same points as one newline-separated blob. Redundant with pros and cons; useful only if you want the raw text. | yes |
| useCasesAndScope | string | How and where the reviewer deploys the product. Long free text, and it can carry HTML fragments from the source's editor. | yes |
| likelihoodToRecommend | string | Who the reviewer thinks the product suits, in their own words. Also long free text that can carry HTML. | yes |
| returnOnInvestment | string | Business outcomes the reviewer attributes to the product. | yes |
| efficienciesGained | string | Time or effort savings reported. Frequently absent. | yes |
| alternativesConsidered | string | What the buyer weighed this product against, and why they chose it. The reason this source exists. It is free text under the source's prompt, so most values name rivals but some just describe the product. | yes |
| keyDifferentiators | string[] | Short labels for what the reviewer says sets the product apart, for example Scalability or Ease of Use. Present on maybe half of rows. | yes |
| productsReplaced | boolean | Whether the purchase displaced an incumbent tool. Filter on true to find switching stories. | yes |
| otherSoftwareUsed | object[] | Tools the reviewer runs alongside this one, as objects with two keys: name and link. Not a flat string array. | yes |
| businessProcessesSupported | string | Which internal processes the product supports. Newline-separated text. | yes |
| innovativeUses | string | Unusual applications. Arrives as a stringified Python-style dict rather than parsed JSON, and as the literal "{}" when empty. | yes |
| futurePlannedUses | string | What the reviewer intends to use it for next. Frequently absent. | yes |
| likelihoodToRenew | string | Renewal intent. Often just a number as a string rather than a sentence. | yes |
| usabilityPros | string | What is easy about using the product, newline-separated. | yes |
| usabilityCons | string | What is awkward about using it. Sparse; absent on both rows sampled while writing this page. | yes |
| easyTasks | string | Tasks the reviewer finds straightforward. | yes |
| difficultTasks | string | Tasks the reviewer finds hard. Useful as a product-gap signal. | yes |
| supportPros | string | What is good about vendor support. | yes |
| supportCons | string | What is poor about vendor support. | yes |
| supportHeadcountRequired | string | Internal headcount needed to run the product. Usually a bare number as a string. | yes |
| implementationPartner | string | Who implemented it. Often the source's own placeholder, such as "Don't know". | yes |
| implementationIssues | string | Problems encountered during rollout. Often "N/A" rather than empty. | yes |
| usersAndRoles | string | Who uses the product internally and how many. Another stringified Python-style dict, "{}" when empty. | yes |
| keyInsights | string | The source's yes and no verdicts on value, features, implementation and buying again. Stringified dicts, one per line. | yes |
| featureRatings | object[] | Per-feature numeric scores from this reviewer, as objects with two keys: feature and rating. Nineteen entries on one sampled row. This is what makes a feature-level comparison possible. | yes |
| ratings | object | Usability, support and implementation scores in one object. Any of the three can be missing; one sampled row carried usability only. | yes |
| reviewAuthor | string | Display name the reviewer publishes under. Often a real name, so treat it as personal data. | yes |
| authorPosition | string | The reviewer's job title. This is what lets you weight an opinion. | yes |
| authorCompanyName | string | Company the reviewer works for. | yes |
| authorCompanyIndustry | string | Industry of the reviewer's company. | yes |
| authorCompanySize | string | Employee band as a string, for example "1001-5000 employees". Parse it if you want to sort. | yes |
| authorExperienceYears | integer | Years the reviewer has used the product, as a plain number. | yes |
| authorLabels | string[] | Badges the source applies, such as Vetted Review or Verified User. | yes |
| authorIncentivized | boolean | Whether the reviewer got something for writing it. Discount these when you weight sentiment. | yes |
| summary | string | One-line plain-language digest of the row, including the rating and the reviewer's role, so an agent can read a record without post-processing. | no |
| sourceUrl | string | Error rows only. The input URL this error relates to. | yes |
| error_message | string | Error rows only. Plain-language reason the input returned nothing. | yes |
| error_type | string | Error rows only. Machine-readable category, for example CollectionError. | yes |
| fetched_at | string | UTC timestamp for when the row was collected. | no |
Worked examples
{
"mode": "product",
"productUrls": ["https://www.trustradius.com/products/asana/reviews"],
"maxReviewsPerProduct": 25
}
{
"mode": "product",
"productUrls": [
"https://www.trustradius.com/products/asana/reviews",
"https://www.trustradius.com/products/hubspot-crm/reviews",
"https://www.trustradius.com/products/slack/reviews"
],
"maxReviewsPerProduct": 1,
"pages": 1
}
{
"mode": "product",
"productUrls": ["gtm-workspace"],
"maxReviewsPerProduct": 300,
"pages": 15
}
{
"mode": "product",
"productUrls": ["asana", "hubspot-crm", "slack", "gtm-workspace"],
"maxReviewsPerProduct": 1,
"pages": 1
}
{
"mode": "review",
"reviewUrls": [
"https://www.trustradius.com/reviews/asana-2025-12-01-09-11-31",
"https://www.trustradius.com/reviews/zoominfo-sales-2025-04-15-13-25-48"
]
}
POWER-USER TIP
maxReviewsPerProduct is the whole bill — You are charged once per review pushed to the dataset, so the cap is your budget and nothing else is. pages only decides how deep the list walk goes before the cap stops it; raising pages without raising the cap changes which reviews you get, not how many. A four-product watchlist at one review each lands around two cents.
POWER-USER TIP
Product slugs do not follow redirects — A product that has been renamed or merged returns an error row rather than the page it now redirects to. zoominfo-sales is the live example, since that product now sits at gtm-workspace; product mode on the old slug returns result_type error while the same product's individual review URLs still resolve in review mode. Open the product page in a browser and copy the slug from the address bar.
POWER-USER TIP
alternativesConsidered is free text, and not always about alternatives — The field carries whatever the reviewer typed under the source's prompt. Most values name rival products and say why one won, which is the point of this source, but some reviewers use the box to praise the product instead. Match the text against your own competitor list rather than assuming every non-empty value is a comparison.
POWER-USER TIP
Three fields arrive as stringified dicts — innovativeUses, usersAndRoles and keyInsights come back as Python-style dict strings, for example {'question': 'valueprice', 'answer': 'yes'}, not as parsed JSON, and as the literal {} when the reviewer left them blank. Read them as text, or parse with ast.literal_eval. Everything else in the schema is properly typed.
POWER-USER TIP
Split on result_type before you aggregate — An input that returns nothing produces a row with result_type error and a plain-language error_message instead of vanishing, which is the behaviour you want, but it will drag a mean rating down if you forget to filter. No review-scraped event is charged for those rows, confirmed on a run that produced one.
POWER-USER TIP
Long text fields can carry HTML — useCasesAndScope and likelihoodToRecommend sometimes include div and br fragments from the source's own editor. Strip tags before the text goes into a spreadsheet or a model prompt.
Coverage
49
fields on a review row — competitive · scored · reviewer context
52
fields in the full schema — 49 review fields plus 3 that appear only on error rows
50
products per run — product mode
500
review URLs per run — review mode
2,000
reviews per product, ceiling — cap it lower, you pay per review
25
list pages walked per product — higher values reach older reviews
10
rating scale — 1 to 10, not 1 to 5
2
dataset views — reviews overview · competitive comparisons
9
published task pages — ready-made configurations on the platform listing
Tasks
Saved runs with the inputs already filled in — each one a standalone page and a working configuration example.
Code
curl
curl -X POST "https://api.apify.com/v2/acts/johnvc~trustradius-reviews-api/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode":"product","productUrls":["asana"],"maxReviewsPerProduct":25}'
Python
from apify_client import ApifyClient
client = ApifyClient("APIFY_TOKEN")
run = client.actor("johnvc/trustradius-reviews-api").call(
run_input={
"mode": "product",
"productUrls": ["https://www.trustradius.com/products/asana/reviews"],
"maxReviewsPerProduct": 25,
}
)
for row in client.dataset(run.default_dataset_id).iterate_items():
if row.get("result_type") != "review":
print("skipped:", row.get("error_message"))
continue
print(row["reviewRating"], row.get("authorPosition"), row.get("authorCompanySize"))
print(" weighed against:", row.get("alternativesConsidered"))
for fr in row.get("featureRatings") or []:
print(" ", fr["feature"], fr["rating"])
Competitive comparisons view
# the alternatives, differentiators, replacement flag and feature scores as one table curl "https://api.apify.com/v2/datasets/$DATASET_ID/items?view=competitive&format=csv" \ -H "Authorization: Bearer $APIFY_TOKEN"
Changelog
2026-08-08
Published on the Store. Product-mode discovery re-verified against live product pages.
2026-08-05
Pay-per-review pricing applied, with volume tiers stepping down below the base rate.
What people use it for
- Win loss analysis without running buyer interviews
- Competitive displacement signals in B2B software
- Software vendor comparison grids
- Feature-level benchmarking against a named rival
- Buyer sentiment split by company size and industry
- Scheduled competitor review monitoring
- Grounding an AI agent in real buyer comparisons
Alternatives
G2 Reviews API — the other large B2B review site, with far more reviews per product; better when you need volume, but it has no field naming what the buyer weighed the purchase againstours
Glassdoor Reviews API — employee reviews of the vendor rather than buyer reviews of the product, which is often where a support-quality shift is explainedours
Crunchbase Company API — funding and firmographics on the vendor, no review text at allours
TrustRadius Reviews Scraper on the same marketplace — returns ratings, pros, cons, review text and dates, but not the alternatives the buyer considered, the per-feature scores or the replaced-an-incumbent flagcompeting