# Capterra Reviews Scraper - Pros, Cons & Ratings (`s-r/capterra-reviews`) Actor

Scrape Capterra software reviews: overall and five dimension ratings, a 0-10 recommendation score, separate pros and cons, incentivised-review flag, plus the reviewer's company size, industry, job title and tenure. Every row is checked against the product you asked for.

- **URL**: https://apify.com/s-r/capterra-reviews.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Business, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 actor run starteds

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

## Capterra Reviews Scraper

Export the reviews behind a Capterra software listing as structured data. Paste one
or more product pages, get one row per review, up to 2,500 reviews per product.

Capterra's star rating is a single number standing in for thousands of individual
opinions. This Actor gives you the opinions: who wrote each one, what they liked,
what they did not, how long they had used the product, how big their company was,
and whether they were offered anything in return for writing it.

### Every row is checked against the product you asked for

This is the part worth reading first, because it is not a detail.

A Capterra product address looks like `/p/135003/Slack/`, and it reads as though
the name identifies the product. It does not. Capterra resolves on the number
alone and ignores everything after it, so `/p/135003/Notion/` returns **Slack**,
as a completely normal page, under the name you typed. Product numbers are not
guessable either: `189442` is not a company anyone would guess, and a single
mistyped digit lands on a real, different product rather than on an error.

So a scraper that trusts the address it was given can hand you one company's
reviews filed under another company's name, and nothing in the output would
look wrong.

This Actor reads the product identity back off the page and checks it against
the number that was requested, on **every page**, not just the first. If they
disagree, nothing from that page is kept and the run reports it. Every row then
carries `product_id`, `product_name` and `product_slug` exactly as the page
reported them, so the attribution is on the row itself and you can verify it
without trusting us. Where the name you asked for and the name Capterra returned
are different, the run summary says so explicitly.

The same care applies to the reviews themselves. A Capterra reviews page also
carries the product's recommended alternatives, each with its own name, id and
rating summary sitting in the same data. Reviews are read only from the one
record that both carries reviews and matches the requested id.

### What you get per review

Every row carries the whole review, not a summary of it.

**Scores.** The overall star rating, plus all five of Capterra's individual
dimension scores: ease of use, customer support, functionality, value for money,
and the reviewer's 0-10 answer to how likely they are to recommend the product.
That last one is on Capterra's own 0-10 scale and stays there rather than being
squeezed into five stars, because it answers a different question from the rating.

**The text, in the fields the reviewer filled in.** Capterra asks for pros and cons
separately and this Actor keeps them separate, so you get `pros` and `cons` as their
own columns rather than one blob you have to split later. On top of those: the
review title, the reviewer's overall comments, any advice they left for other
buyers, why they chose this product, and why they switched away from a previous one.
Where a reviewer named the products they weighed against this one, or the product
they moved off, those come through as lists.

**Who wrote it.** Job title, industry, company size band, and how long they had been
using the product when they wrote. Whether Capterra validated them, and by what
method. Whether they posted anonymously.

**The incentive disclosure.** Capterra records, per review, whether the reviewer was
offered something in exchange, such as a gift card. That flag is in every row, with
Capterra's own wording of the disclosure alongside it, so you can weight incentivised
reviews differently or drop them entirely.

**The vendor's reply.** Where the software vendor publicly answered a review, the
reply and its date are in the row. Some vendors answer almost every review and some
answer none, which is itself worth knowing about a vendor.

**Where the review came from.** Capterra publishes reviews gathered on its sibling
directories next to its own. Every row states which one it came from, so a mixed set
never looks like a single-source one.

### Input

Paste product page addresses, one per line. The reviews page address works too, and
so does an id and slug pair like `135003/Slack`.

Everything else is optional:

| Input | What it does |
| --- | --- |
| Reviews per product | Stop after this many reviews for each product |
| Maximum products | Cap on how many products the run covers |
| Sort reviews by | Newest, oldest, highest rated, lowest rated, most recommended, or as served |
| Minimum / maximum overall rating | Keep only reviews in a star range |
| Reviewer company size | Keep only reviewers from companies of a given size |
| How long the reviewer used it | Free trial, under six months, up to two years, longer |
| Review source | Restrict to one of the directories Capterra draws from |
| Drop incentivized reviews | Leave out reviews written for a reward |
| Only reviews with both pros and cons | Drop the one-liners |
| Written on or after / before | Date range, as YYYY-MM-DD |
| Text contains | Keep only reviews mentioning a word or phrase |
| Maximum pages per product | Request ceiling, for when a narrow filter would otherwise read everything |
| Delay between page requests | Pacing, in seconds |
| Attempts per page | Retries for a page that came back incomplete |

#### About the filters and the sorting

These are applied to the reviews after they are collected, not asked of Capterra.

That is a deliberate choice and worth explaining, because Capterra's review pages do
have a sort and filter panel, and its controls do write parameters into the address
bar. Requested directly, the site accepts every one of those parameters and returns
exactly the same reviews it would have returned without them. We checked each one
against a baseline confirmed identical across three consecutive requests: sorting,
star rating, company size, reviewer role and length of use all came back with the
same twenty-five reviews, indistinguishable from a parameter name we invented on the
spot. Passing them through would have produced an Actor whose filter inputs did
nothing at all, which is worse than not offering them.

