How to use Capterra Reviews Scraper
Step by step: in the Apify Store, over the API, from JavaScript or Python, in n8n, and from AI agents through MCP or LangChain.
$1.79 per 1,000 reviews on the Free plan, down to $1.16 on higher plans (live Apify price on 2026-10-31).
1. In the Apify Store (no code)
- Open arcadium/capterra-reviews and sign in to Apify (a free account works).
- Fill in the input form. The fields below are the ones the example uses.
- Set a maximum cost per run in the run options if you want a hard cap.
- Start the run. Rows appear in the Output tab as they arrive.
- Export the dataset as JSON, CSV or Excel, or connect an integration.
| Input field | Type | Description |
|---|---|---|
startUrls | array | Capterra product or reviews page URLs, e.g. https://www.capterra.com/p/135003/Slack/reviews/. Regional Capterra sites (capterra.co.uk, capterra.com.au, ...) are accepted and read from capterra.com. Leave empty if you use search queries. |
maxReviewsPerProduct | integer | Maximum number of reviews to collect for each product (up to 50,000). Default: 100. |
Example input:
{
"startUrls": [
{
"url": "https://www.capterra.com/p/135003/Slack/reviews/"
}
],
"maxReviewsPerProduct": 20
}
2. Over the Apify API
The synchronous endpoint runs the actor and returns its rows in one call (for runs up to about five minutes; use the asynchronous run endpoint for longer ones).
curl -X POST "https://api.apify.com/v2/acts/arcadium~capterra-reviews/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"startUrls":[{"url":"https://www.capterra.com/p/135003/Slack/reviews/"}],"maxReviewsPerProduct":20}'
3. From JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/capterra-reviews').call({
"startUrls": [
{
"url": "https://www.capterra.com/p/135003/Slack/reviews/"
}
],
"maxReviewsPerProduct": 20
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
4. From Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/capterra-reviews").call(run_input={
"startUrls": [{
"url": "https://www.capterra.com/p/135003/Slack/reviews/",
}],
"maxReviewsPerProduct": 20,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
5. In n8n
Paste this node into a workflow (it is a plain HTTP Request node). Create a Header Auth credential with name Authorization and value Bearer <APIFY_TOKEN>; each returned row becomes one item for the next node.
{
"nodes": [
{
"name": "Run arcadium/capterra-reviews",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~capterra-reviews/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"startUrls\":[{\"url\":\"https://www.capterra.com/p/135003/Slack/reviews/\"}],\"maxReviewsPerProduct\":20}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}
6. From an AI assistant (MCP)
Add Apify’s MCP server to Claude Desktop, Cursor, VS Code or any MCP client, limited to this actor:
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/capterra-reviews",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}
More on the MCP page.
7. From LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/capterra-reviews")
rows = tool.invoke({"run_input": {
"startUrls": [{
"url": "https://www.capterra.com/p/135003/Slack/reviews/",
}],
"maxReviewsPerProduct": 20,
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])
Sample output
One row, from the example in the actor’s README. Long text is shortened and lists are cut to two items.
{
"rowType": "review",
"platform": "capterra",
"productName": "Slack",
"productUrl": "https://www.capterra.com/p/135003/Slack/",
"entityId": "135003",
"reviewId": "Capterra___7224147",
"reviewUrl": "https://www.capterra.com/p/135003/Slack/#Capterra___7224147",
"title": "Very strong communication tool.",
"text": "Generally very good, quick and easy. Good options to integrate, easy to file share. I typically run multiple Slack instances.",
"pros": "Ability to communicate in real time with colleagues when we're working remotely. Also for file sharing.",
"cons": "The threads feature is not something that I find particularly intuitive...",
"ratingOverall": 5,
"ratings": {
"easeOfUse": 5,
"customerService": 4,
"features": 5,
"valueForMoney": 5,
"likelihoodToRecommend": 9
},
"date": "2026-10-02",
"reviewerRole": null,
"reviewerIndustry": null,
"reviewerCompanySize": null,
"reviewerTimeUsed": null,
"switchedFrom": "Google Workspace",
"reasonForSwitching": "Google Workspace chat works well. However, several of our technical staff wanted to use Slack.",
"incentivized": true,
"incentive": "NominalGift",
"reviewerVerified": null,
"product": {
"platform": "capterra",
"entityId": "135003",
"name": "Slack",
"url": "https://www.capterra.com/p/135003/Slack/",
"rating": 4.7,
"reviewCount": 24229,
"category": "Team Communication",
"vendor": "Slack",
"website": null
},
"scrapedAt": "2026-10-20T17:49:58.237Z"
}
| Field | What it holds |
|---|---|
rowType | Kind of row. Filter on this first. Values: 'review': one review (charged). 'coverage': stated total vs delivered and why reading stopped (free). 'error': an input could not be read; see code and message (free). |
platform | Source site, lower case, e.g. 'tripadvisor', 'capterra', 'googleplay', 'appstore'. |
productName | Name of the reviewed hotel, business, product or app. |
productUrl | Page of the reviewed hotel, business, product or app on the source site. |
entityId | The site's own id of the reviewed entity (place id, product id, app id). |
reviewId | Stable review id in the form '<Platform>___<native id>'. Use it to deduplicate between runs. |
reviewIdSource | 'native' when the id comes from the site, 'derived' when it is a hash of the content (less stable). |
reviewUrl | Link to the single review, null when the site has none. |
title | Review headline, null when the site has none. |
text | Review text in the language it was written in. Null when the review has no text. |
pros | What the reviewer liked, for sites that ask separately (Capterra, TrustRadius, Glassdoor, Booking). Null otherwise. |
cons | What the reviewer disliked, for sites that ask separately. Null otherwise. |
ratingOverall | Overall rating on a 0 to 5 scale (Booking: its 1-10 score divided by 2, original in extras.score10). Null when missing. |
ratings | Sub-ratings as an object, e.g. { value: 5, cleanliness: 4 }. Keys depend on the site; empty object when there are none. |
date | Date the review was written, ISO format yyyy-mm-dd. Null if unknown. |
reviewerRole | Job title or role the reviewer states. No names. Null if not given. |
reviewerIndustry | Industry the reviewer states. Null if not given. |
reviewerCompanySize | Company size bracket of the reviewer, e.g. '51-200 employees'. Null if not given. |
reviewerTimeUsed | How long the reviewer used the product, as the site words it. Null if not given. |
switchedFrom | Product the reviewer switched from, null if not given. |
alternativesConsidered | Alternatives the reviewer considered, comma-separated, null if not given. |
reasonForChoosing | Why the reviewer chose this product, free text, null if not given. |
reasonForSwitching | Why the reviewer left the previous product, free text, null if not given. |
adviceToOthers | Advice the reviewer gives others (Glassdoor: advice to management), null if not given. |
incentivized | True when the site marks the review as incentivized (for example a gift card). Null when the site does not say. |
incentive | The site's raw incentive label, null if none. |
reviewerVerified | True when the site marks the review as verified. Null when the site does not say. |
vendorResponse | Reply of the owner, vendor, developer or employer, null when there is none. |
helpfulVotes | Number of helpful or useful votes, null when the site shows none. |
language | Language of the review text as a two-letter code, null if unknown. |
country | Country of the reviewer or the storefront where it applies (lower case code), otherwise null. No personal data. |
product | Summary of the reviewed entity repeated on each row: { platform, entityId, name, url, rating, reviewCount, category, vendor, website }. rating and reviewCount are the site's overall figures. |
extras | Site-specific extras, e.g. trip type, visit date, translated text, photo URLs, owner reply date. Keys are listed in the README. |
scrapedAt | ISO timestamp of when the row was collected. |
stated | Coverage rows: total the site states for the entity, null when it does not say. |
delivered | Coverage rows: number of rows delivered for the entity in this run. |
skippedSeen | Coverage rows: items skipped because an earlier run already delivered them (only-new mode). |
duplicates | Coverage rows: repeats the site returned that were dropped. |
stopReason | Coverage rows: why reading stopped. limit (your maximum reached), end (no more pages), dateFrom (older than your date), onlyNew (reached already delivered items), budget (run charge limit), cap (site-side depth limit), error (ended by a failure), filter (ended with a filter active). |
capSource | Coverage rows: who limited the result, 'user' (your maximum or budget) or 'site' (a limit of the website). Null if nobody. |
shortfall | Coverage rows: true when fewer rows were delivered than the site states although no maximum or filter was set. |
input | Error and coverage rows: the input (URL, id or search text) the row is about. Example: 'https://www.example.com/page'. |
code | Error rows: machine-readable reason. One of invalid_input (fix the input), not_found (page does not exist), blocked (site refused, retry later), unavailable (temporary error, retry later), filter_unsupported (the filter cannot be applied on this site), no_match (name search found nothing). |
message | Error rows: short human-readable explanation. Do not parse it, use code. |
Schedule it
In Apify Console, create a Task from your input and add a Schedule (for example daily). Each run writes a new dataset; integrations can push the rows to Google Sheets, Slack, a webhook or your database.