aggregateRating in structured data matches visible on-page values
Why it matters
A JSON-LD aggregateRating block that shows 4.5 stars while the page displays 4.3 stars is an active schema-org policy violation — Google's Rich Results guidelines prohibit structured data that misrepresents visible content. Discovery of the discrepancy triggers manual review and can result in a site-wide rich-result penalty. Beyond compliance, rating mismatch is a data-integrity failure: users who click a listing based on an inflated SERP star count and see a lower on-page rating experience an immediate trust collapse. The ratingValue and reviewCount in schema-org AggregateRating must be computed from the same database query that drives the visible UI.
Severity rationale
High because mismatched structured-data ratings violate Google's explicit rich-results content policies, risking manual review penalties that suppress all rich results site-wide.
Remediation
Derive the JSON-LD aggregateRating from the same source as the rendered rating — never hardcode or cache independently. Compute both from a single database read per listing.
// In your listing data fetcher
const listing = await db.listing.findUnique({
where: { id },
select: {
name: true,
averageRating: true,
reviewCount: true,
// ...
},
});
const jsonLd = {
"@type": "LocalBusiness",
...(listing.reviewCount > 0 && {
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": listing.averageRating,
"reviewCount": listing.reviewCount,
},
}),
};
The guard listing.reviewCount > 0 prevents an empty aggregateRating block, which is separately flagged as invalid by Google's structured data validator.
Detection
-
ID:
rating-accuracy -
Severity:
high -
What to look for: Count all listings with ratings. For each, find the aggregateRating object in JSON-LD. Compare its
ratingValueandreviewCountagainst the ratings/review counts displayed on the page. Verify they match. -
Pass criteria: The JSON-LD aggregateRating values match the displayed rating and review count on the page — 100% of displayed ratings must match the computed average of individual reviews, with no more than 0.1 point deviation. Report: "X rated listings found, all Y display accurate aggregate ratings."
-
Fail criteria: The JSON-LD shows one rating/count while the page displays different values, or aggregateRating is present but empty.
-
Skip (N/A) when: The listing has no ratings or reviews.
-
Detail on fail: Quote the actual rating values showing the discrepancy. Example:
"JSON-LD shows 4.5 rating and 120 reviews, but page displays 4.3 stars and 95 reviews"or"aggregateRating block present but empty" -
Remediation: Populate aggregateRating from the same source as the displayed ratings:
const jsonLd = { "@type": "LocalBusiness", "aggregateRating": { "@type": "AggregateRating", "ratingValue": listing.averageRating, "reviewCount": listing.reviewCount } }
External references
- schema-org · AggregateRating — AggregateRating type for rating structured data
- schema-org · ratingValue — ratingValue property must match displayed value
- schema-org · reviewCount — reviewCount property must match displayed count
Taxons
History
- 2026-04-18·v1.0.0·Initial import from directory-listing-schema·automated