So the filtering happens here, on fields that are present in the row you receive and
that you can check for yourself. Two of Capterra's own facets, reviewer role and
frequency of use, are not offered at all, because the review data carries no such
field and there would be nothing honest to filter on.

One consequence worth knowing: a reviewer who did not state their company size or
tenure cannot satisfy a filter on it, so those rows drop out when you set one. The
run summary reports how many rows your filters removed, so the effect is visible
rather than silent.

### Limits, and how this Actor handles them

**2,500 reviews per product, and it really is per product.** Capterra publishes at
most 100 pages of 25 reviews for any product, however many it holds. Slack has over
24,000 reviews and the site will show you 2,500 of them. That is the site's ceiling,
not this Actor's, and asking for more cannot produce more.

The part worth stating plainly, because it decides whether this Actor is useful for
more than one product at a time: the 2,500 is **not a budget your run spends**. It is
a property of each product. Two large products in a single run each returned the full
2,500, the second one in a session that had already collected 5,000 reviews, with no
slowdown and no errors. A separate run starting cold was served the identical reviews
at pages 1, 50 and 100. So ten products in one run means 25,000 reviews, not 2,500
shared between them.

Past the ceiling Capterra does something worth guarding against: ask for page 101 and
it answers with page 1 again, as a perfectly normal response carrying twenty-five
genuine reviews and the correct product. Its own "more pages available" flag keeps
saying yes well past the end, so it cannot be used to decide when to stop. A crawler
that followed either signal would append silent duplicates to the end of every large
product. This Actor tracks the reviews it has already collected and refuses to return
any of them twice, and the run summary reports how many duplicates were blocked.

**A product with very few reviews has no reviews page.** Capterra redirects the review
address for those products to the product profile, which answers normally and contains
no reviews. That case is reported as an error with its own code rather than as a
product with nothing to say, because a scraper that reported "no reviews" for a
product with three would be giving you a confident wrong answer.

**A product id that does not exist** is reported as an error too, rather than as an
empty result.

**The number in the URL is what identifies a product.** Capterra resolves on it and
ignores the name after it, so `/p/135003/Notion/` returns Slack. Every row states the
product name the page actually reported, and the run summary flags any product whose
name did not match the one you asked for.

### Output

One dataset item per review. The key-value store holds a run summary, with per-product
figures: the name Capterra resolved, how many reviews it holds in total, pages
fetched, rows returned, rows removed by your filters, duplicates blocked, and why the
crawl stopped for that product. Anything that could not be read lands in `errors` with
a code.

### Speed and cost

One page of 25 reviews every few seconds, paced deliberately rather than run flat
out. Reading a product all the way to the site's ceiling is a hundred requests and
took **six to seven minutes** in testing, for 2,500 reviews. Reading 100 reviews from
each of ten products is a much shorter run. Use the reviews-per-product and
maximum-pages inputs to set the ceiling you want before you start.

Long runs are bounded on purpose. A single page that stops responding is given up on
rather than waited out, and each product has a wall-clock budget well above what a
full read needs. If a product ever hits it, the reviews already collected are
returned and the summary says what happened, instead of the whole run expiring with
nothing to show.

### Related

If you want the product profile rather than the reviews behind it, including the
rating dimensions, review counts, starting price and pricing model as one row per
product, use the Capterra Scraper Actor instead.

# Actor input Schema

## `products` (type: `array`):

Paste the address of each Capterra product page, one per line. The reviews page address works too, and so does an id and slug pair like 135003/Slack. Capterra resolves a product on the number alone and ignores the name after it, so every row states the product name the page actually returned and the run summary flags any product whose name did not match what you asked for.

## `maxReviewsPerProduct` (type: `integer`):

Stop after this many reviews for each product. Capterra publishes at most 100 pages of 25 reviews, so 2500 is the ceiling the site itself imposes and asking for more cannot produce more. That ceiling is per product, not shared across the run, so ten products can return 25000 reviews. A full 2500 takes six to seven minutes for one product.

## `maxProducts` (type: `integer`):

Stop after this many products, counted once duplicates are removed. Each product is up to 100 page requests, so this is the ceiling on how long the run can take.

## `sortBy` (type: `string`):

Order of the rows in the dataset. Capterra ignores its own sort parameter when the page is requested directly, so the ordering is applied to the reviews this run collected rather than asked of the site. As served keeps Capterra's own default order, which is its Most Helpful ranking.

## `minRating` (type: `integer`):

Keep only reviews rated at least this many stars overall. Applied to the rating carried on each review, so it is checkable against the row you receive.

## `maxRating` (type: `integer`):

Keep only reviews rated at most this many stars overall. Set it to 2 or 3 with no minimum to read the complaints.

## `companySizes` (type: `array`):

Keep only reviewers from companies of these sizes. Reviewers who did not state a company size are left out when this is set, and the run summary reports how many rows the filters removed. Leave empty for every size.

