Using AggregateRating without risking a penalty
Star ratings in Google search results noticeably increase click-through rate, but Google's review schema policies are strict and actively enforced. Marking up reviews without visible content or borrowing third-party data risks losing rich result eligibility entirely. This article shows how to build Review and AggregateRating schema correctly, integrate it in Magento, and keep it within guidelines long term.
Table of Contents
- 1. Why star ratings determine click-through rate and trust
- 2. Review and AggregateRating schema: structure and required fields
- 3. Understanding Google's self-serving review policy
- 4. The visibility requirement: only what users can read belongs in schema
- 5. Review snippet abuse: manual actions and the 2019 rich result removal
- 6. Trustpilot, Trusted Shops, and others: third-party review syndication rules
- 7. Integrating Magento_Review correctly into Product schema
- 8. ViewModel pattern for aggregateRating in Hyvä themes
- 9. Compliant vs. risky review schema scenarios compared
- 10. Summary
- 11. FAQ
1. Why star ratings determine click-through rate and trust
Star ratings in Google search results are among the most effective rich result types out there: they draw the eye, convey instant trust, and visually distinguish a result from plain text snippets. For Magento stores with hundreds or thousands of product pages, this effect adds up to a measurable click-through advantage over competitors without AggregateRating schema. Studies on rich snippet performance regularly show double-digit increases in click-through rate when stars appear in search results.
That visibility comes at a price, though: Google scrutinizes review schema far more strictly than most other structured data types, because star ratings directly influence purchase behavior and are therefore a favorite target for abuse. Ignoring or not knowing the guidelines risks not just losing individual rich results, but a manual action affecting the entire domain in repeat cases. The following sections show how to build Review and AggregateRating schema correctly, cleanly separate it from third-party data, and integrate it into Magento within guidelines.
2. Review and AggregateRating schema: structure and required fields
The Review type describes a single, individual review and needs at minimum reviewRating (with ratingValue, optionally bestRating and worstRating), an author of type Person or Organization, and ideally reviewBody and datePublished. If bestRating/worstRating are missing, Google assumes the default 1 to 5 scale, which leads to incorrectly rendered stars when the actual scale differs.
The AggregateRating type summarizes multiple individual reviews into an average value and usually appears as a sub-object of Product, LocalBusiness, or another reviewable item, referenced via itemReviewed. Crucially, distinguish between reviewCount (the number of reviews with visible text) and ratingCount (the total number of star ratings, including those without text). Confusing the two fields, or specifying reviewCount without actual written review text existing, violates Google's documentation.
// CORRECT: first-party product review, fully visible on this page
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Sample Product",
"image": "https://mironsoft.de/media/catalog/product/sample.jpg",
"sku": "MS-1234",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"bestRating": "5",
"worstRating": "1",
"reviewCount": "128"
},
"review": [
{
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"ratingValue": "5",
"bestRating": "5",
"worstRating": "1"
},
"author": { "@type": "Person", "name": "Julia K." },
"datePublished": "2026-06-02",
"reviewBody": "Fast shipping, great build quality, exactly as described."
}
]
}
// Note: this exact review text is rendered visibly on the product page,
// directly above the script tag that outputs this markup.
3. Understanding Google's self-serving review policy
The self-serving review policy prohibits websites from marking up reviews about themselves, their own organization, or their own products and services with schema unless those reviews are rendered in full and visibly on exactly the page that carries the markup. The reasoning: Google wants to prevent businesses from attributing arbitrary star ratings to themselves without users being able to read the underlying reviews at all.
In practice, this means: an Organization rating on the homepage is only allowed if individual reviews with text, author, and date are actually visible there, not just an aggregated number. For pure product reviews, the exception is that Product schema with AggregateRating is allowed even without a full review list on the same page, as long as the underlying individual reviews are traceable and visible somewhere on the domain, such as in the product page's review tab.
// INCORRECT: self-serving aggregateRating with no visible review content
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Sample Store Inc.",
"url": "https://samplestore.example",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.6",
"reviewCount": "312"
}
}
// This number was copied from a third-party widget. No review text,
// author or star rating is rendered anywhere on this page, which
// violates Google's self-serving review policy and is not eligible
// for rich results, regardless of whether the number itself is accurate.
4. The visibility requirement: only what users can read belongs in schema
The core principle behind almost all of Google's structured data guidelines is this: schema must reflect the page's visible content, not add to or exceed it. For review schema, that means concretely: every individual review with reviewBody stated in the JSON-LD must appear as readable text on the same page, and every number in ratingValue must match the stars actually displayed. An AggregateRating without corresponding individual reviews present in the DOM is a classic trigger for rich result removal.
In practice, this rule is easy to check technically: when the page is viewed with JavaScript disabled or through Google's text-cache mode, the full review content must still be readable. Reviews that load exclusively via Ajax and remain invisible without interaction, or reviews hidden behind a tab click, are generally still considered visible under server-side rendering; with purely client-side loading and no SSR fallback, the risk of Google misjudging visibility is significantly higher.
5. Review snippet abuse: manual actions and the 2019 rich result removal
In September 2019, Google acted on years of abuse: review rich snippets were removed from organic search results for practically all page types outside of Product, LocalBusiness, and a few other permitted categories. Mainly affected were advisory websites, recipe portals, and affiliate sites that had attached blanket star ratings to articles or categories without those ratings being attributable to a concrete, reviewable product or business.
At the same time, Google tightened enforcement against structured data spam in general: when systematic abuse is detected, such as fabricated reviews, purchased reviews, or bulk-copied third-party data, Google issues manual actions, visible under "Manual Actions" in Search Console. Such an action typically affects not just review rich results, but can impact all structured data across the entire domain, and is only lifted after remediation and a reconsideration request.
6. Trustpilot, Trusted Shops, and others: third-party review syndication rules
Platforms like Trustpilot or Trusted Shops collect customer reviews centrally on their own domain and are of course entitled to output their own review schema there, with complete, visible reviews, because they are the actual host and content owners of that data. When a merchant embeds a Trustpilot widget on their own website, that widget usually shows only an aggregated star number without the underlying full texts, because the actual reviews remain hosted at Trustpilot.
That's exactly the point where many stores violate the guidelines: taking the aggregated number from a third-party widget and unilaterally outputting it as AggregateRating in your own schema, even though no visible review text exists on your own page, counts as a self-serving violation, regardless of whether the number itself is technically accurate. For stars in Google Shopping and Google Ads, the official path instead runs through Google Customer Reviews or Merchant Center product rating feeds, which work completely separately from schema.org review markup and are designed exactly for that purpose.
// CORRECT: ratingCount for star-only ratings without written review text
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Sample Product B",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.3",
"bestRating": "5",
"worstRating": "1",
"ratingCount": "47"
}
}
// ratingCount covers all star ratings, including those without text.
// reviewCount must only be used when written, visible review text exists.
// Never mix a third-party widget's total into either field without
// the underlying reviews being visible on this same page.
7. Integrating Magento_Review correctly into Product schema
Magento's native Magento_Review module manages approved customer reviews per product, including author, review text, individual rating, and approval status, visible in the product detail page's review tab. This data is the only correct source for AggregateRating values in Product schema, because the reviews are actually visible there and remain fully traceable via Magento\Review\Model\Review and the associated rating summary resource.
Magento's rating summary is stored internally on a 0 to 100 scale and must be converted to the standard 1 to 5 scale for schema.org. Equally important: the review count (getReviewsCount()) must be checked before outputting schema, since a product without approved reviews must not receive an aggregateRating object at all, not even placeholder values like 0, since Google flags this as an erroneous or fabricated field.
<?php
declare(strict_types=1);
namespace Mironsoft\SeoSuite\ViewModel;
use Magento\Catalog\Model\Product;
use Magento\Framework\View\Element\Block\ArgumentInterface;
use Magento\Review\Model\ResourceModel\Review\Summary as ReviewSummaryResource;
use Magento\Store\Model\StoreManagerInterface;
/**
* Builds AggregateRating schema data from genuine, visible Magento product reviews.
*/
class ProductReviewSchema implements ArgumentInterface
{
/**
* @param ReviewSummaryResource $reviewSummaryResource
* @param StoreManagerInterface $storeManager
*/
public function __construct(
private readonly ReviewSummaryResource $reviewSummaryResource,
private readonly StoreManagerInterface $storeManager
) {
}
/**
* Returns AggregateRating data, or null when no visible reviews exist.
* Never emit aggregateRating for a product without rendered review content.
*
* @param Product $product
* @return array<string, string>|null
*/
public function getAggregateRatingData(Product $product): ?array
{
$storeId = (int) $this->storeManager->getStore()->getId();
$this->reviewSummaryResource->load($product, (int) $product->getId(), $storeId);
$reviewCount = (int) $product->getReviewsCount();
if ($reviewCount === 0) {
// No visible reviews rendered on the page, omit aggregateRating entirely
return null;
}
// Magento stores the rating summary as 0-100, schema.org expects 1-5
$ratingSummary = (int) $product->getRatingSummary();
$ratingValue = round($ratingSummary / 20, 1);
return [
'ratingValue' => (string) $ratingValue,
'bestRating' => '5',
'worstRating' => '1',
'reviewCount' => (string) $reviewCount,
];
}
}
8. ViewModel pattern for aggregateRating in Hyvä themes
In Hyvä themes, the logic for determining review data belongs in a dedicated ViewModel that implements ArgumentInterface and is injected into the product layout via layout XML, instead of overloading block classes with extra responsibility. The ViewModel encapsulates access to the rating summary resource, the scale conversion, and the null check in a single place that is reused by both the visible review template and the JSON-LD template.
What matters for policy compliance is that exactly the same method feeds both the visible stars in the frontend and the values in the structured data block, so a discrepancy between display and schema can never occur. If the method returns null because no approved reviews exist, the associated phtml template consistently suppresses the entire aggregateRating block instead of outputting an empty or faulty placeholder.