Schema.org Types in 2026: complete current-state reference
Companion to framework-schema.md. This document inventories the schema types that move citation needles in 2026. The parent framework covers JSON-LD installation, the @id graph…
Advanced Schema.org Type Reference for AI Search and Modern SERP Features
Companion to framework-schema.md. This document inventories the schema types that move citation needles in 2026. The parent framework covers JSON-LD installation, the @id graph pattern, and the foundation schemas every site needs. This framework drills into the type-specific patterns for SoftwareApplication, Course, Event, JobPosting, Recipe, HowTo, FAQPage, QAPage, Review, Product, LocalBusiness subtypes, Person, ProfilePage, ClaimReview, and the broader CreativeWork hierarchy.
Cross-stack implementation note: code samples are JSON-LD inside
<script type="application/ld+json">blocks. For React, Vue, Svelte, Next.js, Nuxt, SvelteKit, Astro, Hugo, 11ty, Remix, WordPress, Shopify, and Webflow equivalents see framework-cross-stack-implementation.md. For pure client rendered SPAs see framework-react.md.
1. Document Purpose
Canonical reference for advanced Schema.org types relevant to 2026 SEO and AI search. The parent framework-schema.md covers foundation (format selection, @id graph, Organization/WebSite/WebPage/Person/BreadcrumbList, validation, audit rubric). This framework covers type-specific patterns for products, events, jobs, courses, recipes, software, reviews, fact checks, and the dozens of specialized content types Schema.org defines.
AI engines extract entities from structured data more reliably than from prose. Schema App 2025 study (14,200 pages): 71% of pages cited by ChatGPT, Perplexity, and Google AI Overviews had specific subtype schema (Plumber, Recipe, SoftwareApplication, JobPosting) versus 19% for non-cited pages with generic schema. 84% of Google AI Overview product citations carried full Product schema with Offer, AggregateRating, and brand.
Three forces drive type specificity in 2026: Google's Knowledge Graph weights subtype specificity when reconciling entities (Plumber resolves more confidently than LocalBusiness); AI engines parse schema for type tokens matching query intent; SERP features reward specific types with dedicated visual treatments (Job Posting cards, Course carousels, Recipe rich results, Event packs).
Required tools: Google Rich Results Test, Schema.org Validator, Google Search Console Enhancements, Yandex Microdata Validator, JSON-LD Playground, curl plus jq, Schema.org Pending vocabulary tracker.
2. SoftwareApplication and MobileApplication
2.1 The SoftwareApplication Type
SoftwareApplication covers SaaS products, downloadable desktop applications, command line tools, browser extensions, and APIs sold as products. The type drives the Install button rich result on Google when paired with applicationCategory, operatingSystem, an Offer, and an AggregateRating.
{
"@type": "SoftwareApplication",
"@id": "https://example.com/products/myapp/#software",
"name": "MyApp",
"operatingSystem": "Windows, macOS, Linux",
"applicationCategory": "BusinessApplication",
"applicationSubCategory": "InvoicingSoftware",
"softwareVersion": "4.2.1",
"datePublished": "2026-03-01",
"fileSize": "84MB",
"downloadUrl": "https://example.com/download/myapp-4.2.1.dmg",
"screenshot": "https://example.com/screenshots/myapp-dashboard.png",
"softwareRequirements": "macOS 12 or later, 8GB RAM, 200MB disk",
"publisher": { "@id": "https://example.com/#organization" },
"offers": {
"@type": "Offer",
"price": "29.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"reviewCount": "1432"
}
}
2.2 The MobileApplication Type
MobileApplication is a SoftwareApplication subclass for iOS and Android apps distributed via App Store, Google Play, or sideload. The dedicated subclass exists because Google ranks apps in a separate index and the rich result treatment is distinct from desktop software.
{
"@type": "MobileApplication",
"@id": "https://example.com/apps/mybox/#mobile",
"name": "MyBox Mobile",
"operatingSystem": "iOS 16.0+, Android 13+",
"applicationCategory": "ProductivityApplication",
"installUrl": [
"https://apps.apple.com/us/app/mybox/id1234567890",
"https://play.google.com/store/apps/details?id=com.example.mybox"
],
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.6",
"ratingCount": "12840"
}
}
2.3 App Store and Google Play Deep Linking
Two patterns drive the install rich result. The simpler pattern uses installUrl with both stores listed. The richer pattern declares the app as the sameAs target of a parent SoftwareApplication. Apple App Store URLs follow apps.apple.com/us/app/[slug]/id[number]; Google Play follows play.google.com/store/apps/details?id=[package]. Chrome Web Store, Firefox Add-ons, and Microsoft Store URLs are also valid sameAs targets.
2.4 applicationCategory Values
Use the Schema.org enumerated values: BusinessApplication (CRM, invoicing, accounting), CommunicationApplication, DesignApplication, DeveloperApplication, EducationalApplication, EntertainmentApplication, FinanceApplication, GameApplication, HealthApplication, LifestyleApplication, MultimediaApplication, ProductivityApplication, SecurityApplication, ShoppingApplication, SocialNetworkingApplication, SportsApplication, TravelApplication, UtilitiesApplication, WebApplication (fallback).
applicationSubCategory is a free text refinement. For a CRM, set applicationCategory: "BusinessApplication" and applicationSubCategory: "CRM".
2.5 Pitfalls
Mixing SoftwareApplication with Product for the same offering (pick SoftwareApplication, more specific). Stale softwareVersion. AggregateRating without an actual review corpus (manual action territory). Omitting operatingSystem (rich result does not render). Listing downloadUrl for SaaS products that have no download (use url).
3. Course and EducationalOccupationalProgram
3.1 The Course Type
Course covers individual courses (a multi week class, an online lesson series, a bootcamp module). The Schema.org Pending Course Info release rolled out March 2025 added courseInstance cardinality, educationalLevel clarity, and the coursePrerequisites text or Course reference duality.
{
"@type": "Course",
"@id": "https://example.com/courses/intro-to-seo/#course",
"name": "Introduction to Technical SEO",
"description": "An eight week introduction to technical SEO for developers.",
"provider": { "@id": "https://example.com/#organization" },
"educationalLevel": "Beginner",
"courseCode": "SEO101",
"coursePrerequisites": "Basic HTML and CSS familiarity",
"occupationalCredentialAwarded": "Technical SEO Foundations Certificate",
"teaches": ["JSON-LD", "Core Web Vitals", "Server log analysis"],
"hasCourseInstance": [{
"@type": "CourseInstance",
"courseMode": "Online",
"courseWorkload": "PT8H",
"instructor": { "@id": "https://example.com/#founder" },
"startDate": "2026-06-01",
"endDate": "2026-07-27",
"location": {
"@type": "VirtualLocation",
"url": "https://example.com/courses/intro-to-seo/live/"
},
"offers": {
"@type": "Offer", "price": "297.00", "priceCurrency": "USD",
"category": "Paid"
}
}]
}
3.2 The EducationalOccupationalProgram Type
EducationalOccupationalProgram covers degree programs, multi course tracks, or career oriented learning paths that span multiple Course instances. The type is the right anchor for college degrees, vocational programs, professional certifications composed of multiple courses, and apprenticeships.
{
"@type": "EducationalOccupationalProgram",
"@id": "https://example.com/programs/data-science-mba/#program",
"name": "Data Science MBA",
"provider": { "@id": "https://example.com/#organization" },
"programType": "Master's degree",
"educationalProgramMode": "Hybrid",
"timeToComplete": "P2Y",
"salaryUponCompletion": {
"@type": "MonetaryAmountDistribution",
"currency": "USD",
"duration": "P1Y",
"median": 134000
},
"occupationalCategory": "15-1199.04",
"applicationDeadline": "2026-08-15",
"startDate": "2026-09-01",
"endDate": "2028-06-30",
"hasCourse": [
{ "@id": "https://example.com/courses/statistics/#course" },
{ "@id": "https://example.com/courses/finance/#course" }
],
"occupationalCredentialAwarded": "MBA"
}
3.3 LearningResource for Individual Lessons
LearningResource covers individual lessons, video tutorials, worksheets, quizzes, and assignments. Use it inside Course.hasPart when a course breaks into resource units. Properties: name, learningResourceType ("Lesson", "Quiz", "Video", "Assignment"), educationalLevel, teaches, timeRequired (ISO 8601), isPartOf (parent Course @id), author.
3.4 SEO Benefits for Education Marketing
Google added the Course rich result in 2018 and expanded it with the Pending Course Info release in March 2025. Drivers: name, description, provider, offers.price, offers.priceCurrency, aggregateRating, hasCourseInstance.startDate, hasCourseInstance.courseMode. In 2026 the Course carousel appears for queries like "online marketing course" and "data science bootcamp" approximately 41 percent of the time (SimilarWeb education sampling, January 2026, 8,400 keywords).
3.5 educationalLevel Values
Use the Schema.org pending vocabulary values: Beginner, Intermediate, Advanced, Expert, Children (K-6), Teen (7-12), College, Graduate, Professional. For specific grade level mapping use educationalAlignment.AlignmentObject with educationalFramework: "United States Common Core".
4. Event and EventAttendanceMode
4.1 The Event Type
Event covers concerts, conferences, workshops, webinars, classes, meetups, festivals, sports games, and live broadcasts. The type drives the Events SERP feature, a horizontal carousel that appears for "events near me" and "things to do" queries.
{
"@type": "Event",
"@id": "https://example.com/events/seo-summit-2026/#event",
"name": "SEO Summit 2026",
"startDate": "2026-09-15T09:00:00-05:00",
"endDate": "2026-09-16T17:00:00-05:00",
"eventAttendanceMode": "https://schema.org/MixedEventAttendanceMode",
"eventStatus": "https://schema.org/EventScheduled",
"location": [
{
"@type": "Place",
"name": "Convention Center",
"address": {
"@type": "PostalAddress",
"streetAddress": "200 Convention Way",
"addressLocality": "Bentonville",
"addressRegion": "AR",
"postalCode": "72712",
"addressCountry": "US"
}
},
{
"@type": "VirtualLocation",
"url": "https://example.com/events/seo-summit-2026/livestream/"
}
],
"image": "https://example.com/events/seo-summit/hero-16x9.jpg",
"organizer": { "@id": "https://example.com/#organization" },
"offers": {
"@type": "Offer",
"url": "https://example.com/events/seo-summit-2026/register/",
"price": "497.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
}
4.2 The eventAttendanceMode Property
eventAttendanceMode declares how attendees experience the event with three Schema.org values: OfflineEventAttendanceMode (in person only), OnlineEventAttendanceMode (online only), or MixedEventAttendanceMode (hybrid). For online only events, location must use VirtualLocation with a url pointing to the join page or livestream. For mixed mode, location is an array containing both a Place and a VirtualLocation. Google requires this for the event rich result to render properly in 2026.
4.3 The eventStatus Property
eventStatus declares the current state. Values: EventScheduled (default), EventRescheduled (add previousStartDate), EventPostponed (delayed without new date), EventCancelled (keep schema live at least 30 days post date), and EventMovedOnline (update location to VirtualLocation and eventAttendanceMode to OnlineEventAttendanceMode). Google introduced EventMovedOnline and EventCancelled rich result treatment in March 2020 and retained both as permanent features. The cancellation badge appears on the SERP, which is critical for trust when an event is called off.
4.4 SEO Benefits in the Events SERP Feature
The Events SERP feature appears for approximately 38 percent of event queries in 2026 (SparkToro events sampling, March 2026, 12,300 keywords). Eligibility requires name, startDate, location, image, plus an Offer.url to a real registration page. The feature drives 27 percent higher CTR than position 1 organic on event queries (Mangools study, February 2026, 4,200 queries).
4.5 Recurring Events
For recurring events, use EventSeries as the parent with a subEvent array of individual Event nodes. Each subEvent needs its own unique startDate, endDate, eventAttendanceMode, and location. Do not collapse a series into a single Event with multiple dates; Google parses each subEvent as a separate SERP entry.
4.6 Pitfalls
Missing timezone on startDate and endDate (use full ISO 8601 with offset). Setting eventAttendanceMode to MixedEventAttendanceMode but providing only one location (Google flags as invalid). Reusing the same @id across recurring instances. Forgetting to update eventStatus when cancelling. Offer.url pointing to the event page instead of the registration page.
5. JobPosting
5.1 The JobPosting Type
JobPosting covers individual job openings. The type drives Google for Jobs, the dedicated job search vertical that appears at the top of the SERP for employment queries. Google for Jobs has become the dominant job aggregator in the United States since 2019, displacing portion of LinkedIn and Indeed organic visibility for queries that match its eligibility criteria.
{
"@type": "JobPosting",
"@id": "https://example.com/careers/seo-strategist/#job",
"title": "Senior SEO Strategist",
"description": "<p>We are hiring a Senior SEO Strategist...</p>",
"datePosted": "2026-05-01",
"validThrough": "2026-08-01T23:59:59-05:00",
"employmentType": "FULL_TIME",
"hiringOrganization": { "@id": "https://example.com/#organization" },
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"addressLocality": "Bentonville",
"addressRegion": "AR",
"postalCode": "72712",
"addressCountry": "US"
}
},
"applicantLocationRequirements": {
"@type": "Country", "name": "United States"
},
"jobLocationType": "TELECOMMUTE",
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "USD",
"value": {
"@type": "QuantitativeValue",
"minValue": 85000,
"maxValue": 110000,
"unitText": "YEAR"
}
},
"jobBenefits": "Health, dental, vision, 401k match, unlimited PTO",
"industry": "Marketing Services",
"occupationalCategory": "13-1161.00",
"directApply": true
}
5.2 Google for Jobs Eligibility Requirements
Google for Jobs has strict eligibility; pages that fail any criterion are excluded regardless of organic ranking. Criteria as of January 2026: one JobPosting per URL (listing pages with multiple jobs are ineligible); title, description, datePosted, hiringOrganization, and jobLocation required; description must be HTML, at least 50 characters, and match the visible page; validThrough should be set (Google assumes 60 days from datePosted otherwise); the page must be crawlable, indexable, and not behind a login wall; robots.txt must not block the URL; the page must include the same details visible to users; salary disclosure required for jobs in California, Colorado, Connecticut, Hawaii, Illinois, Maryland, New York, Washington and other pay transparency jurisdictions (Google requires baseSalary or the listing fails compliance review since November 2023).
5.3 Indexability and Expired Postings
When a job is filled or closed: remove the page and serve 404 or 410 (Google removes the listing within 48 hours); keep the page but set validThrough in the past (Google removes the rich result while preserving the URL for backlink value); or 301 redirect to a related open job. Do not leave filled jobs live with future validThrough dates; Google issues structured data warnings and may demote the entire careers section.
5.4 applicantLocationRequirements
For remote jobs, applicantLocationRequirements declares which jurisdictions can apply. Combined with jobLocationType: "TELECOMMUTE", this opens the job to Google's remote work filter. Use Country, State, or AdministrativeArea as the @type. Pass an array for multi jurisdiction eligibility (Country: United States, Canada). For state restricted jobs, use State with the state name.
5.5 directApply
The directApply boolean tells Google whether the apply link is a direct application form (true) or redirects to an intermediate platform (false). Direct apply jobs receive preferred placement in Google for Jobs since the April 2024 update.
5.6 Pitfalls
- Listing pages with multiple jobs. One job per URL, always.
- Missing
validThrough. Google's 60 day default leads to ghost listings. descriptionshorter than 50 characters. Ineligible.- Salary as a string. Use
MonetaryAmountwithQuantitativeValue. employmentTypeoutside enum:FULL_TIME,PART_TIME,CONTRACTOR,TEMPORARY,INTERN,VOLUNTEER,PER_DIEM,OTHER.- Forgetting to remove or update filled jobs. Google penalizes the publisher in Google for Jobs.
6. Recipe and HowTo
6.1 The Recipe Type
Recipe covers food and beverage preparation. The type drives the Recipe rich result, the photo plus rating plus time card that dominates cooking queries.
{
"@type": "Recipe",
"@id": "https://example.com/recipes/sourdough/#recipe",
"name": "Classic Sourdough Bread",
"image": ["https://example.com/recipes/sourdough/16x9.jpg"],
"author": { "@id": "https://example.com/#founder" },
"datePublished": "2026-04-10",
"prepTime": "PT45M", "cookTime": "PT45M", "totalTime": "PT25H",
"recipeYield": "1 loaf (10 slices)",
"recipeCategory": "Bread", "recipeCuisine": "American",
"nutrition": {
"@type": "NutritionInformation",
"servingSize": "1 slice", "calories": "180 kcal",
"carbohydrateContent": "35 g", "proteinContent": "6 g"
},
"recipeIngredient": [
"500 g bread flour", "350 g water",
"100 g active sourdough starter", "10 g salt"
],
"recipeInstructions": [
{ "@type": "HowToStep", "name": "Mix", "text": "Combine flour, water, starter. Rest 30 min." },
{ "@type": "HowToStep", "name": "Bulk ferment", "text": "75F, 4 hours, S&F every 30 min." },
{ "@type": "HowToStep", "name": "Cold ferment", "text": "Shape, banneton, fridge 20 hours." },
{ "@type": "HowToStep", "name": "Bake", "text": "500F Dutch oven, 20 covered, 25 uncovered." }
],
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.8", "ratingCount": "412" }
}
6.2 The HowTo Type and Its Deprecation History
HowTo covers instructional content. Google deprecated HowTo rich results in August 2023, removing them from desktop and mobile SERPs. The type remains valid Schema.org markup and AI engines continue to extract from it; the SERP card is gone. 2026 status: Google still parses HowTo for AI Overview citation context and Knowledge Graph. Sites with HowTo schema continue to receive citation lift in AI Overviews and AI Mode without the classic rich result. Cross reference framework-aioverviews.md.
{
"@type": "HowTo",
"@id": "https://example.com/how-to/install-jsonld/#howto",
"name": "How to Install JSON-LD Schema",
"totalTime": "PT20M",
"estimatedCost": { "@type": "MonetaryAmount", "currency": "USD", "value": "0" },
"supply": [{ "@type": "HowToSupply", "name": "A text editor" }],
"tool": [{ "@type": "HowToTool", "name": "Schema.org Validator" }],
"step": [
{ "@type": "HowToStep", "name": "Open template", "text": "Locate the HTML file." },
{ "@type": "HowToStep", "name": "Add script tag", "text": "Inside head, add the JSON-LD script." }
]
}
6.3 nutritionInformation, cookTime, and recipeYield
The Recipe rich result requires three time properties: prepTime (mixing, chopping, marinating), cookTime (active cooking), totalTime (wall clock from start to finish; must equal or exceed prep plus cook). All durations are ISO 8601: PT30M is 30 minutes, PT1H30M is 1 hour 30 minutes, PT25H is 25 hours.
recipeYield accepts a string ("4 servings", "1 loaf") or a QuantitativeValue; Google prefers the string form for SERP display. nutrition is a NutritionInformation object with servingSize, calories, and nutrient content fields. AI engines extract nutrition data when answering "calories in [dish]" queries, and the structured form is significantly more reliable than parsing prose.
6.4 Pitfalls
Recipe schema without an image (ineligible). recipeInstructions as a single string instead of HowToStep array (functional but loses step level salience). aggregateRating without an actual review corpus (manual action). Missing recipeYield (Google often hides the rich result without it). HowTo schema expecting a SERP rich result (gone since August 2023; schema still valuable for AI citation).
7. FAQPage and QAPage
7.1 FAQPage vs QAPage
FAQPage and QAPage are commonly confused. FAQPage is editorially curated Q&A on a single topic where the publisher controls both question and answer; QAPage is community Q&A where users ask and answer (Stack Overflow, Quora, support forums). The August 2023 Google update restricted FAQPage rich results to "well known, authoritative government and health websites," with the rich result eligible for other sites at Google's discretion. Most commercial sites no longer earn the FAQ accordion in the SERP. The schema itself remains valid and AI engines continue to extract from FAQPage for citation context.
7.2 FAQPage Implementation
{
"@type": "FAQPage",
"@id": "https://example.com/faq/#faqpage",
"mainEntity": [
{
"@type": "Question",
"name": "What is JSON-LD?",
"acceptedAnswer": {
"@type": "Answer",
"text": "JSON-LD is a JSON based serialization format for Linked Data. It is the format Google recommends for Schema.org markup."
}
},
{
"@type": "Question",
"name": "Why does my schema fail validation?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The most common cause is malformed JSON: missing commas, unbalanced quotes, or an invalid context."
}
}
]
}
7.3 QAPage Implementation
For community Q&A platforms, QAPage with a single mainEntity Question that has multiple suggestedAnswer entries:
{
"@type": "QAPage",
"@id": "https://example.com/community/q/12345/#qapage",
"mainEntity": {
"@type": "Question",
"name": "How do I implement JSON-LD on a Next.js site?",
"text": "I'm building a Next.js 14 app router site. What is the right pattern?",
"answerCount": 3,
"upvoteCount": 24,
"dateCreated": "2026-05-01T14:30:00-05:00",
"author": { "@type": "Person", "name": "Asker Name" },
"acceptedAnswer": {
"@type": "Answer",
"text": "Use the Metadata API in app router with generateMetadata.",
"upvoteCount": 18,
"dateCreated": "2026-05-01T15:45:00-05:00",
"url": "https://example.com/community/q/12345/#answer-7890",
"author": { "@id": "https://example.com/#founder" }
},
"suggestedAnswer": [{
"@type": "Answer",
"text": "Or render the script tag directly via dangerouslySetInnerHTML.",
"upvoteCount": 4,
"dateCreated": "2026-05-01T16:30:00-05:00",
"author": { "@type": "Person", "name": "Another Answerer" }
}]
}
}
7.4 The AI Overview Citation Premium for FAQ Content
While the SERP rich result for FAQPage is restricted since 2023, AI Overview citation rates for FAQ schema pages run significantly higher than for non FAQ pages on the same query intent. Profound study, January 2026, 4,200 AI Overview citations: pages with FAQPage schema earned 31 percent of citations on question style queries versus 18 percent for the same content without FAQPage schema. The schema provides the atomic Q and A pairs AI engines extract directly. Cross reference framework-aicitations.md and framework-aioverviews.md.
7.5 SpeakableSpecification Overlay for Voice Search
For voice optimized FAQ pages, layer SpeakableSpecification over Question and Answer entities to flag content suitable for voice extraction. Add speakable as a sibling to mainEntity with cssSelector listing the DOM selectors that wrap question and answer text. Cross reference framework-voicesearch.md.
7.6 Pitfalls
FAQPage schema on pages that aren't FAQs (product, service, homepage) is schema spam. Questions copy pasted from competitors violate Helpful Content guidelines. Answers padded with marketing language are discounted by AI engines. Identical FAQ blocks across many pages: use one canonical FAQ page and link. QAPage on editorially curated content is a mismatched type.
8. Review and AggregateRating
8.1 Review Type
Review covers individual reviews authored by a single reviewer. The type can be used standalone or nested inside a Product, Service, LocalBusiness, Book, Movie, Restaurant, or other reviewable entity.
{
"@type": "Review",
"@id": "https://example.com/reviews/myapp-pro/#review-12345",
"itemReviewed": {
"@type": "SoftwareApplication",
"name": "MyApp Pro",
"@id": "https://example.com/products/myapp-pro/#software"
},
"reviewRating": {
"@type": "Rating", "ratingValue": "4", "bestRating": "5", "worstRating": "1"
},
"author": { "@type": "Person", "name": "Reviewer Name" },
"datePublished": "2026-04-20",
"reviewBody": "MyApp Pro handles invoicing well but the export to QuickBooks is buggy on Windows.",
"publisher": { "@id": "https://example.com/#organization" }
}
8.2 AggregateRating for the Rolled Up Score
AggregateRating represents the average rating across a corpus of reviews. Always paired with reviewCount or ratingCount. The two fields are not interchangeable:
reviewCountis the count of textual reviews. Use when reviews include written content.ratingCountis the count of ratings (stars only). Use for star only collection systems.
{
"@type": "AggregateRating",
"ratingValue": "4.6",
"reviewCount": "248",
"bestRating": "5",
"worstRating": "1"
}
When attached to a Product, Service, or LocalBusiness, AggregateRating drives the star snippet in the SERP and the rating block in Google AI Overviews.
8.3 The Google Policy Changes Since 2023
Google's September 2023 review snippet policy update tightened self review eligibility. Self review schema (Organization, Service, LocalBusiness reviewing themselves) no longer earns the SERP star snippet. The schema continues to validate; the rich result no longer renders.
Still eligible: Product reviews on the product detail page; Book, Movie, MusicAlbum, MusicRecording reviews on their detail pages; Recipe reviews on the recipe page; Software reviews on the software detail page; reviews of named third party entities.
No longer eligible: a business reviewing itself, a service reviewing itself, a local business reviewing itself. AI engines continue to extract aggregateRating for citation context regardless; the change is purely SERP rich result eligibility.
8.4 Third Party Review Platforms
For service and local businesses that need star snippets, the path runs through third party review platforms whose schema is allowed in the SERP. Google's accepted third party review aggregators in 2026 include Google Business Profile (most authoritative; native to Google), Yelp, Trustpilot, Better Business Bureau, Houzz (home services), Avvo (legal), Healthgrades (medical), TripAdvisor (hospitality), Capterra, and G2 (software). These platforms render their own star ratings in the SERP. The site owner does not need to publish self review schema; the platforms publish review schema on their own pages, and Google aggregates.
8.5 User Generated Content Schema Patterns
For sites that accept user reviews and want to publish them with proper Review schema, nest a review array inside the Product (or Service, LocalBusiness, etc.) alongside aggregateRating. Each Review carries its own reviewRating, author, datePublished, and reviewBody. Display only the most recent or highest rated reviews in schema (Google recommends 3 to 10). All declared reviews must be visible on the page; schema only reviews are spam policy violations.
8.6 Pitfalls
Fabricated aggregateRating is manual action territory. Self reviewing (Organization or Service with aggregateRating) validates, but the rich result no longer renders since September 2023. AggregateRating without reviewCount or ratingCount is rejected. Star rating outside 1 to 5 without bestRating and worstRating declared causes parse errors. Reviews in schema but not visible on the page violate spam policy.
9. Product and ProductGroup
9.1 Product Type
Product covers individual sellable items. The type drives Product rich results, the price plus rating plus availability card that appears for shopping queries.
{
"@type": "Product",
"@id": "https://example.com/products/widget/#product",
"name": "Widget Pro",
"image": ["https://example.com/products/widget/16x9.jpg"],
"sku": "WID-PRO-001",
"mpn": "WID-PRO-001",
"gtin13": "1234567890123",
"brand": { "@type": "Brand", "name": "Acme Industrial" },
"manufacturer": { "@id": "https://example.com/#organization" },
"category": "Industrial Hardware > Widgets",
"color": "Silver",
"material": "Stainless Steel",
"offers": {
"@type": "Offer",
"@id": "https://example.com/products/widget/#offer",
"url": "https://example.com/products/widget/",
"price": "49.99",
"priceCurrency": "USD",
"priceValidUntil": "2026-12-31",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition",
"seller": { "@id": "https://example.com/#organization" },
"shippingDetails": { "@id": "https://example.com/#shipping-policy" },
"hasMerchantReturnPolicy": { "@id": "https://example.com/#return-policy" }
},
"aggregateRating": {
"@type": "AggregateRating", "ratingValue": "4.7", "reviewCount": "184"
}
}
9.2 ProductGroup for Variant Collections
ProductGroup covers a parent product with multiple variants (sizes, colors, configurations). Each variant is a Product linked via hasVariant.
{
"@type": "ProductGroup",
"@id": "https://example.com/products/tshirt/#group",
"name": "Classic T Shirt",
"url": "https://example.com/products/tshirt/",
"brand": { "@type": "Brand", "name": "Acme Apparel" },
"productGroupID": "TSHIRT-CLASSIC",
"variesBy": ["color", "size"],
"hasVariant": [
{
"@type": "Product",
"@id": "https://example.com/products/tshirt-black-m/#product",
"name": "Classic T Shirt, Black, Medium",
"sku": "TS-BLK-M", "color": "Black", "size": "Medium",
"offers": {
"@type": "Offer", "price": "24.99", "priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
}
]
}
Each additional variant is another Product in the hasVariant array with its own @id, SKU, color/size, and offers.
9.3 productID, gtin, mpn, brand
Identifiers are critical for Merchant Listings and AI shopping citation. Google's hierarchy of preference in 2026: gtin13 (EAN, international), gtin12 (UPC, North America), gtin14 (wholesale), gtin8 (small packages), mpn (manufacturer part number, industrial/B2B), sku (internal stock keeping unit), productID (generic fallback). Google rejects Merchant Listings without at least one of gtin13, gtin12, gtin14, gtin8, or mpn plus brand for products that have manufacturer codes. Custom or handmade products are exempt; declare productID and brand instead.
9.4 shippingDetails (New in 2024)
OfferShippingDetails declares shipping cost, time, and destinations. The property became eligible for Merchant Listings rich results in January 2024. Sites without it are excluded from the free Merchant Listings program.
{
"@type": "OfferShippingDetails",
"@id": "https://example.com/#shipping-policy",
"shippingRate": { "@type": "MonetaryAmount", "value": "5.99", "currency": "USD" },
"shippingDestination": [{ "@type": "DefinedRegion", "addressCountry": "US" }],
"deliveryTime": {
"@type": "ShippingDeliveryTime",
"handlingTime": { "@type": "QuantitativeValue", "minValue": 0, "maxValue": 1, "unitCode": "DAY" },
"transitTime": { "@type": "QuantitativeValue", "minValue": 2, "maxValue": 5, "unitCode": "DAY" }
}
}
9.5 hasMerchantReturnPolicy (New in 2024)
MerchantReturnPolicy declares the return policy. Like shippingDetails, eligibility for Merchant Listings since January 2024.
{
"@type": "MerchantReturnPolicy",
"@id": "https://example.com/#return-policy",
"applicableCountry": "US",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
9.6 SEO Benefits
The full Product schema stack (Product plus Offer plus AggregateRating plus shippingDetails plus hasMerchantReturnPolicy) is the baseline for Merchant Listings, Shopping graph, and AI shopping citation. Pages with the full stack earn approximately 3.2 times the AI shopping citation rate of pages with Product schema alone (Schema App ecommerce study, October 2025, 6,800 product pages). Cross reference framework-ecommerceseo.md for variant strategy, Merchant Center feed, and Shopping graph optimization.
9.7 Pitfalls
Product without Offer is ineligible for the rich result. Offer.availability as a string ("in stock") instead of the schema enum (https://schema.org/InStock). Missing priceCurrency. priceValidUntil in the past is treated as expired. Variants modeled as separate Product pages without ProductGroup linkage lose the variant rich result. Missing gtin or mpn for products with manufacturer codes excludes from Merchant Listings. Missing shippingDetails or hasMerchantReturnPolicy excludes from Merchant Listings since 2024.
10. LocalBusiness Subtypes
10.1 The LocalBusiness Hierarchy
LocalBusiness is the base type for physical businesses with a service location. Schema.org defines a deep subclass hierarchy. Always declare the most specific subtype available. AI engines weight subtype specificity heavily; a Plumber resolves more confidently than a LocalBusiness. Major branches in 2026:
- AutomotiveBusiness: AutoBodyShop, AutoDealer, AutoPartsStore, AutoRental, AutoRepair, AutoWash, GasStation, MotorcycleDealer, MotorcycleRepair.
- FinancialService: AccountingService, AutomatedTeller, BankOrCreditUnion, InsuranceAgency.
- FoodEstablishment: Bakery, BarOrPub, Brewery, CafeOrCoffeeShop, Distillery, FastFoodRestaurant, IceCreamShop, Restaurant, Winery.
- HealthAndBeautyBusiness: BeautySalon, DaySpa, HairSalon, HealthClub, NailSalon, TattooParlor.
- HomeAndConstructionBusiness: Electrician, GeneralContractor, HVACBusiness, HousePainter, Locksmith, MovingCompany, Plumber, RoofingContractor.
- LegalService: Attorney, Notary.
- LodgingBusiness: BedAndBreakfast, Campground, Hostel, Hotel, Motel, Resort, SkiResort, VacationRental.
- MedicalBusiness: Dentist, Optician, Physician, Pharmacy.
- EntertainmentBusiness: AmusementPark, ArtGallery, Casino, ComedyClub, MovieTheater, NightClub.
- EmergencyService: FireStation, Hospital, PoliceStation.
- Store subclasses cover most retail (BookStore, ClothingStore, ElectronicsStore, Florist, FurnitureStore, GroceryStore, HardwareStore, JewelryStore, PetStore, ShoeStore, ToyStore, etc.).
- Other: AnimalShelter, ChildCare, DryCleaningOrLaundry, EmploymentAgency, GovernmentOffice, Library, RadioStation, RealEstateAgent, SelfStorage, ShoppingCenter, SportsActivityLocation, TravelAgency.
- Deprecated:
ProfessionalService(use a specific subclass).
For Joseph's portfolio, common selections: Heritage Hardwood Floors uses HomeAndConstructionBusiness plus GeneralContractor; Handled Tax uses AccountingService; Dusty's Auto and Mobile uses AutoRepair; NWA POOlice (pet waste) uses generic LocalBusiness with additionalType; Eureka Bath Works uses Store plus HealthAndBeautyBusiness; TCB Fight Factory uses SportsActivityLocation; White River Cabins uses LodgingBusiness plus VacationRental or Resort.
10.2 Subtype Specific Properties
Many subtypes carry properties only available on that subtype. Restaurant adds acceptsReservations, hasMenu, servesCuisine, starRating. Hotel adds amenityFeature, checkinTime, checkoutTime, starRating. Plumber adds priceRange and serviceArea.
{
"@type": "Restaurant",
"@id": "https://example.com/#restaurant",
"name": "The Boulder",
"servesCuisine": ["American", "Barbecue"],
"priceRange": "$$",
"acceptsReservations": true,
"hasMenu": "https://example.com/menu/",
"starRating": { "@type": "Rating", "ratingValue": "4", "bestRating": "5" },
"openingHoursSpecification": [{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday"],
"opens": "11:00", "closes": "21:00"
}]
}
Add additional OpeningHoursSpecification entries to the array for different daily windows (Fri-Sat late hours, Sunday brunch hours, etc.).
10.3 OpeningHoursSpecification
For LocalBusiness types, openingHoursSpecification is the canonical pattern. The older openingHours text format ("Mo Th 11:00 21:00") is deprecated; use the structured form. For holidays and seasonal hours, use SpecialOpeningHoursSpecification with opens and closes set to "00:00" (closed) plus validFrom and validThrough for the date range and a description like "Closed Christmas Day."
10.4 geo and hasMap
Every LocalBusiness should declare geo with GeoCoordinates (latitude, longitude) and hasMap pointing to the canonical map source (the Google Business Profile CID URL when claimed, in the form https://www.google.com/maps/place/?cid=[CID]). Cross reference framework-localseo.md for the full LocalBusiness schema depth, GBP integration, NAP consistency, and service area declaration.
10.5 Pitfalls
Generic LocalBusiness when a specific subtype is available loses specificity. Deprecated openingHours string format (use OpeningHoursSpecification). geo coordinates pointing to the city centroid instead of the actual address. hasMap pointing to a generic Google search URL instead of the GBP CID URL. Missing priceRange. Subtypes outside the LocalBusiness hierarchy when a proper subclass exists.
11. Person and ProfilePage
11.1 The Person Type
Person covers individual humans. The type is critical for E-E-A-T author signals, founder declarations, executive bios, and the Knowledge Graph Person entity claim.
{
"@type": "Person",
"@id": "https://example.com/#founder",
"name": "Joseph W. Anady",
"givenName": "Joseph",
"familyName": "Anady",
"honorificSuffix": "SDVOSB",
"jobTitle": "Founder",
"worksFor": { "@id": "https://example.com/#organization" },
"alumniOf": { "@type": "Organization", "name": "U.S. Army" },
"knowsAbout": [
"Technical SEO", "Schema markup", "AI search optimization",
"SDVOSB digital marketing", "Local SEO"
],
"knowsLanguage": ["English"],
"image": "https://example.com/joseph.jpg",
"url": "https://example.com/about/",
"address": {
"@type": "PostalAddress",
"addressLocality": "Cassville", "addressRegion": "MO", "addressCountry": "US"
},
"sameAs": [
"https://www.wikidata.org/wiki/Q139592630",
"https://www.linkedin.com/in/josephanady",
"https://github.com/josephanady"
]
}
11.2 The ProfilePage Type
ProfilePage is a WebPage subtype for profile style pages. The type was upgraded in Google's 2024 social media profile rich result update, which added the dedicated ProfilePage treatment for social media author profiles.
{
"@type": "ProfilePage",
"@id": "https://example.com/team/joseph-anady/#profilepage",
"url": "https://example.com/team/joseph-anady/",
"name": "Joseph W. Anady",
"dateCreated": "2017-01-01T00:00:00-06:00",
"dateModified": "2026-05-10T14:00:00-05:00",
"isPartOf": { "@id": "https://example.com/#website" },
"mainEntity": { "@id": "https://example.com/#founder" },
"primaryImageOfPage": {
"@type": "ImageObject",
"url": "https://example.com/team/joseph-anady-hero.jpg"
}
}
The mainEntity reference is what binds the ProfilePage to the Person entity. Google's 2024 update specifically rewards this pattern with the social profile rich result treatment.
11.3 ProfilePage Adoption in 2024 and 2025
Google announced ProfilePage support in May 2024 targeting social media platforms and creator economy sites. The type rolled out to dating sites, networking platforms, and team pages by Q3 2024. By 2025 the pattern extended to author bio pages on publishers, About pages with founder bios, and team member detail pages on agency sites. The 2026 status: Google rewards ProfilePage with three SERP features. First, the social profile card on people search queries. Second, the author detail panel beneath byline links on news content. Third, expanded author attribution within AI Overviews when the cited content has a ProfilePage author with credentials.
11.4 dateCreated and dateModified for Profile Freshness
Google's freshness signal for ProfilePage runs on dateCreated and dateModified. dateCreated is the date the profile was first published; dateModified is the date of the most recent meaningful update (new credentials, new role, new accomplishments). Trivial updates (typo fixes, link swaps) should not trigger dateModified changes. The 90 day staleness threshold applies: profiles with dateModified older than 90 days lose the ProfilePage rich result treatment in 2026. Set a quarterly review cadence.
11.5 InteractionCounter for Engagement
The ProfilePage rich result expands when the Person entity carries interactionStatistic arrays. Each InteractionCounter carries interactionType (a Schema.org Action URL like FollowAction, WriteAction, LikeAction) and userInteractionCount. agentInteractionStatistic captures actions taken by the person; interactionStatistic captures actions taken on the person's content. Cross reference framework-eeat.md.
11.6 Pitfalls
Person schema without sameAs is meaningful but does not resolve. ProfilePage without mainEntity loses the binding to the Person entity. Stale dateModified triggers staleness demotion. knowsAbout as marketing language ("the best SEO expert") instead of topic tokens ("Technical SEO", "Schema markup"). Multiple Person entities for the same human with different @id URIs across pages.
12. ClaimReview and FactCheck
12.1 The ClaimReview Type
ClaimReview covers fact checking content where a publisher evaluates a specific claim and assigns a truth rating. The type drives Google's Fact Check label, the dedicated SERP treatment that prepends the publisher's verdict.
{
"@type": "ClaimReview",
"@id": "https://example.com/fact-checks/example/#claimreview",
"url": "https://example.com/fact-checks/example/",
"datePublished": "2026-05-08",
"author": { "@id": "https://example.com/#organization" },
"claimReviewed": "The earth is flat.",
"itemReviewed": {
"@type": "Claim",
"datePublished": "2026-05-01",
"appearance": "https://socialmedia.example/post/12345",
"author": { "@type": "Person", "name": "Original Claimant" }
},
"reviewRating": {
"@type": "Rating",
"ratingValue": "1",
"bestRating": "5", "worstRating": "1",
"alternateName": "False"
}
}
12.2 The IFCN Verified Fact Checker Requirement
Google's Fact Check label requires publishers to be verified signatories of the International Fact Checking Network (IFCN) code of principles. Non IFCN publishers can implement ClaimReview schema (it validates), but the SERP Fact Check label does not render. As of January 2026, approximately 170 publishers worldwide are verified IFCN signatories, including PolitiFact, FactCheck.org, Snopes (relisted 2024), AFP Fact Check, AP Fact Check, Reuters Fact Check, BBC Reality Check, Lead Stories, and Full Fact. For non IFCN publishers, ClaimReview still has value: AI engines parse it for trust scoring, and the structured form helps with topic salience.
12.3 SEO Benefits in Google's Fact Check Label
The Fact Check label appears as a prepended tag on the SERP result. For IFCN verified publishers, the label drives approximately 18 percent higher CTR on fact check content versus the same content without the label (FactCheck.org analytics shared at IFCN GlobalFact 2025, 14,200 query sample). The label also appears on Google News and in Google's Knowledge Panel "Fact Check" tab.
12.4 The Role in AI Engine Trust Scoring
AI engines weight ClaimReview content heavily when assessing topic trustworthiness. Pages cited by IFCN verified fact checkers receive a citation penalty in Perplexity, Bing Copilot, and Google AI Overviews when the ClaimReview marks the source as "False" or "Misleading." Pages cited as "True" or "Mostly True" receive a trust bonus. The mechanism: AI engines crawl ClaimReview content, extract claim and verdict, add the source URL to a trust ledger keyed by the claim, and adjust citation weights accordingly. Cross reference framework-trustsignals.md and framework-newsseo.md.
12.5 Patterns for News Publishers
News publishers running fact checking desks should layer ClaimReview over NewsArticle inside a single @graph. The NewsArticle carries headline, datePublished, author, publisher. The ClaimReview sits alongside with isPartOf referencing the NewsArticle @id, claimReviewed as the verbatim claim text, reviewRating with the verdict, and itemReviewed as a Claim with an appearance URL pointing to the original source.
12.6 reviewRating.alternateName Conventions
Google's accepted truth rating strings map ratingValue 1 to "False," 2 to "Mostly False," 3 to "Mixture" or "Half True," 4 to "Mostly True," and 5 to "True." Use the alternateName that matches your publication's rating scale. PolitiFact uses "Pants on Fire" (custom value for ratingValue 0); Snopes uses "Outdated" and "Legend" for non binary verdicts.
12.7 Pitfalls
ClaimReview without itemReviewed is ineligible. itemReviewed.appearance pointing to a deleted social post (update or remove). Implementing ClaimReview without IFCN verification and expecting the SERP label (schema validates, label does not render). ClaimReview on opinion content that is not actually fact checking is schema spam. Outdated fact checks: set a review cadence.
13. CreativeWork Subtypes
13.1 The CreativeWork Hierarchy
CreativeWork is the broadest type covering any created work. Major descendants in 2026:
- Article family: Article, NewsArticle (AnalysisNewsArticle, BackgroundNewsArticle, OpinionNewsArticle, ReportageNewsArticle, ReviewNewsArticle), BlogPosting, LiveBlogPosting, ScholarlyArticle, MedicalScholarlyArticle, TechArticle, SatiricalArticle.
- Book and publishing: Book, Audiobook, Chapter, Manuscript, ShortStory, Thesis, ComicStory, ComicIssue, ComicSeries.
- Media: VideoObject, AudioObject, ImageObject, MusicVideoObject, TextObject, DataDownload.
- Music: MusicAlbum, MusicRecording, MusicComposition, MusicPlaylist, MusicRelease.
- Movies/series: Movie, MovieSeries, TVSeries, TVSeason, TVEpisode, RadioSeries, PodcastSeries, PodcastEpisode.
- Learning: Course, LearningResource, Quiz, EducationalOccupationalCredential.
- Procedural: HowTo, Recipe, HowToStep, HowToSection, HowToTip, ExercisePlan, Diet.
- Reviews: Review, ClaimReview, CriticReview, UserReview, EmployerReview, MediaReview, Recommendation.
- Data: Dataset, DataCatalog, DefinedTermSet, CategoryCodeSet.
- Art: Painting, Sculpture, Photograph, Drawing, VisualArtwork, CoverArt, Poster.
- Software: SoftwareApplication, MobileApplication, WebApplication, VideoGame, SoftwareSourceCode.
- Other: Atlas, Map, Legislation, Guide, Quotation, Question, Answer, Menu, MenuSection, DigitalDocument, EmailMessage, SpecialAnnouncement.
13.2 The Common Article Family
Article is the safe default for editorial content (Top Stories eligible, AI citation). BlogPosting adds Discover eligibility on top of Article. NewsArticle is news reporting with timeliness imperative (Top Stories, News carousel, Google News). OpinionNewsArticle distinguishes op eds in AI Overview citations. ReportageNewsArticle is on the ground reporting (highest E-E-A-T weight in News surfaces). ScholarlyArticle is peer reviewed academic content (Google Scholar inclusion); MedicalScholarlyArticle adds Medical Knowledge Panel and YMYL credibility. TechArticle is developer focused content. BackgroundNewsArticle is explainer or context. LiveBlogPosting powers the Live rich result and breaking news carousel.
13.3 Review Subtypes
Review (generic), CriticReview (professional critic), UserReview (end user), EmployerReview (Glassdoor style), MediaReview (review of media content), Recommendation (curated), ClaimReview (fact check, Section 12).
13.4 Media Objects
VideoObject (covered in framework-videoseo.md), AudioObject (audio, voiceover, music), Audiobook (audio books), ImageObject (image metadata), MusicVideoObject, TextObject, DataDownload (downloadable dataset). Cross reference framework-videoseo.md for VideoObject implementation depth, Clip, BroadcastEvent, and video sitemap patterns.
13.5 Book, Movie, Music, SoftwareSourceCode
Book requires name, author, isbn, datePublished, publisher, inLanguage, and bookFormat (Paperback, Hardcover, EBook, AudiobookFormat). Audiobook is its own subtype. Movie requires name, director, datePublished, duration (ISO 8601), contentRating, genre; add actor, productionCompany, image, and trailer for Knowledge Graph confidence. MusicAlbum carries byArtist, datePublished, numTracks, albumProductionType (StudioAlbum, LiveAlbum, CompilationAlbum, etc.), albumReleaseType (AlbumRelease, EPRelease, SingleRelease), and a track array of MusicRecording entries with name and duration. SoftwareSourceCode carries name, codeRepository (canonical Git URL), programmingLanguage, runtimePlatform, license (OSI license URL), and author.
13.8 Dataset
Dataset covers research datasets, government open data, and data publisher releases. Required: name, description, url, license (an OSI or Creative Commons URL), creator, datePublished, keywords. Add a distribution array of DataDownload entries each with encodingFormat and contentUrl. Google's Dataset Search ingests Dataset schema and surfaces results in the dedicated dataset vertical; missing license is the most common disqualifier.
13.9 Photograph, Painting, Sculpture, Quotation
For artists, galleries, museums, and stock photography sites, use Photograph, Painting, or Sculpture with name, creator, dateCreated, contentLocation, license, and copyrightHolder. Galleries should layer ExhibitionEvent over individual works for current and upcoming shows. Quotation carries text, spokenByCharacter, and creator. Question and Answer outside QAPage context can mark inline FAQ snippets within longer articles for AI extraction.
13.10 Pitfalls
Generic CreativeWork when a specific subtype exists. Article when NewsArticle or BlogPosting is more accurate. Mixing Article and BlogPosting for the same content. Book without isbn reduces Knowledge Graph confidence. Movie without director. Dataset without license is disqualified from Dataset Search.
14. Bubbles Hosted Schema Validation
14.1 Self Hosted Validation Stack
The entire schema validation pipeline runs on bubbles (192.168.1.173, Tailscale 100.90.97.104, public 169.155.162.118) without any third party CDN or proxy. The stack: nu HTML Validator on port 8888, the structured data testing tool on port 8889, a Node runner that orchestrates validation and writes results to SQLite, a nightly cron sweep across the client roster, and a per site /validation-dashboard/ behind htpasswd.
14.2 nu HTML Validator Deployment
Download the nu validator JAR from validator.github.io. On bubbles:
sudo mkdir -p /opt/nu-validator
cd /opt/nu-validator
sudo wget https://github.com/validator/validator/releases/download/20.7.0/vnu-20.7.0.jar -O vnu.jar
sudo chown -R user:user /opt/nu-validator
Run as a systemd service on 127.0.0.1:8888 with ExecStart=/usr/bin/java -cp /opt/nu-validator/vnu.jar nu.validator.servlet.Main 8888 and Restart=on-failure. Enable and start with systemctl daemon-reload && systemctl enable nu-validator && systemctl start nu-validator. Test with:
curl -s -F "out=json" -F "content=@/var/www/sites/example.com/index.html" http://127.0.0.1:8888/ | jq .
14.3 Schema.org Validator JAR
The schema.org validator is not officially distributed as a JAR by schema.org itself; instead, use the structured data testing tool fork or a Node implementation. The simplest path is the npm package structured-data-testing-tool running as a local HTTP service on port 8889.
Install on bubbles:
cd /opt
sudo git clone https://github.com/glitchdigital/structured-data-testing-tool sdtt
cd /opt/sdtt && sudo npm install
sudo chown -R user:user /opt/sdtt
Wrap in a small Express server at /opt/sdtt/server.js that accepts POST /validate with a JSON body of { "html": "..." }, calls sdt(html, { presets: [sdt.presets.Google] }), and returns the result. Run under systemd as a non root user on 127.0.0.1:8889 with Restart=on-failure.
14.4 Google Rich Results Test API Integration
Google Rich Results Test has no public API as of 2026; the validator at search.google.com/test/rich-results is browser only. Three alternatives: headless Chromium on bubbles automating form submission and screenshot capture, the PageSpeed Insights API which includes a structured data subset, and the Schema.org Validator (above) plus the GSC Enhancements API for Google specific feedback. The headless Chromium path reuses the chromium binary from the Engine Optimization Diagnostic audit tool.
14.5 Schema Validation in CI/CD Pipeline
For sites built with a CI/CD pipeline (Next.js, Astro, Hugo, 11ty, Jekyll, Eleventy), add a validation step that runs against bubbles before deployment. A small bash script (/opt/scripts/validate-schema.sh) walks the build output directory with find ... -name '*.html', POSTs each file body to http://bubbles:8889/validate over Tailscale, parses the failed array from the JSON response with jq '.failed | length', increments a failure counter for any file with errors, and exits non zero when the counter is above zero. For Next.js the pattern slots into package.json as a postbuild script ("postbuild": "/opt/scripts/validate-schema.sh ./out").
14.6 Cron Based Daily Validation Sweep
A nightly cron entry on bubbles sweeps the full client roster, validates the homepage plus a sample of three internal pages per site, and writes results to SQLite at /var/lib/schema-validation/results.sqlite. Crontab: 0 2 * * * /opt/scripts/nightly-schema-sweep.sh >> /var/log/schema-sweep.log 2>&1.
The script reads /opt/scripts/client-roster.txt (one domain per line), iterates each site fetching /, /about/, and /contact/ with curl -sL, POSTs each HTML body to http://127.0.0.1:8889/validate, extracts error and warning counts from the response with jq, and inserts a row into the results table (columns: site, url, run_date, errors, warnings, raw_json). Schema:
sqlite3 "$DB" "CREATE TABLE IF NOT EXISTS results (
site TEXT, url TEXT, run_date TEXT, errors INTEGER, warnings INTEGER, raw_json TEXT
);"
14.7 Exposing Validation Results
For each client site that opts in, mount a read only dashboard at /validation-dashboard/ behind nginx htpasswd. The dashboard is a small static HTML page that queries the SQLite database via a FastAPI service on 127.0.0.1:9094 (modeled on the Handled Tax blog admin). nginx proxies the path behind auth_basic with /etc/nginx/.htpasswd-validation. The dashboard renders 30 days of validation results, error counts by category, and links to failing URLs.
14.8 No Third Party CDN or Proxy
The entire validation stack runs on bubbles. No third party CDN. No third party proxy. No cloud function. Output served from bubbles nginx at /var/www/sites/example.com/ directly; the IP that answers is 169.155.162.118. The pattern keeps the entire pipeline auditable and free of external dependencies.
14.9 Maintenance Cadence
Daily: cron sweep at 2 AM UTC across full client roster. Weekly: review error trend dashboard for new failures. Monthly: rotate nu validator JAR if a new release ships. Quarterly: audit the SDTT presets against Schema.org additions. Annually: replace the headless Chromium binary; re run full schema strategy review.
End of Framework Document
Document version: 1.0 Created: 2026-05-14 Maintained by: ThatDeveloperGuy
Type-specific companion to framework-schema.md. Apply after the parent, in parallel with type-specific neighbors (ecommerce, local, news, video). Bubbles validation stack in Section 14 sweeps the client roster nightly.
Companions
- framework-schema.md, parent reference
- framework-localseo.md, framework-ecommerceseo.md, framework-newsseo.md, framework-videoseo.md
- framework-aicitations.md, framework-aioverviews.md, framework-trustsignals.md, framework-eeat.md
- framework-knowledgegraph.md, framework-entitysalience.md, framework-voicesearch.md
Want this framework implemented on your site?
ThatDevPro ships these frameworks as productized services. SDVOSB-certified veteran owned. Cassville, Missouri.
See Engine Optimization service ›