# G2 Scraper (`juanoox/g2-scraper`) Actor

Scrape the ranked software landscape of any G2 category: rating, review count, the top pros and cons with how many reviews mention each, G2's own review summary, and which placements are paid.

- **URL**: https://apify.com/juanoox/g2-scraper.md
- **Developed by:** [Juan ignacio Veltri](https://apify.com/juanoox) (community)
- **Categories:** Business, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## G2 Scraper

Scrapes the **ranked software landscape of any G2 category** — every product in the
list with its rating, how many reviews back it, the attributes reviewers keep
praising and complaining about, and **which slots G2 sold**.

One request returns the whole competitive picture of a software market. Point it
at `https://www.g2.com/categories/crm` and you get the top 15 CRMs ranked, each
with its score, its review volume, and the five things users love and the five
they don't — the conclusions, not the raw reviews.

### What makes this different

Most G2 scrapers hand you reviews: thousands of rows of free text that you then
have to read, cluster, and count yourself to learn anything. This one starts from
what G2 has already computed across all of them.

**Reviewers' verdict, already counted.** HubSpot Sales Hub has 13,919 reviews.
This Actor returns:

```json
"pros": [
  { "name": "Ease of Use",     "mentions": 1215 },
  { "name": "Features",        "mentions": 701  },
  { "name": "Helpful",         "mentions": 672  },
  { "name": "Lead Management", "mentions": 570  },
  { "name": "Intuitive",       "mentions": 538  }
],
"cons": [
  { "name": "Missing Features",      "mentions": 435 },
  { "name": "Limited Features",      "mentions": 409 },
  { "name": "Learning Curve",        "mentions": 383 },
  { "name": "Expensive",             "mentions": 318 },
  { "name": "Limited Customization", "mentions": 288 }
]
```

That is the answer you would otherwise scrape 13,919 reviews to compute.

**Paid placements, flagged.** G2 sells slots inside its category listings and
does not mark them apart from the ranking in any machine-readable way. Every row
carries `isSponsored`, and paid slots get `rank: null` because they hold no
ranked position. Without that field a G2 export reads as a merit ranking when
part of it is sold inventory.

**The ranking is a real ranking.** `rank` counts ranked products only. Ads are
not interleaved into the numbering, so `rank: 5` means fifth best by G2 Score,
not fifth row on the page.

### Input

```json
{
  "startUrls": ["https://www.g2.com/categories/crm"],
  "maxPagesPerListing": 3,
  "minReviews": 50,
  "includeSponsored": true
}
```

Product URLs work too, on their own or mixed in:

```json
{
  "startUrls": [
    "https://www.g2.com/categories/project-management",
    "https://www.g2.com/products/hubspot-sales-hub/reviews"
  ],
  "scrapeProductDetails": true
}
```

| Parameter | Type | Default | What it does |
|---|---|---|---|
| `startUrls` | array | `["https://www.g2.com/categories/crm"]` | Category URLs, product URLs, or both. `?order=highest_rated` and `?order=popular` are respected and kept across pages. |
| `maxPagesPerListing` | integer | `3` | Pages per category. 15 ranked products per page, so 3 pages is the top 45. Big categories run past 100 pages. |
| `scrapeProductDetails` | boolean | `false` | Opens each product page to fill in all ten mention counts, every category the product is listed in, and the vendor's description. Costs one extra request per product. |
| `minRating` | string | — | Keep products at or above this, 0–5. Unrated products are dropped rather than assumed to pass. |
| `minReviews` | integer | — | Keep products with at least this many reviews. Drops the 5.0-from-four-reviewers noise. |
| `includeSponsored` | boolean | `true` | Set to `false` for a listing with no advertising in it. |
| `prosKeywords` | array | — | Keep only products praised for one of these. |
| `consKeywords` | array | — | Keep only products criticized for one of these — how you find competitors whose users are unhappy about the thing you do well. |
| `vendorKeywords` | array | — | Keep only products whose vendor or name matches. |
| `maxItems` | integer | `100` | Hard cap on rows saved and charged. `0` means no cap. |
| `includeSeen` | boolean | `true` | Set to `false` to only save products never returned before, for tracking a category over time. |
| `maxConcurrency` | integer | `2` | Parallel requests. |
| `maxRequestsPerMinute` | integer | `20` | Rate limit for the run. |

### Output

One row per product.

```json
{
  "id": "g2:hubspot-sales-hub",
  "url": "https://www.g2.com/products/hubspot-sales-hub/reviews",
  "source": "g2",
  "scrapedAt": "2026-08-24T20:08:11.485Z",
  "productId": "hubspot-sales-hub",
  "name": "HubSpot Sales Hub",
  "vendor": "HubSpot",
  "vendorUrl": "https://www.g2.com/sellers/hubspot",
  "rating": 4.4,
  "reviewsCount": 13919,
  "rank": 2,
  "category": "CRM",
  "categories": ["Sales Engagement", "Email Tracking", "CRM", "..."],
  "reviewSummary": "Users consistently praise the ease of use and intuitive interface of HubSpot Sales Hub, which simplifies managing sales processes and enhances productivity...",
  "pros": [{ "name": "Ease of Use", "mentions": 1215 }],
  "cons": [{ "name": "Missing Features", "mentions": 435 }],
  "isSponsored": false,
  "logo": "https://images.g2crowd.com/uploads/product/image/large_detail/..."
}
```

| Field | Notes |
|---|---|
| `rank` | Position among ranked products, continuing across pages. `null` for a paid slot. |
| `category` | The category the row was read from. `null` when the product was requested directly. |
| `categories` | Every category G2 lists the product in — 13 for HubSpot Sales Hub. Needs a product page, so it fills in with `scrapeProductDetails` or when you pass a product URL. |
| `reviewSummary` | G2's own summary of all its reviews for the product. Only category listings publish it. |
| `pros` / `cons` | Five each, ordered by weight. `mentions` is how many reviews say it — the listing publishes the top one of each, the product page publishes all ten. |
| `rating` | Always on a 0–5 scale. G2 publishes the same score both out of 5 and out of 10 on the same page; this normalizes it. |
| `isSponsored` | `true` for a paid slot, and also `true` for a product that ranks organically **and** bought a slot on the same page. |

### Ratings you can trust

A 5.0 rating from four reviewers outranks a 4.4 from 13,919 unless you look at
`reviewsCount`. Set `minReviews` and the ranking becomes readable.

### What this Actor does not do

It does not scrape individual reviews. If you need the review text itself —
per-reviewer job title, company size, the full write-up — this is the wrong tool
and there are Actors on the Store built for exactly that. This one is for the
market-level view.

### Notes

G2 sits behind DataDome, which filters on TLS fingerprint and a JavaScript
challenge rather than on IP, so rotating addresses alone does not get through.
The Actor runs behind Apify's Unblocker, verified against category pages, deep
pagination, alternate sort orders and product pages.

Each row is charged once. Empty and duplicate rows are never charged.

# Actor input Schema

## `startUrls` (type: `array`):

Paste a category URL to get its whole ranked landscape, or a product URL to get that product alone. Both kinds can be mixed in one run. Category sort is respected: append ?order=highest\_rated or ?order=popular and the Actor keeps that order across pages.

## `maxPagesPerListing` (type: `integer`):

How deep to go in each category. A page holds 15 ranked products, so 3 pages is the top 45. Large categories run past 100 pages.

## `scrapeProductDetails` (type: `boolean`):

Adds one request per product and fills in what the ranking does not publish: how many reviews mention each of the ten attributes (the ranking only shows the top one of each), every category G2 lists the product in, and the vendor's own description. Off by default because it multiplies the request count by about 15.

## `minRating` (type: `string`):

Keep only products rated at or above this, on a 0-5 scale. Products with no rating are dropped, because they cannot be said to clear a minimum. Leave empty for no filter.

## `minReviews` (type: `integer`):

Keep only products backed by at least this many reviews. A 5.0 rating from 4 reviewers is noise; this is how you drop it. Leave empty for no filter.

## `includeSponsored` (type: `boolean`):

G2 sells slots inside its category listings and does not separate them from the ranking. Every row carries isSponsored, and paid slots get rank: null because they hold no ranked position. Turn this off for a ranking with no advertising in it.

## `prosKeywords` (type: `array`):

Keep only products whose reviewers praise one of these, matched against the pros list, e.g. "Ease of Use", "Integrations".

## `consKeywords` (type: `array`):

Keep only products whose reviewers complain about one of these, e.g. "Expensive", "Learning Curve". This is how you find the competitors whose users are unhappy about the thing you do well.

## `vendorKeywords` (type: `array`):

Keep only products whose vendor or name contains one of these.

## `maxItems` (type: `integer`):

Hard cap on how many products are saved and charged. 0 means no cap.

## `includeSeen` (type: `boolean`):

Off means the run only saves products it has never returned before, which is what you want when tracking a category over time.

## `maxConcurrency` (type: `integer`):

Parallel requests. G2 sits behind DataDome; going high gets you blocked rather than fast.

## `maxRequestRetries` (type: `integer`):

How many times a blocked or failed request is retried before the target is given up on.

## `maxRequestsPerMinute` (type: `integer`):

Rate limit for the whole run.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.g2.com/categories/crm"
  ],
  "maxPagesPerListing": 3,
  "scrapeProductDetails": false,
  "minRating": "",
  "includeSponsored": true,
  "maxItems": 100,
  "includeSeen": true,
  "maxConcurrency": 2,
  "maxRequestRetries": 3,
  "maxRequestsPerMinute": 20
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# 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 = {
    "startUrls": [
        "https://www.g2.com/categories/crm"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("juanoox/g2-scraper").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 = { "startUrls": ["https://www.g2.com/categories/crm"] }

# Run the Actor and wait for it to finish
run = client.actor("juanoox/g2-scraper").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 '{
  "startUrls": [
    "https://www.g2.com/categories/crm"
  ]
}' |
apify call juanoox/g2-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,juanoox/g2-scraper"
        }
    }
}

```

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/3KJpaaWOoVvbpGdqi/builds/13qayMFIrDmGy2ubJ/openapi.json
