{"id":47,"date":"2026-09-01T18:37:48","date_gmt":"2026-09-01T11:37:48","guid":{"rendered":"https:\/\/testivo.tech\/blog\/schema-markup-for-testimonials"},"modified":"2026-09-08T11:16:22","modified_gmt":"2026-09-08T04:16:22","slug":"schema-markup-for-testimonials","status":"publish","type":"post","link":"https:\/\/testivo.tech\/blog\/schema-markup-for-testimonials","title":{"rendered":"Schema markup for testimonials: the complete 2026 guide"},"content":{"rendered":"<h2 class=\"wp-block-heading\">The 30-second version<\/h2>\n<p class=\"wp-block-paragraph\">Schema markup is a small block of JSON-LD you drop in your page&#8217;s <code><head><\/code> (or at the bottom of <code><body><\/code>) that tells search engines. and increasingly AI assistants. &#8220;this is a third-party review of this product, by this person, with this rating.&#8221; For testimonials, the three schema types you&#8217;ll actually use are <code>Review<\/code>, <code>AggregateRating<\/code>, and <code>Quotation<\/code>.<\/p>\n<p class=\"wp-block-paragraph\">Used correctly, schema is the difference between a star rating showing in your Google snippet and a generic blue link. Used wrongly, you trigger a manual action and lose the snippet for months.<\/p>\n<p class=\"wp-block-paragraph\">This guide covers the exact JSON-LD, the seven mistakes that get you ignored, and how AI search engines (ChatGPT, Claude, Perplexity) read your schema. or don&#8217;t.<\/p>\n<h2 class=\"wp-block-heading\">Why testimonials specifically need schema<\/h2>\n<p class=\"wp-block-paragraph\">A testimonial is, structurally, a third-party claim about your product. The reader has no way to verify it, no way to know if the photo is real, and no way to know if the company paid for it. Schema markup doesn&#8217;t fix any of that, but it does two important things:<\/p>\n<ol class=\"wp-block-list\">\n<li>It moves the testimonial from &#8220;anonymous marketing text&#8221; to &#8220;machine-readable review&#8221;. the same data class Google uses for product reviews on shopping sites, app store reviews, and the like.<\/li>\n<li>It survives the screenshot-to-text gap. Screenshot testimonials are invisible to crawlers. A testimonial on a real page, with valid schema, is not.<\/li>\n<\/ol>\n<p class=\"wp-block-paragraph\">This is also why testimonial <em>software<\/em>. a wall of love tool, a collection form, an embed. matters for SEO. A JPEG of a tweet doesn&#8217;t ship with <code><script type=\"application\/ld+json\"><\/code>. A wall page does.<\/p>\n<h2 class=\"wp-block-heading\">The three schema types, and when to use each<\/h2>\n<p class=\"wp-block-paragraph\">Schema.org has more \"review-shaped\" types than you'll need. For testimonials specifically, you're choosing between three:<\/p>\n<figure class=\"wp-block-table\">\n<table class=\"has-fixed-layout\">\n<thead>\n<tr>\n<th>Type<\/th>\n<th>When to use it<\/th>\n<th>What it does<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>Review<\/code><\/td>\n<td>A single, named, third-party testimonial of a specific product or service.<\/td>\n<td>Marks one review as a structured fact. Eligible for rich results.<\/td>\n<\/tr>\n<tr>\n<td><code>AggregateRating<\/code><\/td>\n<td>You have multiple reviews and want to show a star average.<\/td>\n<td>Wraps multiple <code>Review<\/code> items and exposes <code>ratingValue<\/code> + <code>reviewCount<\/code>. The Google rich-snippet star.<\/td>\n<\/tr>\n<tr>\n<td><code>Quotation<\/code><\/td>\n<td>The testimonial is a quote you want to surface but it's not a review of a product. e.g., a quote from an interview, a press mention, a thought-leadership pull.<\/td>\n<td>Marks the text as quoted content. Not eligible for star rich results.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/figure>\n<p class=\"wp-block-paragraph\">The common mistake is using <code>Review<\/code> for everything. A pull-quote from your CEO's podcast appearance is a <code>Quotation<\/code>. A customer's \"switched from X to Y, saved 12 hours\/week\" comment on your wall of love is a <code>Review<\/code>. Mixing them up costs you either the rich snippet or the data integrity.<\/p>\n<h2 class=\"wp-block-heading\">The exact JSON-LD for a single <code>Review<\/code><\/h2>\n<p class=\"wp-block-paragraph\">Here's the minimum valid <code>Review<\/code> schema for a B2B SaaS testimonial. Drop this into a <code><script type=\"application\/ld+json\"><\/code> block anywhere on the page that contains the testimonial.<\/p>\n<pre class=\"wp-block-code language-json\"><code>{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"Review\",\n  \"itemReviewed\": {\n    \"@type\": \"SoftwareApplication\",\n    \"name\": \"Testivo\",\n    \"applicationCategory\": \"BusinessApplication\",\n    \"operatingSystem\": \"Web\"\n  },\n  \"author\": {\n    \"@type\": \"Person\",\n    \"name\": \"Maya Chen\",\n    \"jobTitle\": \"Head of Growth\",\n    \"worksFor\": {\n      \"@type\": \"Organization\",\n      \"name\": \"Lumen Analytics\"\n    }\n  },\n  \"datePublished\": \"2026-08-12\",\n  \"reviewBody\": \"We replaced three different tools with Testivo and cut our testimonial workflow from 6 hours a week to under 30 minutes. The wall of love ships with schema already wired up, which is the part nobody else gets right.\",\n  \"reviewRating\": {\n    \"@type\": \"Rating\",\n    \"ratingValue\": \"5\",\n    \"bestRating\": \"5\",\n    \"worstRating\": \"1\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"Testivo\"\n  }\n}<\/code><\/pre>\n<p class=\"wp-block-paragraph\">A few things to notice:<\/p>\n<ul class=\"wp-block-list\">\n<li><code>itemReviewed<\/code> is a <code>SoftwareApplication<\/code>, not just an <code>Organization<\/code>. Schema.org distinguishes between reviewing the company and reviewing the product. For SaaS, the product is almost always what you want.<\/li>\n<li><code>datePublished<\/code> is <strong>required<\/strong> for AI recency signals and is recommended by Google for review rich results. Most guides skip it.<\/li>\n<li><code>author<\/code> is a <code>Person<\/code>, not a string. If you skip <code>jobTitle<\/code> and <code>worksFor<\/code>, you lose the E-E-A-T bump that makes the testimonial look like a real person's real experience.<\/li>\n<li><code>reviewRating<\/code> uses <code>bestRating<\/code> and <code>worstRating<\/code> so the schema is unambiguous. your \"5\" means 5 on a 5-point scale, not 5 on a 10-point scale.<\/li>\n<\/ul>\n<h2 class=\"wp-block-heading\">The exact JSON-LD for <code>AggregateRating<\/code><\/h2>\n<p class=\"wp-block-paragraph\">If your wall of love shows an average rating across many reviews, you want <code>AggregateRating<\/code>. Here's the pattern:<\/p>\n<pre class=\"wp-block-code language-json\"><code>{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"Product\",\n  \"name\": \"Testivo\",\n  \"aggregateRating\": {\n    \"@type\": \"AggregateRating\",\n    \"ratingValue\": \"4.9\",\n    \"reviewCount\": \"127\",\n    \"bestRating\": \"5\",\n    \"worstRating\": \"1\"\n  }\n}<\/code><\/pre>\n<p class=\"wp-block-paragraph\">Two gotchas:<\/p>\n<ol class=\"wp-block-list\">\n<li>The <code>aggregateRating<\/code> must belong to something. Nest it inside a <code>Product<\/code>, <code>SoftwareApplication<\/code>, or <code>Service<\/code>. not on its own. Schema.org's validator will throw an error otherwise.<\/li>\n<li><code>reviewCount<\/code> and <code>ratingValue<\/code> must be honest. If you have 6 testimonials and you set <code>reviewCount: 127<\/code>, Google will eventually catch it and you lose the rich snippet permanently. The honest version of \"we have 6 reviews, average 4.9\" is still eligible. the threshold is a quality bar, not a volume bar.<\/li>\n<\/ol>\n<p class=\"wp-block-paragraph\">A common practice is to emit <em>both<\/em> a <code>Review<\/code> block per testimonial and an <code>AggregateRating<\/code> block on the wall's parent page. They don't conflict; the aggregate is a derived value from the individual reviews. If you're collecting from social DMs and tweets, the <a href=\"\/blog\/import-testimonials-from-twitter-linkedin\">import-from-twitter workflow<\/a> gives you a head start.<\/p>\n<p class=\"wp-block-paragraph\">you already have the social proof, you just need to put it on a real, schema-valid page.<\/p>\n<h2 class=\"wp-block-heading\">The seven mistakes that get your markup ignored<\/h2>\n<p class=\"wp-block-paragraph\">These are the failures we see most often when auditing customer walls, ranked by frequency.<\/p>\n<p class=\"wp-block-paragraph\"><strong>1. Markup is in the page but the testimonial is in an image.<\/strong> If your <a href=\"\/blog\/wall-of-love-what-it-is-and-how-to-add-one\">wall of love<\/a> renders customer quotes as PNG screenshots, the text inside the screenshots is invisible to the crawler no matter how good the schema is.<\/p>\n<p class=\"wp-block-paragraph\">Schema describes what's on the page. If the content isn't text on the page, schema can't describe it. (This is also why we built the wall to be a real text page, not a carousel of images.)<\/p>\n<p class=\"wp-block-paragraph\"><strong>2. Schema references a <code>Person<\/code> who doesn't exist on the page.<\/strong> Google's Rich Results test is increasingly strict. If your JSON-LD says the reviewer is \"Maya Chen, Head of Growth at Lumen Analytics\" but the page only shows \"Maya C.\", the schema is treated as misleading. Either match the page text to the schema, or vice versa.<\/p>\n<p class=\"wp-block-paragraph\"><strong>3. Missing <code>datePublished<\/code>.<\/strong> This is the one we flag most. Without a date, the review has no recency signal. For a testimonial collected in 2024, the AI assistant reading your page in 2026 has no way to know how old it is. Always include it.<\/p>\n<p class=\"wp-block-paragraph\"><strong>4. Using <code>Organization<\/code> as the <code>author<\/code> of a customer review.<\/strong> Your customer is a <code>Person<\/code>, not your own <code>Organization<\/code>. Using <code>Organization<\/code> here is the technical signal for \"we wrote this about ourselves\". which is the <em>self-review<\/em> pattern Google explicitly warns against in its <a href=\"https:\/\/developers.google.com\/search\/docs\/essentials\/spam-policies\" target=\"_blank\" rel=\"noopener\">review spam policies<\/a>.<\/p>\n<p class=\"wp-block-paragraph\"><strong>5. Reviewing your own product with your own name.<\/strong> A test pattern: your marketing team writes five \"testimonials\" and signs them with fictional company names. Schema doesn't catch this on its own, but if the testimonial is also linked from your homepage footer or repeated across pages, Google's manual reviewers will. The fix is structural: collect real reviews from real customers, then publish them.<\/p>\n<p class=\"wp-block-paragraph\"><strong>6. Inconsistent rating scales.<\/strong> If your UI shows 5 stars but the schema says <code>ratingValue: 10<\/code> and <code>bestRating: 10<\/code>, the rich result will silently fail. Google checks <code>ratingValue <= bestRating<\/code> and <code>ratingValue >= worstRating<\/code>. Pick a scale and use it everywhere. on the page, in the schema, in any widgets.<\/p>\n<p class=\"wp-block-paragraph\"><strong>7. Markup on a noindex page.<\/strong> If your wall of love is set to <code>noindex<\/code> (often by accident, via a stray Yoast setting or a custom robots tag), the schema is collected but not surfaced. Either remove the noindex or accept that the page won't show rich results.<\/p>\n<h2 class=\"wp-block-heading\">Schema vs <code>llms.txt<\/code> vs <code>robots.txt<\/code>: what each AI crawler actually reads<\/h2>\n<p class=\"wp-block-paragraph\">A common question in 2026: if I have schema, do I need anything else for AI search? The honest answer is layered.<\/p>\n<ul class=\"wp-block-list\">\n<li><strong>Schema (JSON-LD)<\/strong> is read by Google's crawler and Bing's crawler, which both feed ChatGPT, Copilot, and Perplexity. Schema affects what the <em>search backend<\/em> sees, not the model itself.<\/li>\n<li><strong><code>llms.txt<\/code><\/strong> is a static plain-text file at <code>\/llms.txt<\/code> (and sometimes <code>\/llms-full.txt<\/code>) that hands AI assistants a clean, normalized version of your content. Schema doesn't do this. Without llms.txt, the AI assistant has to crawl your HTML, follow JavaScript, and guess which content is structured. With it, you give them a one-shot read of everything.<\/li>\n<li><strong><code>robots.txt<\/code><\/strong> decides which crawlers can fetch your site at all. If you block <code>GPTBot<\/code> or <code>ClaudeBot<\/code> in robots.txt, no amount of schema will get you mentioned in their answers. Most sites do this by default; the fix is two lines.<\/li>\n<\/ul>\n<p class=\"wp-block-paragraph\">The pragmatic stack is: schema for Google\/Bing, llms.txt for ChatGPT\/Claude\/Perplexity, robots.txt to make sure you haven't blocked anyone important. All three, for the cost of a few minutes. None of them, alone, is enough anymore.<\/p>\n<p class=\"wp-block-paragraph\">A wall of love page that has all three. valid <code>Review<\/code> and <code>AggregateRating<\/code> JSON-LD, an <code>llms.txt<\/code> entry, and a robots.txt that allows the major AI crawlers. is materially more citable than a wall page that just renders testimonials as text. This is the same point we make in our <a href=\"\/blog\/do-testimonials-help-seo\">overview of testimonials and SEO<\/a>, and the reason schema is necessary but not sufficient.<\/p>\n<h2 class=\"wp-block-heading\">How to validate before you publish<\/h2>\n<p class=\"wp-block-paragraph\">Two tools will catch 95% of issues before Google does.<\/p>\n<p class=\"wp-block-paragraph\"><strong>Google's <a href=\"https:\/\/search.google.com\/test\/rich-results\" target=\"_blank\" rel=\"noopener\">Rich Results Test<\/a><\/strong>. paste your URL, get a per-block report. If your <code>Review<\/code> or <code>AggregateRating<\/code> is eligible for a rich result, this is the tool that tells you so. The test runs the same parser Google uses in production.<\/p>\n<p class=\"wp-block-paragraph\"><strong>Schema.org's <a href=\"https:\/\/validator.schema.org\/\" target=\"_blank\" rel=\"noopener\">Markup Validator<\/a><\/strong>. stricter, schema-spec-level. If your JSON-LD parses here, it's structurally valid against the vocabulary. The Rich Results Test is more lenient but more \"Google-shaped.\"<\/p>\n<p class=\"wp-block-paragraph\">Run both. If the Schema.org validator accepts it and the Rich Results test says \"eligible for review rich result\", you're done. If either fails, the error message usually names the property. fix it, re-test, ship.<\/p>\n<p class=\"wp-block-paragraph\">One last check that takes ten seconds: open the page in a private browser, view source, and confirm the <code><script type=\"application\/ld+json\"><\/code> block is actually in the HTML. Plenty of test environments serve a different page than production, and you don't want to find out from a search console notification three weeks later.<\/p>\n<h2 class=\"wp-block-heading\">When this is enough, and when you want a tool<\/h2>\n<p class=\"wp-block-paragraph\">If you have a small number of testimonials (say, under twenty) and you publish them on a single page by hand, copying the JSON-LD templates above and validating them is the right move. It's a one-time job and the maintenance is trivial. new testimonial, new JSON-LD block, re-validate.<\/p>\n<p class=\"wp-block-paragraph\">Past that point, the manual work gets old. A dedicated <a href=\"\/testimonial-software\">testimonial tool<\/a> handles the schema generation, the <code>llms.txt<\/code> feed, the AI-crawler robots.txt check, and the wall page itself in one shot. For most teams shipping more than a testimonial a month, the time savings pay for the tool within the first quarter.<\/p>\n<p class=\"wp-block-paragraph\">Either way, the markup itself is the same. <code>Review<\/code> for individual testimonials, <code>AggregateRating<\/code> for the wall summary, <code>datePublished<\/code> on every entry. Get those right and the rich snippet is yours.<\/p>\n<p class=\"wp-block-paragraph\">The full reference for review-related schema is the <a href=\"\/blog\/review-schema\">review schema pillar<\/a>, with the three types (Review, AggregateRating, Quotation) and the honest scope on what schema does and does not do.<\/p>\n<p class=\"wp-block-paragraph\">The schema layer only works on testimonials you have actually collected. The <a href=\"\/blog\/how-to-collect-testimonials\">how to collect testimonials<\/a> page is the upstream reference for the collection process the schema layer renders.<\/p>\n<p class=\"wp-block-paragraph\">The schema layer applies to one type. The <a href=\"\/blog\/types-of-social-proof\">types of social proof<\/a> page covers the full typology.<\/p>\n<p class=\"wp-block-paragraph\">The schema layer applies to one advocacy output. The <a href=\"\/blog\/customer-advocacy\">customer advocacy pillar<\/a> is the system context.<\/p>\n<p class=\"wp-block-paragraph\">The schema layer applies to the written format primarily. The <a href=\"\/blog\/types-of-testimonials\">types of testimonials<\/a> page is the format typology.<\/p>\n<p class=\"wp-block-paragraph\">Schema applies to displayed testimonials. The <a href=\"\/blog\/displaying-testimonials\">displaying testimonials<\/a> page is the placement typology the schema layer renders.<\/p>\n<p class=\"wp-block-paragraph\">The Review schema implementation has a sibling question. The <a href=\"\/blog\/faq-page-schema-vs-review-schema\">FAQ page schema vs review schema<\/a> page covers the distinction and the policy changes.<\/p>\n<p class=\"wp-block-paragraph\">The schema layer is for testimonials. The <a href=\"\/blog\/customer-referrals\">customer referrals<\/a> page covers the referral output.<\/p>\n<p class=\"wp-block-paragraph\">The Schema.org markup is the technical implementation that makes AI citation possible. The <a href=\"\/blog\/testimonials-and-ai-search\">testimonials and AI search<\/a> page is the why, the schema page is the how. For the conversion-impact framework, the <a href=\"\/blog\/social-proof-for-conversion\/\">social proof for conversion<\/a> page is the dedicated reference.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The exact JSON-LD Review and AggregateRating markup Google accepts, the seven mistakes that get your rich result ignored, and how AI search engines read your testimonials differently. With copy-paste examples.<\/p>\n","protected":false},"author":2,"featured_media":292,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[13,12,11],"class_list":["post-47","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-testimonials-and-seo","tag-ai-search","tag-llms-txt","tag-schema-markup"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":8}},"_links":{"self":[{"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/posts\/47","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/comments?post=47"}],"version-history":[{"count":16,"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/posts\/47\/revisions"}],"predecessor-version":[{"id":544,"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/posts\/47\/revisions\/544"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/media\/292"}],"wp:attachment":[{"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/media?parent=47"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/categories?post=47"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/testivo.tech\/blog\/wp-json\/wp\/v2\/tags?post=47"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}