G2 Reviews avatar

G2 Reviews

Pricing

from $2.00 / 1,000 per-run start fees

Go to Apify Store
G2 Reviews

G2 Reviews

Give it a G2 product slug and get that product's reviews as rows: title, full body, star rating, pros, cons, date and the source URL. Page back through the review history and cap the count per run. G2 blocks unauthenticated requests, so bring your own proxy.

Pricing

from $2.00 / 1,000 per-run start fees

Rating

0.0

(0)

Developer

SR

SR

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

G2 Reviews Scraper

Pull real customer reviews for any software product listed on G2 and get them back as clean, structured rows: who wrote the review, what they do for a living, how big their company is, the star rating they gave, the headline, the full text split into what they liked and what they did not, and the date. Point it at a product, say how many reviews you want, and press start.

G2 is where B2B software buyers go before they buy, which makes its review corpus the closest thing the category has to a public record of what customers actually think. This Actor turns that record into a dataset you can query, chart, feed into a model, or drop into a spreadsheet.

What you get

One row per review, de-duplicated, with these fields:

FieldWhat it holds
review_idG2's own identifier for the review. Stable across runs.
review_urlDirect link to that single review on G2.
titleThe headline the reviewer wrote.
ratingTheir star rating, on a 0 to 5 scale. Half stars occur, and 0 is a real rating.
rating_scale_min, rating_scale_maxAlways 0 and 5, stated so you never have to guess the scale.
reviewerDisplay name. Anonymous reviewers appear as "Verified User in ...".
roleTheir job title, when they chose to show it.
industryTheir industry, when they chose to show it.
company_sizeSmall-Business, Mid-Market or Enterprise, with the headcount band.
prosTheir answer to "What do you like best?"
consTheir answer to "What do you dislike?"
problems_solvedTheir answer to "What problems is this solving for you?"
bodyThe three answers joined, for when you just want the text.
date_publishedFull timestamp, ISO 8601.
date_displayedThe date exactly as G2 prints it on the card.
product_nameThe product's name as G2 states it.
product_ratingThe product's overall score, on a 0 to 10 scale.
product_rating_bestAlways 10, for the same reason as above.
product_review_countHow many reviews G2 holds for the product in total.
view, page, source_urlWhere the row came from, so any row is reproducible.

About those two scales

G2 publishes two different numbers that both look like ratings, and mixing them up is the most common mistake in G2 data. Each individual review is scored out of 5. The product's headline score is out of 10. A product showing 9.1 is not a 9.1-star product, and a review showing 4.5 is not 4.5 out of 10. This Actor reports both exactly as G2 states them and ships the scale bounds alongside each one, so nothing downstream has to infer it.

The review scale starts at 0, not at 1. Zero-star and half-star reviews are real and they cluster in the lowest-satisfaction listing, which is precisely the listing anyone doing a weakness analysis will select. Filtering on rating >= 1 would silently drop them.

Input

FieldTypeDefaultMeaning
productSlugstring, requiredslackThe product's identifier, taken from its own G2 address. For https://www.g2.com/products/slack/reviews the slug is slack.
maxReviewsinteger100How many distinct reviews to return, at most.
viewsarray["default"]Which listings to walk. See below.
regionstringemptyOptional preferred region. Leave empty unless you have a reason.

Getting more than 100 reviews

G2's review listing caps out at 100 reviews for any one ordering. That is a property of G2, not of this Actor, and no scraper can page past it on a single listing. What you can do is read a different listing of the same product, which holds its own 100 and largely different reviews.

Four listings are supported, and you can select any combination:

  • Default ordering, G2's own sort.
  • Newest first, which surfaces reviews written in the last weeks.
  • Highest rated first, which surfaces the strongest advocates.
  • Lowest satisfaction scores, which surfaces the unhappiest customers.

Selecting all four gives you up to 400 distinct reviews for one product. Overlap between listings is removed for you, so maxReviews: 400 means 400 different reviews, not 400 rows with repeats in them. In our own runs, the newest-first listing overlapped the default ordering by about 6 per cent and the two rating-based listings barely overlapped it at all, so the fourth listing is still adding new material rather than repeating the first.

