Start a project

Article · Technical SEO · JSON-LD · Schema.org · 11 min read

JSON-LD schema that search and AI can use

JSON-LD is a way to say, in a script tag, what a page is: an article by a named person, a breadcrumb, an organisation, a question and answer that is also visible on the page. Search engines and some AI tools can use that graph when it is valid and when it matches the HTML a person can read. It is not a ranking trick, and it will not rescue a thin page. Markup that invents a rating, an FAQ or a company detail you would not put in the body is a liability.

On this page 10 sections
  1. Markup is a claim about the page, not a ranking trick
  2. The types a small site actually needs
  3. Person, Organization and sameAs
  4. Article, BreadcrumbList and WebPage together
  5. FAQPage only when the questions are visible
  6. The errors that get markup ignored
  7. Testing, diffs and not drifting from the HTML
  8. A starter graph you can adapt
  9. Key takeaways
  10. Frequently asked questions

I publish JSON-LD on leej.uk from the same source as the article, and I built validators for it because a graph that drifts from the HTML is worse than a missing graph. The method below is how I keep the two aligned.

Markup is a claim about the page, not a ranking trick

schema.org is a shared vocabulary. JSON-LD is one way to write it down, and it is the format Google recommends in its structured data documentation. The script does not have to be visible to the reader, but the facts in it do. If the JSON says the author is a chartered engineer in Manchester and the page never says so, you have created a second, quieter page that only machines are invited to trust.

Google’s structured data policies are the rules to read before you chase a rich result. They change in detail, so I will not restate them as a permanent checklist. The durable bit is: mark up content that is on the page, do not mark up content that is misleading, and do not use a type you cannot stand behind.

Rich results — FAQ foldouts, article features, sitelinks search boxes — are optional presentations. Valid markup makes a page eligible for some of them. It does not entitle you to any of them, and eligibility can change. I treat JSON-LD the way I treat a privacy manifest: a precise declaration of what is actually there, not a brochure for a reviewer or a crawler.

The types a small site actually needs

A small business or personal site rarely needs a zoo of types. A useful starter set is:

Add other types when the page is genuinely that thing: SoftwareApplication for a shipped app, ContactPage for a contact page, Offer only if you are displaying a price you would honour. Do not add Product, Review or AggregateRating because a tutorial mentioned a rich result. Invented reviews are one of the fastest ways to have markup ignored, or worse.

The Schema Generator can draft these types. The Schema Entity Graph is useful when you want to see how the nodes join. Neither tool should invent a type the page does not support.

Person, Organization and sameAs

Search engines and assistants need to know who is speaking, and which official profiles belong to that entity. That is the job of a stable Person or Organization node, plus sameAs links to profiles you actually control.

A compact, imaginary example for a sole trader might look like this:

{
  "@context": "https://schema.org",
  "@type": "Person",
  "@id": "https://example.com/#person",
  "name": "Alex Hart",
  "url": "https://example.com/",
  "email": "desk@example.com",
  "sameAs": [
    "https://github.com/example",
    "https://www.linkedin.com/in/example"
  ]
}

Choices that matter:

The Entity Home and sameAs Builder is a way to assemble that node. I use the same “only list what exists” rule that I use in the case study on small, focused Apple apps.

Article, BreadcrumbList and WebPage together

A long article is easier to understand when the graph says, in one go: this is a page, it is an article, it sits in a breadcrumb, and it was written by that person. JSON-LD can express that as a single @graph, or as one node with nested properties. I prefer a graph with stable @ids, so the article can point at the person and the breadcrumb without repeating their details.

A sketch, trimmed to the ideas that matter:

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "TechArticle",
      "@id": "https://example.com/articles/example#article",
      "headline": "How to keep an XML sitemap accurate",
      "datePublished": "2026-11-10",
      "author": { "@id": "https://example.com/#person" },
      "mainEntityOfPage": { "@id": "https://example.com/articles/example#webpage" }
    },
    {
      "@type": "WebPage",
      "@id": "https://example.com/articles/example#webpage",
      "url": "https://example.com/articles/example",
      "isPartOf": { "@id": "https://example.com/#website" },
      "breadcrumb": { "@id": "https://example.com/articles/example#breadcrumb" }
    },
    {
      "@type": "BreadcrumbList",
      "@id": "https://example.com/articles/example#breadcrumb",
      "itemListElement": [
        { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
        { "@type": "ListItem", "position": 2, "name": "Articles", "item": "https://example.com/articles" },
        { "@type": "ListItem", "position": 3, "name": "How to keep an XML sitemap accurate" }
      ]
    }
  ]
}