## `timeUsedProduct` (type: `array`):

Keep only reviewers with this much time on the product. Useful for separating first impressions from long-term experience. Reviewers who did not state it are left out when this is set.

## `sourceSites` (type: `array`):

Capterra publishes reviews collected on its sibling directories alongside its own. Restrict to one source here, or leave empty to keep all of them. Every row carries the source it came from either way.

## `excludeIncentivized` (type: `boolean`):

Leave out reviews where the reviewer was offered something in return, such as a gift card. Capterra discloses this per review and every row carries the disclosure, so you can also keep them and weight them yourself.

## `requireProsAndCons` (type: `boolean`):

Keep only reviews that filled in both the pros field and the cons field. The quickest way to drop one-line reviews when you are feeding the text to something.

## `writtenAfter` (type: `string`):

Keep only reviews written on or after this date, as YYYY-MM-DD. Reviews whose date could not be read are left out when this is set.

## `writtenBefore` (type: `string`):

Keep only reviews written on or before this date, as YYYY-MM-DD.

## `searchText` (type: `string`):

Keep only reviews whose title, pros, cons, overall comments or advice mention this text. Plain case-insensitive matching, no wildcards. Use it to find every review that mentions pricing, onboarding or a competitor by name.

## `maxPagesPerProduct` (type: `integer`):

A ceiling on requests per product, for when a narrow filter would otherwise walk all 100 pages to find a handful of matches. Capterra serves at most 100 pages per product and re-serves page one beyond that, which this Actor detects and refuses to return.

## `requestDelaySeconds` (type: `integer`):

Seconds to wait between requests, plus a small random amount. A full product is 100 sequential requests, so the default is set to be a good guest rather than to be fast. Raise it if a long run starts reporting pages it could not fetch.

## `retries` (type: `integer`):

How often to retry a page that came back without review data. Capterra's checks are intermittent rather than permanent, so a second or third attempt usually succeeds.

## Actor input object example

```json
{
  "products": [
    "https://www.capterra.com/p/168956/Canva/"
  ],
  "maxReviewsPerProduct": 100,
  "maxProducts": 10,
  "sortBy": "asServed",
  "minRating": 1,
  "maxRating": 5,
  "companySizes": [],
  "timeUsedProduct": [],
  "sourceSites": [],
  "excludeIncentivized": false,
  "requireProsAndCons": false,
  "maxPagesPerProduct": 100,
  "requestDelaySeconds": 2,
  "retries": 3
}
```

# Actor output Schema

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

One row per review, across every product requested.

## `summary` (type: `string`):

Per product: the name Capterra resolved, how many reviews it holds, pages fetched, rows returned, rows removed by your filters, duplicate pages blocked, and why the crawl stopped.

## `errors` (type: `string`):

Products that could not be read, with a code and a message. A product whose reviews page is not published appears here rather than as an empty result.

# 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 = {
    "products": [
        "https://www.capterra.com/p/135003/Slack/",
        "https://www.capterra.com/p/211559/Trello/"
    ],
    "maxReviewsPerProduct": 100,
    "maxProducts": 10,
    "sortBy": "asServed",
    "minRating": 1,
    "maxRating": 5,
    "companySizes": [],
    "timeUsedProduct": [],
    "sourceSites": [],
    "excludeIncentivized": false,
    "requireProsAndCons": false,
    "writtenAfter": "",
    "writtenBefore": "",
    "searchText": "",
    "maxPagesPerProduct": 100,
    "requestDelaySeconds": 2,
    "retries": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/capterra-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 = {
    "products": [
        "https://www.capterra.com/p/135003/Slack/",
        "https://www.capterra.com/p/211559/Trello/",
    ],
    "maxReviewsPerProduct": 100,
    "maxProducts": 10,
    "sortBy": "asServed",
    "minRating": 1,
    "maxRating": 5,
    "companySizes": [],
    "timeUsedProduct": [],
    "sourceSites": [],
    "excludeIncentivized": False,
    "requireProsAndCons": False,
    "writtenAfter": "",
    "writtenBefore": "",
    "searchText": "",
    "maxPagesPerProduct": 100,
    "requestDelaySeconds": 2,
    "retries": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/capterra-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 '{
  "products": [
    "https://www.capterra.com/p/135003/Slack/",
    "https://www.capterra.com/p/211559/Trello/"
  ],
  "maxReviewsPerProduct": 100,
  "maxProducts": 10,
  "sortBy": "asServed",
  "minRating": 1,
  "maxRating": 5,
  "companySizes": [],
  "timeUsedProduct": [],
  "sourceSites": [],
  "excludeIncentivized": false,
  "requireProsAndCons": false,
  "writtenAfter": "",
  "writtenBefore": "",
  "searchText": "",
  "maxPagesPerProduct": 100,
  "requestDelaySeconds": 2,
  "retries": 3
}' |
apify call s-r/capterra-reviews --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/capterra-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/Szez1XNQlPLzO45qU/builds/8HrtKSFQ9Tu1HF5Ly/openapi.json