If the product has fewer reviews than you asked for, you simply get what exists and the run reports why it stopped. Nothing is padded.

Output and the run summary

Reviews go to the dataset. The run also writes a summary record that tells you what happened, which matters more here than it does for most scrapers:

{
"productSlug": "slack",
"status": "ok",
"reviewsReturned": 200,
"productName": "Slack",
"productReviewCount": 39649,
"viewsWalked": [
{"view": "default", "pagesFetched": 10, "rowsNew": 100, "stoppedBecause": "view ceiling reached"},
{"view": "most_recent", "pagesFetched": 10, "rowsNew": 94, "rowsDuplicate": 6, "stoppedBecause": "view ceiling reached"}
],
"dataTrustworthy": true
}

status is the field to check. It is one of:

  • ok — reviews were returned.
  • no_reviews — the product exists and genuinely has none matching your selection.
  • not_found — there is no product at that slug. Check the slug against the product's own G2 address.
  • blocked — the source did not serve this run. No conclusion should be drawn about the product from this.
  • failed — the run could not start properly, usually a configuration problem.

That distinction is deliberate. A run that returns zero rows because a product has no reviews and a run that returns zero rows because the source refused it look identical if you only count rows, and they mean opposite things. This Actor never lets the second one masquerade as the first: dataTrustworthy tells you in one boolean whether the emptiness is a fact about the product.

The summary also reports rowsMissingReviewer, rowsMissingRating and rowsMissingBody. On a healthy run those are zero or near it. If they are large, the rows are suspect and you will know before you use them.

Worked examples

A quick look at one product. {"productSlug": "notion", "maxReviews": 25} returns 25 recent reviews in a single short run.

A full competitive read. {"productSlug": "hubspot-crm", "maxReviews": 400, "views": ["default", "most_recent", "highest_rated", "lowest_nps"]} returns up to 400 distinct reviews spanning both the advocates and the detractors, which is the shape you want for a win-loss analysis or a positioning deck.

Tracking sentiment over time. Run with views: ["most_recent"] on a schedule and de-duplicate on review_id in your own store. Each run brings back whatever is new.

Finding what to fix. views: ["lowest_nps"] and read the cons column. It is the shortest path from a review corpus to a product backlog.

Notes and limits

  • The 100-per-listing ceiling is G2's. Four listings is the honest maximum for a single product, which is 400 reviews.
  • Very large products have far more reviews than 400. This Actor does not claim otherwise, and product_review_count tells you the real total so you can see the gap yourself.
  • Reviewer job title and industry are optional on G2. Roughly half of all reviewers leave one or both blank, and anonymous reviewers leave the name blank too. Blank means the reviewer withheld it, not that the Actor missed it.
  • Reviews are returned as written, including typos and formatting. Nothing is rewritten, summarised or scored.
  • Free-plan Apify accounts receive the first 10 rows of each run. Paid plans are uncapped.

Operations

The Actor replays a session that is created outside the platform and handed to it, because the platform's own browser is refused the session every time. Three values travel together and the Actor refuses a partial set: the session cookie, the user agent of the browser that created it, and the address reference it was created through (region:id). They can be attached to the Actor version as secret environment variables (G2_MINT_COOKIE, G2_MINT_USER_AGENT, G2_MINT_PROXY_SESSION) or supplied on the input under "Advanced", where the input wins.

To refresh the session, attach it and rebuild in one go:

set -a && source ~/.apify-agents.env && set +a
~/CascadeProjects/scraping_script/.venv/bin/python scripts/g2_mint_session.py --attach

Things to know:

  • A session lives for a limited time and a limited number of requests, and the run summary says which of the two ran out (status: blocked, with the reason in the errors record). See the lifetime measured below.
  • credentialSource in the summary says whether the run used the supplied session (preminted) or fell back to its own browser (browser). On the platform the fallback does not work, so browser there means the session was not attached.
  • A redeploy with scripts/deploy.py replaces the version's environment variables and drops the three session values. Run the refresh command again after every deploy.
  • The address reference is only valid for as long as the gateway keeps that address; a session whose address has gone reports itself as expired.