A PHP library for scraping product reviews from G2.com using the Apify G2 Reviews Scraper. Returns fully typed DTOs for easy integration into your application. Safe, reliable and scalable.
composer require scraper-apis/g2-reviews-scraper- PHP 8.3+
- Apify API token (get one here)
use G2ReviewsScraper\Client;
$client = new Client('YOUR_APIFY_TOKEN');
// Scrape reviews for a product
$reviews = $client->scrape('https://www.g2.com/products/notion/reviews');
foreach ($reviews as $review) {
echo $review->title . ' - ' . $review->starRating . '★' . PHP_EOL;
}$reviews = $client->scrape(
url: 'https://www.g2.com/products/notion/reviews',
limit: 100
);use G2ReviewsScraper\SortOrder;
// Get lowest-rated reviews first (for competitor analysis)
$reviews = $client->scrape(
url: 'https://www.g2.com/products/jira/reviews',
limit: 500,
sortOrder: SortOrder::LowestRated
);// Only 1-2 star reviews
$reviews = $client->scrape(
url: 'https://www.g2.com/products/salesforce/reviews',
limit: 200,
starRatings: ['1', '2']
);// Reviews mentioning "integration"
$reviews = $client->scrape(
url: 'https://www.g2.com/products/hubspot/reviews',
limit: 100,
searchQuery: 'integration'
);// Scrape reviews + AI-generated pros/cons summary
$result = $client->scrapeWithProsCons(
url: 'https://www.g2.com/products/slack/reviews',
limit: 500
);
// Access reviews
foreach ($result['reviews'] as $review) {
echo $review->title . PHP_EOL;
}
// Access AI summary
$prosCons = $result['prosCons'];
echo "Product: {$prosCons->productName}" . PHP_EOL;
echo "Average Rating: {$prosCons->productAverageRating}" . PHP_EOL;
foreach ($prosCons->aiPros as $pro) {
echo "PRO: {$pro->text} ({$pro->mentions} mentions)" . PHP_EOL;
}
foreach ($prosCons->aiCons as $con) {
echo "CON: {$con->text} ({$con->mentions} mentions)" . PHP_EOL;
}foreach ($reviews as $review) {
// Basic info
echo $review->title;
echo $review->starRating; // e.g., 5.0
echo $review->date; // e.g., "2026-01-15"
// Reviewer details
echo $review->reviewerName; // e.g., "John Smith"
echo $review->reviewerTitle; // e.g., "Software Engineer"
echo $review->getCompanyInfo(); // e.g., "Mid-Market (51-1000 emp.) | Used for 2+ years"
// Review content (pros | cons | recommendations)
echo $review->text;
// Verification badges
if ($review->hasValidatedReviewer()) {
echo "Verified via: {$review->validatedMethod}"; // "LinkedIn" or "Business Email"
}
if ($review->isCurrentUser()) {
echo "Verified current user";
}
if ($review->incentivized) {
echo "Incentivized review";
}
// LLM-ready markdown (for RAG pipelines)
echo $review->markdownContent;
}// Filter for validated reviewers only
$validated = array_filter(
$reviews,
fn ($r) => $r->hasValidatedReviewer()
);
// Filter for current users only
$currentUsers = array_filter(
$reviews,
fn ($r) => $r->isCurrentUser()
);
// Filter by rating
$fiveStarReviews = array_filter(
$reviews,
fn ($r) => $r->starRating === 5.0
);
// Filter non-incentivized reviews
$organic = array_filter(
$reviews,
fn ($r) => !$r->incentivized
);| Property | Type | Description |
|---|---|---|
reviewId |
string | Unique review identifier |
productName |
string | Product name |
productSlug |
string | Product URL slug |
title |
?string | Review headline |
starRating |
?float | Rating (1.0-5.0) |
reviewerName |
?string | Reviewer's name |
reviewerTitle |
?string | Reviewer's job title |
date |
?string | Review date (YYYY-MM-DD) |
text |
?string | Full review text (pros | cons | recommendations) |
reviewerInfo |
?string[] | Company size, usage duration |
reviewerAvatarUrl |
?string | Avatar image URL |
reviewerMonogram |
?string | Avatar initials |
validatedReviewer |
bool | Reviewer identity verified |
validatedMethod |
?string | "LinkedIn" or "Business Email" |
verifiedCurrentUser |
bool | Verified current product user |
reviewSource |
string | Review source (e.g., "Organic") |
incentivized |
bool | Incentivized review flag |
markdownContent |
?string | LLM-ready markdown format |
| Property | Type | Description |
|---|---|---|
productName |
string | Product name |
productSlug |
string | Product URL slug |
productId |
?string | G2 product ID |
productUuid |
?string | G2 product UUID |
vendorId |
?string | Vendor ID |
vendorName |
?string | Vendor company name |
productType |
?string | Product type |
productCategory |
?string | Primary category |
productAverageRating |
?float | Overall rating |
productImageUrl |
?string | Product logo URL |
productCategories |
?string[] | All product categories |
aiPros |
AiSummaryItem[] | AI-generated pro points |
aiCons |
AiSummaryItem[] | AI-generated con points |
topPros |
ProsConsCategory[] | Top pro categories with samples |
topCons |
ProsConsCategory[] | Top con categories with samples |
| Property | Type | Description |
|---|---|---|
text |
string | Summary text |
keyword |
?string | Key term |
mentions |
int | Number of mentions |
| Property | Type | Description |
|---|---|---|
name |
string | Category name (e.g., "Ease of Use") |
summary |
string | Category summary |
keyword |
?string | Key term |
mentions |
int | Total mentions |
sampleReviews |
SampleReview[] | Sample reviews (max 2) |
| Enum | Value | Description |
|---|---|---|
SortOrder::MostRecent |
most_recent |
Newest reviews first |
SortOrder::MostHelpful |
most_helpful |
Most upvoted reviews first |
SortOrder::LowestRated |
lowest_rated |
1-star reviews first |
SortOrder::HighestRated |
highest_rated |
5-star reviews first |
use G2ReviewsScraper\Exception\ApiException;
use G2ReviewsScraper\Exception\RateLimitException;
use G2ReviewsScraper\Exception\InvalidUrlException;
try {
$reviews = $client->scrape($url);
} catch (InvalidUrlException $e) {
// Invalid G2 product URL
echo $e->getMessage();
} catch (RateLimitException $e) {
// Handle rate limiting
sleep($e->retryAfter);
} catch (ApiException $e) {
// Handle other API errors
echo $e->getMessage();
}When scraping multiple products, use reviewId as the unique key:
$allReviews = [];
$products = ['notion', 'asana', 'monday-com'];
foreach ($products as $product) {
$url = "https://www.g2.com/products/{$product}/reviews";
foreach ($client->scrape($url, limit: 100) as $review) {
$allReviews[$review->reviewId] = $review;
}
}
// $allReviews now contains unique reviews only// Get negative reviews for competitor analysis
$reviews = $client->scrape(
url: 'https://www.g2.com/products/competitor-product/reviews',
limit: 500,
sortOrder: SortOrder::LowestRated,
starRatings: ['1', '2', '3']
);
// Analyze common complaints
$complaints = [];
foreach ($reviews as $review) {
$complaints[] = $review->text;
}// Track reviews over time
$result = $client->scrapeWithProsCons(
url: 'https://www.g2.com/products/your-product/reviews',
limit: 1000,
sortOrder: SortOrder::MostRecent
);
$prosCons = $result['prosCons'];
$proMentions = $prosCons->getTotalProMentions();
$conMentions = $prosCons->getTotalConMentions();
$sentimentRatio = $proMentions / max(1, $conMentions);// Export reviews for LLM ingestion
foreach ($reviews as $review) {
// Use pre-formatted markdown
$document = $review->markdownContent;
// Or build custom format
$metadata = [
'reviewId' => $review->reviewId,
'product' => $review->productName,
'rating' => $review->starRating,
'validated' => $review->hasValidatedReviewer(),
];
// Store in vector database
$vectorDb->insert($document, $metadata);
}MIT