The last breadcrumb item is the current page; it does not have to repeat the URL as item, but the visible trail and the list must agree. Dates must match what you would defend on the page. A headline should be the actual title, not a more “clickable” variant you would not show in the H1.

You do not have to mark every page as an Article. A contact page is not an article. A tool page may be a WebPage and, if it is genuinely a piece of software you offer, something more specific, but it should not pretend to be a news story.

FAQPage only when the questions are visible

FAQPage is the type teams add too quickly. It is appropriate when the page visibly contains questions and answers, in a form a person can read without running a special script, and when those questions are the main or a substantial part of the page, or a clearly marked section of it.

It is not appropriate when:

Google has narrowed how FAQ rich results are shown over time, and that policy can change again. Even if a rich result is not on offer for your site, honest visible FAQs can still help a reader and an assistant. Invisible FAQs help nobody and can violate policy.

On this site, article FAQs are written in the page, then turned into both an accordion and, only then, FAQPage markup. The answers are the same words. If I would not show the question, I do not mark it up.

The errors that get markup ignored

The usual failures, in the order I look for them:

When markup is “valid but unused”, it often still has one of those problems, or it is simply not eligible for a rich result. Validity is a floor, not a promise. The Schema and Rich Result Validator will catch many technical issues; it will not tell you whether a sentence is true.

Testing, diffs and not drifting from the HTML

Generate the JSON-LD from the same fields that generate the page: title, dates, author, breadcrumb, visible FAQ. If a person edits the markdown and the graph is hand-copied, the graph will drift. I treat a drift the way I would treat a mismatch in what an iOS app really costs: a number that appears in two places must have one source.

A practical test pass:

  1. View the page source and confirm the script is present in the first response, not only after a client render.
  2. Parse the JSON. Fix syntax before you argue about types.
  3. Validate with a schema tester, and with Google’s own rich results test when you are chasing a specific feature, remembering that tool output can change.
  4. Diff the graph against the live HTML. The Structured Data Diff is built for that comparison. A property that has no counterpart on the page is a bug.
  5. Re-run the check when the title, the FAQ or the author line changes, in the same release.

For a short note on giving assistants a map of the site itself, that is llms.txt, not more schema. The two can coexist; they should not contradict each other about who you are.

A starter graph you can adapt

For a small UK studio site, I would ship, at minimum:

Then stop. Do not add a LocalBusiness opening-hours block if you do not have a premises and hours. Do not add a Product for “web design” unless you are selling a product with a price. The writing guide for AI answers applies to the graph as much as to the prose: say what is true, and leave a clean silence where you do not have a fact.

A launch is a good moment to validate this set once, as part of a technical SEO launch checklist, and then to keep the generator honest as pages change.

Key takeaways

If you want a second pair of eyes on a graph that is valid and still feels untrustworthy, tell me about it.

Frequently asked questions

What JSON-LD types does a small business website need?

Usually WebSite, WebPage, a Person or Organization, BreadcrumbList, and Article on the writing. Add FAQPage, SoftwareApplication or commercial types only when the page is genuinely that thing and the facts are visible. A short, accurate graph is more useful than a long one with empty fields.

Should FAQ schema be on every page?

No. Use FAQPage only where the questions and answers are visible on that page. Do not hide them in the JSON alone, and do not repeat a generic footer FAQ as schema on every URL. Policies for FAQ rich results have changed over time, but the honesty rule does not.

What is an entity graph in schema.org?

It is a set of nodes with stable identities — you, the organisation, the site, a particular article — linked to each other with properties such as author, isPartOf and sameAs. The point is to say “this article was written by this person, who is the same as these official profiles”, not to add more keywords.

How should Article and BreadcrumbList be combined?

Put them in one JSON-LD graph and give each an @id. The article can point at the page, the page at the breadcrumb, and the breadcrumb can list the visible trail, with positions that match what a person sees. The headline and the last breadcrumb name should agree with the H1.

Why is my structured data valid but unused?

Validity only means the JSON parses and the types look well formed. The markup may still disagree with the page, use a type that is not eligible for a rich result, or fall under a policy restriction. Google also does not have to show a rich result for eligible pages. Check a diff against the HTML, and read the current documentation for the feature you were expecting.

Written by Lee Johnson. Published 10 November 2026.

All articles · Swift A–Z · Apps

Questions, or an app in mind?

Let's bring your ideas to life. Whether you're envisioning a game-changing Apple Watch app or a transformative iOS experience, I'm here to make it happen.

Start a project