I run a visa agency in Bali. The half of my week that has nothing to do with visas goes into Apify Actors, and one of them collects B2B software reviews — Capterra, Software Advice, GetApp and TrustRadius — and returns every site with the same field names.
That last part started as a convenience feature — four scrapers, four field vocabularies, one translation layer I got tired of rewriting. What it turned into was a measuring instrument: once the rows line up you can put the sites side by side and see where they disagree. They disagree a lot, and not in the way I expected.
Here is the measurement. Product: Slack. Builds 0.1.39 and 0.1.40, runs on 13–15 October 2026, no proxy.
Three sites, one database, three different totals
Ask for Slack on Capterra, Software Advice and TrustRadius, ten reviews each, and the product header on those rows reads:
source productReviewCount productRating ratingScale
capterra 11,166 4.4643 5
softwareadvice 24,224 4.6639 5
getapp 24,224 4.6639 5
trustradius 10,268 9 10
Software Advice and GetApp agree to four decimal places. Capterra — the biggest brand of the three, the one whose logo everyone recognises — reports 11,166 reviews and an average two tenths of a star lower.
My first guess was that these are simply three separate review pools that happen to overlap in subject matter. That guess is wrong, and the reviewId field is what killed it:
softwareadvice Capterra___7219769
softwareadvice Capterra___7224596
getapp Capterra___7138404
capterra Capterra___7209002
Those are reviews scraped from softwareadvice.com and getapp.com. They carry Capterra identifiers because all three sites are Gartner properties running on one review database. The IDs interleave, too — 13 of the 20 Software Advice IDs I collected fall inside the numeric range of the 45 Capterra IDs. It is one sequence, handed out to one pool of reviews, served through three front doors with three different filters on the way out.
So the 13,058-review gap is not a data gap. It is an editorial decision about which of the shared reviews each brand chooses to display, and it is invisible if you scrape one site.
The same review, twice, with a different reviewer on it
Across 45 Capterra reviews and 20 Software Advice reviews I caught exactly one ID on both sites: Capterra___7224596. Field by field:
field capterra softwareadvice
title "Slack is the best…" "Slack is the best…" same
publishedAt 2026-10-04 2026-10-04 same
pros / cons identical text identical text same
overallRating 5 5 same
easeOfUseRating 5 5 same
functionalityRating 5 5 same
valueForMoneyRating 5 5 same
customerSupportRating 4 4 same
incentivized true (NominalGift) true (NominalGift) same
reviewerIndustry Construction Construction same
--
reviewerName "Chris S." "Chris S——" (full) differs
reviewerJobTitle "General Manager" null differs
reviewerCompanySize "51-200 employees" null differs
reviewerCompanySizeCode null "D" differs
timeUsed null "More than 2 years" differs
usageFrequency null "Daily" differs
Everything a reviewer typed is byte-identical. Everything about the reviewer is split between the two pages. Capterra publishes the job title and a readable company-size range and abbreviates the surname to an initial. Software Advice publishes the reviewer's full first and last name — I'm redacting it here, since the point is the disclosure difference and not this person — drops the job title, and replaces the company-size range with a single letter.
That letter is worth a paragraph of its own. Software Advice and GetApp publish A through I where Capterra publishes 11-50 employees, and neither site publishes a legend. The temptation is to map it yourself: A is surely the smallest bucket, I the largest, nine letters for nine ranges. I didn't, because on this one shared review the letter is D and the range Capterra prints for the same company is 51-200 employees — a single data point, enough to build a guess on and nowhere near enough to ship one. So the Actor passes the letter through in reviewerCompanySizeCode and leaves reviewerCompanySize empty. One confirmed pair is a hint, not a mapping.
What this does to de-duplication
If you are merging Capterra, Software Advice and GetApp, reviewId is a real join key and you should use it. But the merge is not idempotent in either direction, because neither row is a superset of the other. Prefer the Capterra row and you lose usageFrequency and timeUsed. Prefer the Software Advice row and you lose the job title and the human-readable company size. The only correct merge is field-level: for each column, take whichever side is non-null.
TrustRadius stays out of this entirely. Its IDs are slugs with a timestamp — slack-2026-08-05-00-29-43 — its reviewers are its own, and across ten rows it returned zero sub-ratings while filling in reviewerJobTitle on all ten ("Full-stack developer", "Sr. Director engineering", "Senior Salesforce Administrator"). Different site, different database, genuinely additive sample.
And GetApp is no longer a review source in any useful sense. Its product totals are complete and match Software Advice exactly, but the product page now publishes about six short quotes instead of full reviews, with no ratings at all. Of the five rows I got back, three had pros filled and cons empty, two the reverse — the page quotes one half of each review and doesn't say which half. The first one reads "However, managing constant notifications can be overwhelming at times…", which is a complaint, sitting in a field called pros, because that is the only text GetApp published for it. Use GetApp for product-level numbers; don't build a sentiment set from it.
Page one is not a sample
One more thing I only saw because the rows lined up. Ten reviews from each site, in the order the site publishes them, no sort, no filter:
capterra 5 5 3 3 4 5 5 4 4 5 avg 4.3
softwareadvice 5 5 5 5 5 5 5 5 5 5 avg 5.0
Same database. Software Advice's front page was a wall of five stars; Capterra's front page was not. Five of those ten Software Advice reviews were flagged incentivized — NominalGift or VendorReferredIncentivized — against 14 of 45 on Capterra.
The low ratings do exist in the shared pool, they are just not on page one. Asking for them explicitly finds them:
{
"searchTerms": ["Slack"],
"searchSites": ["softwareadvice"],
"maxRating": 3,
"maxReviews": 10
}
Ten rows: nine 3-star and one 1-star, the oldest from 2025-10-31, six of the ten incentivized. The filter runs while paging rather than after it, so the run keeps reading pages until it has what you asked for or the site runs out:
def matches_filters(row, *, min_rating, max_rating, published_after, published_before):
# TrustRadius rates out of 10, the Gartner sites out of 5, so filtering happens on
# the normalized 1-5 value the user is thinking in.
rating = row.get('normalizedRating', row.get('overallRating'))
That normalization is why maxRating: 3 means the same band of unhappy customers on TrustRadius as on Capterra. overallRating keeps the site's own number, ratingScale says which scale it was, normalizedRating is raw * 5 / scale. A TrustRadius 9 arrives as overallRating: 9, ratingScale: 10, normalizedRating: 4.5, and averaging that against a Capterra 5 is finally a legal operation.
Calling it
Search by name on Software Advice and TrustRadius, by URL on Capterra and GetApp — Capterra's search page refuses automated requests even through a residential proxy, and GetApp publishes no search page at all. Their product pages are served normally, so a URL is all you need:
curl -X POST "https://api.apify.com/v2/acts/lergassy~software-reviews-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"productUrls": ["https://www.capterra.com/p/135003/Slack/reviews/"],
"searchTerms": ["Slack"],
"searchSites": ["trustradius", "softwareadvice"],
"maxReviews": 200,
"includeProductRow": true
}'
Thirty reviews across three sites took 46 seconds; thirty from Capterra alone took 90, because reviews come 25 to a page and each page is a request. Budget roughly three to four seconds per page. The Actor is here if you want to run the comparison on your own category — pricing is per row, $0.0012 a review and $0.003 a product summary, and failed URLs come back as error rows at no charge.
When not to use this
Don't use it to count. If you need "how many reviews does this product have", you now know that question has two different right answers depending on which Gartner brand you ask, and any single number you quote is a brand's choice rather than a fact.
Don't use it for G2 — different site, different gate, not covered here. And if what you want is volume over time, note that publishedAt is reliable but the sites' own ordering is not chronological, so a date histogram needs the full history pulled, not a recent slice.
Do use it when the comparison is the point: the same product across four sites, the same columns, and a normalized rating that makes an average mean something. Just merge field-level, and treat page one as a storefront rather than a sample.
Top comments (0)