Testimonials on a page do two jobs. The obvious job is reader-facing: a visitor sees the quote, gains trust, and is closer to converting. The less-obvious job is search-engine-facing: when the testimonial is marked up as structured data, Google can render star ratings, review counts, and snippet text in the search result itself — which earns clicks before the visitor ever reaches the page.
The catch is that Google has tightened the eligibility rules for review rich results aggressively over the past few years. A page that ticks every Schema.org checkbox can still fail to show stars in search, and there are three specific failure modes responsible for almost every "I added the markup but the stars never appeared" complaint. This post walks through what the markup looks like, what eligibility actually requires, and how to verify that your markup is being honored.
The two relevant Schema.org types
Two types do almost all the work for testimonials.
Review describes a single review, with required fields: an author, a reviewBody (the quote), a datePublished, and the itemReviewed (the product, service, or organization being reviewed). Optionally a reviewRating adds a star rating; without it, Google may still index the review but will not render stars.
AggregateRating describes the rolled-up score across many reviews, with required fields: a ratingValue (e.g., 4.7), a ratingCount or reviewCount, and the itemReviewed. This is what produces the "★★★★☆ 4.7 (243)" line under a search result.
Both types nest inside the itemReviewed schema — typically Product, Service, LocalBusiness, Organization, or SoftwareApplication. The choice of parent type matters because Google has different rich-result eligibility rules for each.
A minimal correct example
Here is the smallest correct markup for a SaaS product testimonial page:
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Acme Project Manager",
"applicationCategory": "BusinessApplication",
"operatingSystem": "Web",
"offers": {
"@type": "Offer",
"price": "49.00",
"priceCurrency": "USD"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"ratingCount": "243",
"bestRating": "5",
"worstRating": "1"
},
"review": [
{
"@type": "Review",
"author": { "@type": "Person", "name": "Sarah Kim" },
"datePublished": "2026-03-12",
"reviewBody": "Cut our weekly status meeting from 45 minutes to 12.",
"reviewRating": {
"@type": "Rating",
"ratingValue": "5",
"bestRating": "5"
}
}
]
}
Three things in that snippet matter beyond Schema.org spec compliance.
The Offer block is required for SoftwareApplication to be eligible for rich results. Many engineering teams omit it because the testimonials page does not have its own price — and that omission is the single most common cause of rich-result ineligibility. If you do not want to expose price publicly, you can still include an Offer with a priceValidUntil and a placeholder.
The bestRating and worstRating are technically optional but recommended. Without them, Google assumes a 1-5 scale, which is correct here. If you use a 1-10 scale, omitting these fields produces visibly wrong star renders.
The datePublished on each review must be a real ISO date, not a relative phrase. Reviews older than 12 months are fine to include, but they will sometimes be excluded from rich-result rotation, so do not rely on a wall of three-year-old testimonials carrying SEO weight.
The "self-serving" rule
Google's structured-data documentation has, since 2019, prohibited rich results for reviews "of the entity being marked up" when those reviews appear on the entity's own site. This is the rule that confuses every team adding testimonial markup for the first time.
The rule, simplified: if you sell Product X and you put Review markup on yourpurpduct.com about Product X, Google will not show stars in the search result for that page. The rule exists because the obvious abuse case is a brand putting glowing five-star reviews on its own marketing pages and harvesting rich results from them.
What this means for testimonial pages:
- A SaaS company's own homepage with
Reviewmarkup → no rich results (self-serving) - A SaaS company's product page with
AggregateRatingmarkup → also no rich results - A SaaS comparison or review site marking up reviews of other companies' products → rich results allowed
- A SaaS company marking up reviews of an integration partner → rich results allowed if the
itemReviewedis the partner
There is a narrow exception for LocalBusiness (a coffee shop can mark up its own reviews) and for Organization-level reviews where the reviews are about the company as a whole, not specific products. But for most SaaS and ecommerce testimonial pages, the markup is technically valid and crawlable but Google will not render stars in search.
This is not a bug. It is the policy. The reason teams keep getting confused is that the validator (Rich Results Test) shows the markup as eligible — eligibility for the schema is separate from rendering decision in the SERP.
What you still gain from the markup even without stars
The self-serving rule disables the visible star render but does not disable the structured-data ingestion. Google still parses the markup, indexes the review text, and uses it to inform other features:
- Knowledge panel data (organization-level)
- Product knowledge cards (when aggregated from multiple sources)
- Generative AI overview citations (when AI answers reference customer feedback)
So marking up testimonials remains worth doing for first-party SaaS and ecommerce sites — just calibrate expectations: you are feeding the index, not winning visible SERP stars.
For a comparison site, an external review aggregator, or a partner-review page, the same markup does produce visible stars and is unambiguously worth doing.
Three failure modes that disable rich results
Even on pages where rich results are eligible (third-party reviews, LocalBusiness, etc.), three failure modes account for almost all "I added the markup but stars never showed up" cases.
Failure 1: Review content not visible on the page. Google requires that the marked-up review text be visible in the rendered HTML. If the testimonials are loaded via client-side JavaScript and Googlebot's render does not include them, the markup is treated as misleading. The fix is server-side rendering or static-include of the testimonial content.
Failure 2: Markup more permissive than the on-page content. A page that displays three reviews but marks up an AggregateRating of 243 reviews is treated as deceptive. The markup must reflect what the page actually shows, with at most a small implied aggregate (a "see all 243 reviews" link to a separate page is fine).
Failure 3: Sparse or stale content. Pages with fewer than 5-10 reviews tend not to win rich results even when eligible. The threshold is not published, but in practice the bar appears to be at least 5 visible reviews with datePublished in the last 12-18 months.
Validation workflow
Three tools to run after deploying markup:
The Rich Results Test (search.google.com/test/rich-results) parses your URL and reports schema validity plus eligibility for specific result types. This catches syntax errors and missing required fields immediately.
Google Search Console > Enhancements > Review Snippets shows aggregate validation across all your indexed pages and surfaces new errors as Googlebot discovers them. Check this weekly for the first month after deploying review markup.
The URL Inspection tool (also in Search Console) shows the actual rendered HTML Googlebot saw, which is how you confirm that your testimonials are visible in the rendered page (Failure 1 above).
When the markup is worth doing and when it is not
Markup is worth doing in three scenarios:
- You run a third-party review or comparison site (rich results eligible, direct SERP value)
- You run a LocalBusiness with its own customer reviews (self-serving rule has the LocalBusiness exception)
- You want to feed Google's index for organization-level knowledge graph and AI-overview citations even without visible stars
Markup is not worth a custom build when:
- You have fewer than 5 testimonials per product (under the implicit volume threshold)
- Your testimonials are loaded via a third-party widget that cannot expose structured data (the widget vendor has likely already handled this; check before adding your own)
- Your testimonials are unattributed or anonymized (Schema.org requires
author; placeholder authors are policy-violating)
A pragmatic implementation order
For a SaaS team adding testimonial schema for the first time, the order that produces the most value with the least effort:
-
Add
Organization-levelAggregateRatingto your homepage, sourced from a third-party review platform (G2, Capterra, Trustpilot) where the underlying reviews already exist. This is the cleanest case because the platform has already done the verification. -
Add
Reviewmarkup to product pages where you display individual customer testimonials. Even with the self-serving rule, this feeds the index. -
Add
AggregateRatingto product pages once you have at least 10 reviews per product visible on the page. -
Skip schema entirely for a wall-of-love or social-proof carousel page until it is replaced by a page that meets the visibility and freshness requirements.
The marginal SEO value from steps 1-3 is real but modest — typically a 1-3% lift in CTR for the affected pages over six months in the data we see from teams that measure it. Treat schema as a hygiene step rather than a growth lever, ship it once, monitor it in Search Console, and move on.