Structured data is the part of your page no visitor ever sees: a small block in the source that tells a machine what the page actually contains. Not as prose, but as a list of named fields — author, date, price, opening hours, question and answer. For search engines this has been routine for years. For AI answer systems it is getting interesting, because what they need from a page is exactly that: unambiguous facts they can take over without guessing.
Why machines need a second copy of your facts
A human reads "open Tue–Fri, 9am to 6pm" and understands immediately. A program has to guess: is that the shop's opening hours, the phone line's, or the café next door mentioned in the same paragraph? That guessing works surprisingly well and still goes wrong regularly, especially on pages full of numbers.
Structured data takes the guessing away. It says: this object is a LocalBusiness, this is its name, these are its hours, that is its location. The gain is not that the machine learns something new — it is all in the text already. The gain is that it knows for certain. And systems that have to compose an answer and back it with a source reach for what they know for certain.
The four types that are enough for a small site
The vocabulary behind structured data is called schema.org and covers over eight hundred types. You need four of them; everything else is refinement.
Organization describes who is behind the site: name, logo, URL, contact, social profiles. This one block is the basis for systems recognizing your brand as a thing with an identity rather than as an arbitrary domain. If you run a shop or a local trade, use LocalBusiness instead and add address and opening hours.
Article (or BlogPosting) describes a piece of writing: headline, author, publication and modification date. The modification date is the underrated part. Answer systems prefer the more current source for time-sensitive questions, and the only reliable statement about that is the machine-readable one you provide yourself.
FAQPage describes question-and-answer pairs. It is the type with the most direct bearing on AI answers, because it makes the mapping "this question is answered here" explicit instead of leaving it to be inferred from headings. An honest caveat on it below.
BreadcrumbList describes where the page sits in your structure. It sounds like housekeeping, but it helps every reader — human or machine — tell whether they are on an overview or a detail page.
If you sell products, add Product with an Offer carrying price and availability. That is the only further type genuinely worth it for most small sites.
How to add structured data for AI, step by step
The work has five steps, and the order is not arbitrary: first you look at what is already there, then you decide per page type what belongs where, and only then do you write, wire in and validate. Start with the writing and you almost always end up with duplicate markup, because the theme or a plugin has been emitting something all along.
Step 1: Audit what you have
Pick three pages: the home page, a typical article and a service or product page. For each, open the source (Ctrl+U on Windows, Cmd+Option+U on a Mac) and search for application/ld+json. Every hit is a block. Note which @type it contains and where it comes from — theme, SEO plugin, shop system, a leftover from the last rebuild.
Google's Rich Results Test is quicker: enter the URL, and the detected items list every type Google finds. Three findings turn up almost every time:
- Duplicate types. Two
Organizationblocks with the name spelled differently, because theme and plugin both emit one. One of them has to go. - Placeholders. An author called "admin", the theme's logo, a date from the year of installation.
- Markup that only exists in the browser. If a block shows up in the developer tools ("Inspect"), but not in the page source, it is injected by JavaScript, often through Tag Manager. Google executes JavaScript; most AI crawlers do not. For them that markup does not exist.
Step 2: Choose types per page type
Not every page needs every type. This mapping fits most small sites:
| Page type | Types | What matters |
|---|---|---|
| Home page | Organization (or LocalBusiness) plus WebSite | describe it properly once, every other page refers to it |
| Article, guide | Article or BlogPosting with a Person as author, plus BreadcrumbList | date and author from the system, not typed by hand |
| Product page | Product with Offer | price, currency and availability as shown on the page |
| Service page | Service pointing to your company as provider | describes the offer, does not produce any special display |
| Location, contact | LocalBusiness with address and opening hours | the same details as in your Business Profile |
| Page with questions | FAQPage | only where questions and answers are visible in the text |
The WebSite block on the home page is small but not redundant: Google uses it as the basis for the site name shown above the title in results. Legal notice, privacy policy, contact form and internal search result pages, on the other hand, need no markup at all.
For local businesses there is one more rule: name, address and phone number in the LocalBusiness block read exactly as they do in your Google Business Profile, not roughly the same. Why the profile carries so much weight is covered in getting found locally.
Step 3: Write the JSON-LD
There are three notations for structured data; use JSON-LD. The block sits as its own <script> element in the source, usually in the <head>, entirely separate from the rest of the HTML. That is the practical advantage over the older Microdata and RDFa formats, where the annotation is woven into the visible markup and breaks with the next layout rebuild.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Structured data for AI answers",
"datePublished": "2026-03-14",
"dateModified": "2026-08-01",
"author": { "@type": "Person", "name": "First Last" },
"publisher": {
"@type": "Organization",
"name": "My Company",
"url": "https://my-company.com"
}
}
</script>
The same pattern covers the other types. Here is a complete LocalBusiness block for the fictional kitchen studio that also serves as the example in creating llms.txt — swap in your own name, address and hours and it is ready to paste:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Harper Kitchens",
"url": "https://harperkitchens.com",
"image": "https://harperkitchens.com/showroom.jpg",
"telephone": "+44 117 496 0000",
"address": {
"@type": "PostalAddress",
"streetAddress": "12 Example Road",
"addressLocality": "Bristol",
"postalCode": "BS1 4ST",
"addressCountry": "GB"
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
"opens": "09:00",
"closes": "18:00"
}
],
"sameAs": ["https://www.instagram.com/harperkitchens"]
}
</script>
And this is what a FAQPage block looks like for two questions. The condition from further down applies here with full force: both questions and both answers must appear in the visible text of the page.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What does a fitted kitchen cost?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Most projects come in between £9,000 and £25,000, depending on size and appliances. Measuring up and planning are free."
}
},
{
"@type": "Question",
"name": "How long does the installation take?",
"acceptedAnswer": {
"@type": "Answer",
"text": "A typical kitchen is fitted in two to three working days. Water and electrics have to be in place before fitting day."
}
}
]
}
</script>
For shops, the Product block with an Offer comes on top. Price and availability change, so these values belong in the block straight from the shop system, not typed in by hand — a markup price that differs from the page price is exactly the contradiction described further down:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Solid oak worktop, 40 mm",
"image": "https://harperkitchens.com/products/oak-worktop.jpg",
"description": "Solid oiled-oak worktop, cut to measure up to 4.1 m in length.",
"sku": "WT-OAK-40",
"brand": { "@type": "Brand", "name": "Harper Kitchens" },
"offers": {
"@type": "Offer",
"url": "https://harperkitchens.com/products/oak-worktop",
"price": "169.00",
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock"
}
}
</script>
Several types on one page go either into several <script> blocks or into one using @graph. Both are valid; several blocks are easier to maintain, while a @graph is clearer as soon as you link the objects to each other. What that looks like is in the section on entities below.
Step 4: Wire it into your site
In WordPress, Shopify and most site builders a plugin or the theme handles this — in which case your job is mostly to check what it emits rather than to write it yourself. Every route shares the condition from step 1: the markup has to be in the HTML the server delivers, not generated in the browser by JavaScript.
Plain HTML. Paste the <script> block into the <head> of the page in question. Google also reads it in the <body>; the <head> is just the usual place. The Organization block goes on the home page or the about page, which is where Google recommends putting it.
WordPress. Yoast SEO and Rank Math already emit a linked @graph with organization, website, page, article and author. Your job there is configuration: set in the plugin settings whether the site represents an organization or a person, enter name, logo and profile URLs, fill in the author profiles. And make sure only one plugin emits markup; many themes and shop extensions bring their own. A custom block without a plugin goes into your child theme via the wp_head hook:
add_action('wp_head', function () {
if (!is_front_page()) {
return;
}
$data = [
'@context' => 'https://schema.org',
'@type' => 'Organization',
'name' => 'Harper Kitchens',
'url' => home_url('/'),
'logo' => home_url('/logo.png'),
];
echo '<script type="application/ld+json">' . wp_json_encode($data) . '</script>';
});
Drupal. The Schema.org Metatag module extends the Metatag module with schema.org fields. You enable the submodules for the types you need, such as Organization, WebSite and Article, and fill the fields per content type under Configuration → Search and metadata → Metatag using tokens: [node:title] for the headline, [node:changed:html_datetime] for the modification date. That way the values come from the content itself and never go stale.
Next.js and React. In the App Router you render the block as a <script> element directly in page.tsx or layout.tsx, which is also what the Next.js documentation recommends. The replace stops a < in your content from closing the script element early:
export default async function Page({ params }) {
const { slug } = await params;
const post = await getPost(slug);
const jsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: post.title,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
author: { "@type": "Person", name: post.authorName },
};
return (
<article>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
}}
/>
{/* … */}
</article>
);
}
Because the page is rendered on the server, the block is in the delivered HTML. In a purely client-side app, such as a Vite app without server rendering, it is not; there the only fix is to prerender the pages you want found.
Step 5: Validate
Two tools, two minutes. Google's Rich Results Test (search.google.com/test/rich-results) tells you what Google reads out of your page and whether it qualifies for enhanced presentation. The validator at validator.schema.org is stricter and also reports fields Google ignores but other systems read. Both take a URL, so there is nothing to install.
The third test is the most important and needs no tool: open the page, view source, find the JSON block and read it. Does it really say what the page says? With auto-generated markup the answer is surprisingly often no — an old date, an author called "admin", a price without a currency.
Once the page is live, Search Console takes over the ongoing monitoring. Under "Enhancements" it creates a report for every detected type that has its own presentation in the results, such as breadcrumbs or product snippets, with valid and invalid items and the error per URL. That is where you notice a theme update breaking the markup on two hundred pages at once. Not every type gets a report — Organization, WebSite and Article, for example, do not; for those the Rich Results Test remains your check. After a fix, click "Validate fix" in the report and Google will look at the affected pages again.
The most common mistake: markup with no cover in the text
Structured data may only describe what is visible on the page. A FAQPage annotation for questions that do not appear in the text, or a rating that is nowhere to be found, is not optimization but a false statement. Google treats it as spam and can ignore your markup across the whole domain; for AI systems it is simply a contradiction between two accounts of the same page, and that damages exactly the trust you were trying to build.
The rule is simple: text first, markup second. Reverse the order and you have built a nice façade in front of an empty room.
Entities: turning separate blocks into one graph
So far the blocks sit side by side: an article, a company, an author, each on its own. That does not tell a machine that the author on the article page is the same person as the one on the team page, or that the publisher in the article is the same company as the Organization on the home page. It can assume so because the names match. It can only know if you say so.
There are two tools for that. @id gives every object a fixed identifier in the form of a URL, usually the address of the page describing it plus a fragment: https://harperkitchens.com/#organization for the company, https://harperkitchens.com/team/emma-harper#person for the author. Everywhere else the company appears, you then write only { "@id": "https://harperkitchens.com/#organization" } instead of describing it again. The identifier does not have to resolve to anything; it only has to be spelled exactly the same everywhere.
sameAs links an object to profiles outside your site: Instagram, LinkedIn, the trade directory, and for better-known companies the Wikidata entry. With it you say: the company on this page and the company on that profile are the same. It works the same way for people, for example with their LinkedIn profile. Only list profiles you own and maintain; an abandoned account with an old address connects you to precisely the wrong details.
This is what a guide article from the kitchen studio looks like when company, website, article and author are linked into one graph:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "LocalBusiness",
"@id": "https://harperkitchens.com/#organization",
"name": "Harper Kitchens",
"url": "https://harperkitchens.com/",
"logo": "https://harperkitchens.com/logo.png",
"telephone": "+44 117 496 0000",
"address": {
"@type": "PostalAddress",
"streetAddress": "12 Example Road",
"addressLocality": "Bristol",
"postalCode": "BS1 4ST",
"addressCountry": "GB"
},
"sameAs": [
"https://www.instagram.com/harperkitchens",
"https://www.linkedin.com/company/harper-kitchens"
]
},
{
"@type": "WebSite",
"@id": "https://harperkitchens.com/#website",
"url": "https://harperkitchens.com/",
"name": "Harper Kitchens",
"inLanguage": "en-GB",
"publisher": { "@id": "https://harperkitchens.com/#organization" }
},
{
"@type": "Person",
"@id": "https://harperkitchens.com/team/emma-harper#person",
"name": "Emma Harper",
"jobTitle": "Owner and kitchen designer",
"url": "https://harperkitchens.com/team/emma-harper",
"worksFor": { "@id": "https://harperkitchens.com/#organization" },
"sameAs": ["https://www.linkedin.com/in/emma-harper-kitchens"]
},
{
"@type": "Article",
"@id": "https://harperkitchens.com/guides/cost#article",
"headline": "What does a fitted kitchen cost?",
"datePublished": "2026-02-10",
"dateModified": "2026-09-01",
"mainEntityOfPage": "https://harperkitchens.com/guides/cost",
"isPartOf": { "@id": "https://harperkitchens.com/#website" },
"author": { "@id": "https://harperkitchens.com/team/emma-harper#person" },
"publisher": { "@id": "https://harperkitchens.com/#organization" }
}
]
}
</script>
On the home page the first two objects are enough. On article pages you simply repeat company and website; it costs nothing, and every page stays understandable on its own. What matters is that the @id values are identical on every page. Opening hours and the remaining fields from the LocalBusiness block above can go into the first object.
Why the effort? An answer system assembles its picture of your company from several sources: your page text, your markup, your Business Profile, directories, review sites. If they agree, the matter is settled. If the markup says "Harper Kitchens Ltd", the footer says "Harper Kitchen Design", the Business Profile has an old phone number and Instagram a different founding year, the system has to decide which detail holds, or whether this is even the same company. It may decide correctly. It may also blend the details, and a source that contradicts itself is not one anybody likes to cite.
In practice: settle once how the company name, address, phone number, founding year and author names are written, and align page text, markup and profiles to that. It is the same discipline local search has demanded for years, just with more readers. Markup does not replace that alignment. It only makes visible whether you have done it.
AI structured data: which systems read your markup
Searching for AI structured data suggests there might be a second vocabulary — something to add on top of schema.org specifically for language models. There is not. AI structured data is the same JSON-LD described above; what has changed is who reads it, and what they do with it.
Three groups of readers are worth telling apart. Google's AI Overviews sit directly on Google's index: everything your markup clarifies for classic search — who is behind the site, when a piece was last updated, which question a paragraph answers — is clarified for the generated answers too. Systems like ChatGPT and Perplexity fill their own indexes with their own crawlers; how much weight their parsers give schema.org is not published, but the markup costs them nothing to read and hands them your facts in the least ambiguous form available. And the crawlers that collect training data take pages wholesale — for them markup matters least, because nothing is being looked up, only absorbed.
The practical conclusion is reassuring: there is nothing separate to build. A page whose blocks are correct for classic search is as prepared for AI systems as markup can make it. What remains worth your time on top is not more markup but fewer ambiguities in the text itself: visible dates, named authors, one question per heading.
What markup can and cannot do for AI answers
It does two things. It makes facts unambiguous, so an answer system can take over price, date or author without interpreting. And it makes relationships explicit — that this text belongs to this organization, that this answer belongs to this question.
It does not make a page rank better. Structured data is not a ranking factor, neither in classic search nor, as far as anyone can judge from outside, in how answer systems pick their sources. It makes sure an already good page is understood correctly; it does not make a weak page win.
And a note on expectations for FAQPage: since 2023 Google has shown the expandable FAQ results in its listings almost exclusively for government and health sites. That does not make the markup worthless — it is still read, and it makes your question-and-answer structure unambiguous for any parser — but anyone adding it for the colourful presentation in the results page will be disappointed.
Checklist: structured data for AI
- Audit what you have. Search the source of the home page, an article and a service page for
application/ld+json, and note duplicate types and placeholders. - Choose types per page type.
OrganizationorLocalBusinessplusWebSiteon the home page,Articlewith aPersonon articles,ProductorServiceon offer pages,FAQPageonly where the questions are visible. - Write the JSON-LD. Pull date, price and author from the system rather than typing them.
- Link it into one graph. Fixed
@idvalues for company, website and people,sameAspointing to your own, maintained profiles. - Render it on the server. The markup is in the delivered HTML, and only one plugin or module emits it.
- Validate. Rich Results Test,
validator.schema.organd a look at the page source. - Align and monitor. Keep names, address and facts the same across text, markup and profiles, and watch the "Enhancements" reports in Search Console.
Where this belongs
Markup is the second of three moves that make you citable in AI answers. The first is clear facts and self-contained paragraphs, the third is an llms.txt as a map of your best pages. How the three work together, and where ChatGPT gets its sources in the first place, is in getting found in ChatGPT. And because markup is only read if a crawler is allowed to fetch the page at all, check your robots.txt: what to allow and what not is covered in controlling AI crawlers.
Frequently asked questions
What is structured data?
Machine-readable annotations in a page's source that name what the visible content means: that this string is a price, that one a publication date, and the third the author's name. The usual vocabulary is schema.org, the usual format JSON-LD.
Is there special structured data for AI?
No. AI structured data is regular schema.org in JSON-LD — the same vocabulary and notation search engines have read for years, now also consumed by AI answer systems. A page whose markup is correct for Google needs nothing extra for ChatGPT, Perplexity or AI Overviews.
How do I add structured data for AI?
The same way as for Google: as a JSON-LD block using the schema.org vocabulary in the page source. First check what your theme or plugin already emits, then choose the right types per page type, render the block on the server and test it with the Rich Results Test and validator.schema.org. There is no separate format for AI systems.
Does ChatGPT read schema markup?
OpenAI does not publish whether, or how heavily, ChatGPT uses schema.org. What is known is that ChatGPT fetches pages with its own crawlers for web search, and a JSON-LD block in the source comes along with them. You cannot count on markup leading to a citation with any provider; you can count on it making your facts unambiguous.
Which schema types matter most for AI visibility?
Organization or LocalBusiness for who is behind the site, Article with author and modification date for content, Product with Offer for shops, and FAQPage where questions are visibly answered. More important than the number of types is that they are linked via @id and agree with the page text.
Is structured data a ranking factor?
No. It does not influence position directly. It does influence how a result is presented and how confidently machines capture a page's facts — which pays into click-through rate and citability indirectly, but it is not a ranking signal.
Which format should I use, JSON-LD or Microdata?
JSON-LD. It sits as its own block in the source and survives layout changes, while Microdata is woven into the HTML of the visible content and breaks easily in the process. All major providers recommend JSON-LD.
How do I test my structured data?
With Google's Rich Results Test for what Google reads out, and with validator.schema.org for the stricter check against the vocabulary. Both only need the URL. It is worth also viewing the source to confirm that auto-generated markup really carries the current values.
Is FAQPage markup still worth anything?
For the expandable FAQ results in Google's listings, barely — since 2023 Google shows those almost only for government and health sites. The markup is still read, and for systems that pull question-and-answer pairs out of a page it is the least ambiguous way to offer them.
What does sameAs mean in structured data?
sameAs lists URLs that describe the same object elsewhere, for example a company's Instagram, LinkedIn or Wikidata profile. It tells machines explicitly that those profiles and your website belong to the same company or person. Only list profiles you own and keep up to date.
Is JSON-LD read if it is added with JavaScript or Tag Manager?
By Google, usually yes, because Google renders pages with JavaScript. Most AI crawlers, however, do not execute JavaScript and never see that markup. If you write for both, output the block on the server, in the delivered HTML.