---
title: "Schema Markup Deep Dive"
slug: "schema-markup-deep"
category: "seo"
tags: ["seo", "schema-org", "json-ld", "rich-results"]
status: "stable"
last_updated: 2026-05-29
summary: "Beyond Article and BreadcrumbList. HowTo, FAQPage, Product, Course, VideoObject, DefinedTerm, validators, and pitfalls."
related: ["[[seo/structured-data]]", "[[seo/e-e-a-t]]", "[[seo/local-seo]]", "[[seo/content]]", "[[seo/technical]]", "[[seo/glossary]]", "[[seo/audit-checklist]]"]
---

## Overview

The five baseline schemas (`WebSite`, `Organization`, `BreadcrumbList`, `Article`, `Person`) cover most pages. The next tier splits in two. `Product`, `Course`, and `VideoObject` still earn specialized rich results when the page genuinely is that thing. `HowTo` and `FAQPage` no longer earn any rich result; Google retired both (see the sections below), but the markup is still valid schema.org and still useful for AI-agent consumption and entity grounding. Shipping the wrong schema or marking up invisible content triggers manual actions; ship only what matches what a human sees on the page. For the one-page type-to-required-field reference, see [[cheatsheets/schema-org-types]]; for the static-site embed walkthrough, see [[howto/add-jsonld-to-static-site]].

## HowTo: valid markup, but no rich result

`HowTo` no longer earns a rich result. Google restricted HowTo rich results to desktop on 2023-08-08 and removed them entirely later in September 2023; as of 2026 HowTo produces no rich result on any device. The markup remains valid schema.org and is still worth shipping for AI-agent consumption, not for a SERP feature. Do not add `HowTo` expecting step-by-step results in search.

```json
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "Install Postgres on Ubuntu 24.04",
  "totalTime": "PT10M",
  "supply": [{ "@type": "HowToSupply", "name": "Ubuntu 24.04 server" }],
  "tool": [{ "@type": "HowToTool", "name": "apt" }],
  "step": [
    { "@type": "HowToStep", "name": "Update apt", "text": "Run sudo apt update." },
    { "@type": "HowToStep", "name": "Install Postgres", "text": "Run sudo apt install postgresql-16." }
  ]
}
```

- Every step has a `name` and a `text`. Add `image` for visual steps.
- `totalTime` uses ISO 8601 duration format: `PT10M` is ten minutes, `PT1H30M` is 90 minutes.
- Apply only when the page is a real procedural guide. Marking up a generic article as `HowTo` is a guideline violation.

## FAQPage: valid markup, but no rich result

`FAQPage` no longer earns FAQ accordions in search. Google fully removed FAQ rich results from Search on 2026-05-07; the FAQ search appearance, the rich-result report, and Rich Results Test support are being retired through 2026. No site, including government and health sites, gets the rich result now. Google still parses `FAQPage` for page understanding, and the markup helps AI-agent consumption, so ship it on genuine Q&A pages for those reasons, not to win a SERP feature.

```json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is Postgres?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Postgres is an open-source relational database."
      }
    }
  ]
}
```

- The page must visibly contain the same questions and answers. Hidden FAQs trigger manual actions.
- Each question has exactly one accepted answer. Multiple `acceptedAnswer` entries are invalid.
- Answers contain real information, not "contact us for details." Promotional answers disqualify the markup.

## Product: e-commerce schema with offers and reviews

`Product` schema earns shopping rich results, the price-and-stars annotations under organic results.

```json
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Mechanical Keyboard",
  "image": "https://example.com/keyboard.jpg",
  "sku": "KB-001",
  "brand": { "@type": "Brand", "name": "Example Brand" },
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/products/keyboard",
    "priceCurrency": "USD",
    "price": "149.00",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "127"
  }
}
```

- The price in the JSON-LD must match the price on the page. Drift here triggers a Merchant policy violation.
- `aggregateRating` requires real reviews on the same page. Pulling ratings from external sources is a violation.
- Use `Product` only on product detail pages. Category and listing pages use `ItemList` instead.

## Course: structured education content

`Course` schema earns course-card rich results in education searches.

```json
{
  "@context": "https://schema.org",
  "@type": "Course",
  "name": "Intro to Postgres",
  "description": "A 4-week intro course covering Postgres fundamentals.",
  "provider": {
    "@type": "Organization",
    "name": "Example University",
    "url": "https://example.com"
  }
}
```

- Required: `name`, `description`, `provider`. Recommended: `hasCourseInstance` with `courseMode`, `startDate`, and `endDate`.
- For online courses, set `courseMode` to `"online"` or `"blended"`.
- Apply only to actual courses; do not mark a blog series as a course unless it has enrollment and completion semantics.

## VideoObject: embedded video metadata

`VideoObject` schema earns video thumbnails in search results.

```json
{
  "@context": "https://schema.org",
  "@type": "VideoObject",
  "name": "Postgres Backup Tutorial",
  "description": "A 12-minute walkthrough of pg_dump.",
  "thumbnailUrl": "https://example.com/thumb.jpg",
  "uploadDate": "2026-05-14",
  "duration": "PT12M",
  "contentUrl": "https://example.com/video.mp4",
  "embedUrl": "https://example.com/embed/video"
}
```

- Required: `name`, `description`, `thumbnailUrl`, `uploadDate`. Recommended: `duration`, `contentUrl` or `embedUrl`.
- The video must be embedded or playable on the page. Marking up a link to YouTube on a different domain is a violation.
- For YouTube embeds, the schema lives on the embedding page; YouTube already declares its own.

## DefinedTerm: glossary and term pages

`DefinedTerm` and `DefinedTermSet` schema markup glossary pages and individual term definitions. Useful for AI-agent consumption even where rich results do not exist.

```json
{
  "@context": "https://schema.org",
  "@type": "DefinedTerm",
  "name": "Crawl Budget",
  "description": "The number of URLs a search engine will fetch from a site within a window.",
  "inDefinedTermSet": {
    "@type": "DefinedTermSet",
    "name": "SEO Glossary",
    "url": "https://example.com/glossary/"
  }
}
```

- Use on dedicated definition pages and glossary entries.
- Pair with `Article` schema on the surrounding page if the page also has editorial body content.

## Validate with the Rich Results Test and the Schema Validator

Two validators, both required.

- Google Rich Results Test (`https://search.google.com/test/rich-results`): confirms eligibility for specific rich result types Google currently surfaces.
- Schema.org Validator (`https://validator.schema.org`): confirms the JSON-LD parses and conforms to the vocabulary.

Run both pre-deploy on the top 20 URLs from the sitemap. Failures block the deploy. See the validation rules in [[seo/structured-data]].

## Never mark up invisible content

The cardinal rule, repeated. Schema markup must match what a human sees on the page.

- A `FAQPage` schema with questions that do not appear in the visible HTML is a Google guideline violation.
- A `Product` schema on a page without product details is a violation.
- A `HowTo` schema with steps that are not visible is a violation.
- Recovery from a structured-data manual action takes 2 to 6 weeks; do not risk it for a rich result.

## Related

- [[seo/structured-data]]
- [[seo/e-e-a-t]]
- [[seo/local-seo]]
- [[seo/content]]
- [[seo/technical]]
- [[seo/glossary]]
- [[seo/audit-checklist]]
