Ask whether a page can carry more than one type of structured data and you will get two confident, opposite answers. One camp says a page should have exactly one type, because anything else confuses search engines. The other says stack everything you can, because more markup is more chances at a rich result.
Both are wrong, and Google has published the actual answer in pieces across several documentation pages. Nobody collects them, so here they are together, along with the two types that come with a hard limit almost nobody knows about.
Google's actual position
Two sentences from Google's structured data guidelines settle most of the argument.
On multiple items:
If there are items that are more helpful when they are linked together (for example, a recipe and a video), use
@idin both the recipe and the video items to specify that the video is about the recipe on the page.
That is Google describing two types on one page as normal, and telling you how to connect them.
And on which one matters:
To make sure that Google Search understands what the main purpose of the page is, include the main type of structured data that reflects the main focus of the page.
Put those together and the rule is not a count. It is: describe everything the page genuinely contains, make sure one of them is clearly the point of the page, and connect the pieces.
So a recipe page with a video, a breadcrumb trail and an author is four types, and that is correct. The same page with a Product bolted on because product markup gets rich results is four correct types and one lie.
One script block, or several?
A practical question that comes up immediately, and the honest answer is that both work.
Several blocks. Each <script type="application/ld+json"> holds one item. Google's own examples show separate blocks for separate items on a page.
One block with @graph. A single script containing an array of nodes, each with its own @id, cross-referencing each other.
{
"@context": "https://schema.org",
"@graph": [
{ "@type": "WebPage", "@id": "https://example.com/recipes/soup" },
{ "@type": "Recipe", "@id": "https://example.com/recipes/soup#recipe" },
{ "@type": "Person", "@id": "https://example.com/about#priya" }
]
}Google does not document a preference between the two, so anyone telling you one is required is overstating. What the @graph form gives you is practical rather than official: everything on the page is in one place, which makes it much harder to end up with two blocks that describe the same thing differently, and much easier to see the whole picture when you view source.
The trade-off is that a single block has to be assembled by one system. If your SEO plugin emits one block and your recipe plugin emits another, you have separate blocks whether you wanted them or not, and the job becomes making sure they reference each other rather than duplicate each other.
The joint that holds it together
Whichever form you use, @id is what turns several items into one description instead of several competing ones.
Google's recipe-and-video example above is the small version of this. The general version is that every node gets a stable identifier, and anything that needs to mention it points at the identifier rather than describing it again. Your article names its author by @id; the author is described once on the about page.
This is the single most important thing about multi-type pages, and it is covered properly in our Understand pillar guide, including the four specific ways these connections break. Two of them are worth naming here because they are what multi-type pages get wrong:
Duplicate entities. The same real thing declared twice with different identifiers, usually because two plugins each emitted their own. Now there are two of your company on one page.
Dangling references. A node points at an @id that nothing defines. Your Article claims a publisher and the publisher exists nowhere. This is worse than leaving the property out, because a reference that resolves to nothing looks like an answer and is not one.
Both get more likely as you add types, which is the real cost of stacking. Not that Google objects to five types, but that five types from three different sources is where the graph quietly falls apart.
Naming the main one
Google asks you to include "the main type of structured data that reflects the main focus of the page." There are two properties for making that explicit rather than leaving it implied.
mainEntity goes on the page and points at the thing the page is about. mainEntityOfPage is its inverse, going on the thing and pointing at the page. They are the same statement from opposite ends, so you only need one, and the distinction from isPartOf (being a piece of something, rather than being the subject of a page) is covered in our hasPart guide.
In practice, on most pages the main entity is obvious from the types present, and a WebPage node with a mainEntity pointing at the Article or Recipe is a tidy way to say so without ambiguity. It is worth being explicit on pages where two types could plausibly be the point.
Two kinds of type, and why the distinction helps
A useful way to think about a page's stack: some types describe the page, and some describe things the page mentions. Confusing the two is behind a lot of muddled markup.
Page-level types answer "what kind of page is this?" WebPage and its subtypes, CollectionPage, AboutPage, ContactPage, SearchResultsPage, QAPage. A page is one kind of page, which is why these rarely stack. A page is not both an about page and a search results page.
Thing-level types answer "what is described here?" Article, Recipe, Product, Person, Organization, Event, Service. These stack freely, because a page can genuinely describe an article, its author and its publisher all at once.
The pattern that follows is the one in the example below: one page-level node, holding a mainEntity that points at the primary thing, plus however many thing-level nodes the page honestly contains.
This also explains the QAPage limit without having to memorise it. QAPage is page-level, and a page is one page, so of course there is only one. The same instinct catches most mistakes before you have to look up a rule.
The two types with hard limits
Here is the part that is genuinely obscure, and it is the reason a page can be "correctly" stacked and still be wrong.
WebSite: once per site, on the home page only. Google's site names documentation is explicit:
If you already have
WebSitestructured data on your site, make sure that you nest the site name properties in the same node. In other words, avoid creating an additionalWebSitestructured data block on your home page if you can help it.
And the placement rule is narrower than most people implement: the markup belongs on the home page, meaning the domain or subdomain root, not on subdirectory pages. A WebSite block emitted site-wide by a template on every page is a common and incorrect pattern. If you already have one from a plugin, add to that node rather than publishing a second.
QAPage: one per page. From Google's QAPage documentation:
There must only be one
QAPagetype definition per page.
Straightforward, and easy to breach on a forum or community template that loops over threads.
These are the two with published limits. For everything else the constraint is not a count but honesty: does the page actually contain this thing?
A page that legitimately carries five types
An article with a named author, on a site with a breadcrumb trail. Nothing exotic, and every node earns its place.
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebPage",
"@id": "https://example.com/blog/repiping-a-terrace",
"url": "https://example.com/blog/repiping-a-terrace",
"mainEntity": { "@id": "https://example.com/blog/repiping-a-terrace#article" },
"breadcrumb": { "@id": "https://example.com/blog/repiping-a-terrace#breadcrumb" }
},
{
"@type": "Article",
"@id": "https://example.com/blog/repiping-a-terrace#article",
"headline": "How to Repipe a Victorian Terrace",
"datePublished": "2026-08-14",
"author": { "@id": "https://example.com/about#priya" },
"publisher": { "@id": "https://example.com/#organization" }
},
{
"@type": "Person",
"@id": "https://example.com/about#priya",
"name": "Priya Raman"
},
{
"@type": "Organization",
"@id": "https://example.com/#organization",
"name": "Example Plumbing"
},
{
"@type": "BreadcrumbList",
"@id": "https://example.com/blog/repiping-a-terrace#breadcrumb",
"itemListElement": []
}
]
}Read what each one is doing. The WebPage is the page itself and names its main entity. The Article is the point of the page. The Person and Organization are referenced, not repeated, so the same author and company on two hundred posts stay one author and one company. The BreadcrumbList describes navigation position, which our breadcrumb schema guide covers on its own.
Five types, one description, no duplication. Nothing here is stacking for the sake of it, and removing any of them would leave something true unsaid.
The same shape on a product page
The types change, the structure does not:
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebPage",
"@id": "https://example.com/shop/copper-pipe",
"mainEntity": { "@id": "https://example.com/shop/copper-pipe#product" }
},
{
"@type": "Product",
"@id": "https://example.com/shop/copper-pipe#product",
"name": "15mm Copper Pipe, 3m",
"brand": { "@id": "https://example.com/#organization" },
"offers": {
"@type": "Offer",
"price": "14.50",
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock"
}
},
{
"@type": "BreadcrumbList",
"@id": "https://example.com/shop/copper-pipe#breadcrumb",
"itemListElement": []
}
]
}One page-level node, one primary thing, one supporting structure, and the brand referenced rather than restated. Note that Offer is nested inside the Product rather than given its own @id: an offer has no independent existence that another page needs to point at, so nesting is the honest shape. Reserve identifiers for things that get referenced from elsewhere.
Nest it, or give it an identifier?
Once a page carries several types, this becomes the recurring decision, and there is a clean rule for it.
Nest a node inside its parent when the thing has no independent existence and nothing else will ever point at it. An Offer inside a Product, a PostalAddress inside an Organization, a NutritionInformation inside a Recipe. These are details of their parent, not things in their own right. Giving them identifiers adds noise and invites nothing.
Give a node its own @id when it is a real thing that other pages or nodes need to reference. Your organization, your authors, the primary entity of the page. The test is whether anything, anywhere on the site, would sensibly want to point at it.
Getting this backwards in either direction is common. Identifiers on every nested detail produce a graph full of nodes nobody references. Nesting your organization inline on every page produces two hundred copies of your company instead of one entity that two hundred pages point at, which is the failure the whole reference pattern exists to prevent.
When a type does not belong
The test is not "would this be eligible for something." It is "is this thing on the page."
If the page has no FAQ, it does not get FAQPage. Worth a specific note, because FAQPage is the type most often added speculatively, and the reason people added it has gone: Google retired the FAQ rich result, though the markup remains valid schema.org. Our post on FAQ schema status covers what that does and does not mean. Adding it to a page with no questions was never right and now does not even pay.
If the page is not a product page, it does not get Product. The commonest version of adding a type for its rich result rather than for what the page is.
If your content is not a list, it does not get ItemList. An ItemList describes an actual enumeration of things, not a page that happens to contain several paragraphs.
If nothing on the page is a review you collected legitimately, it does not get Review. Review markup has its own eligibility rules that a type declaration does not override.
The underlying principle is one Google states plainly elsewhere and it applies to every type you consider adding: structured data should be a true representation of the page's content. A type is a claim. If the claim is not true of the page, the correct number of that type is zero.
Common mistakes
Emitting WebSite on every page. It belongs on the home page, once. This is usually a template doing it site-wide without anyone deciding to.
Two blocks describing the same entity with different identifiers. The classic two-plugin problem, and the fastest way to turn one company into two.
Adding a type because it might produce a rich result. Eligibility follows from the page being that thing, not from the declaration.
No main entity on a page where it is ambiguous. If two types could each plausibly be the point of the page, say which one is.
References that point nowhere. An author or publisher naming an @id that no node defines. Deleting the reference is a legitimate fix if you are not going to define the entity.
Assuming one script block is required. Multiple blocks are fine. What matters is that they do not contradict each other.
Stacking types onto a thin page. Five types describing a page with two paragraphs is a signal that the markup is doing work the content is not.
How to audit a page's type stack
1. Count the blocks and list the types. View source, find every application/ld+json, and write down every @type in each. This takes two minutes and is usually where the surprise is.
2. Ask what each type claims, and check the page. For each one, find the thing on the page it describes. Any type you cannot point at is the one to remove.
3. Collect the identifiers. List every @id that is declared and every @id that is referenced. Anything referenced but never declared is a dangling reference. Anything declared but never referenced may be fine, or may be an orphan doing no work.
4. Look for the same thing twice. Two nodes with the same name and different identifiers is a duplicate entity, and it is the most common defect on pages built by more than one tool.
5. Check WebSite placement specifically. It should appear on your home page and nowhere else.
6. Confirm one type is clearly the main focus, and add mainEntity if the page could be read either way.
7. Validate, and read both results. The Rich Results Test tells you what Google could render, which is only ever a subset. validator.schema.org checks the vocabulary for everything else, including the types that produce nothing visual and are still doing entity work. See our common schema errors for what the validator messages actually mean.
Frequently Asked Questions
AI Schema Gen builds the whole page as one connected graph, so the types on a page reference each other instead of competing, and nothing points at an entity that does not exist. Start free and see what your pages currently declare.
Is your site ready for AI?
Get a free readiness score in under a minute. No signup, no card.
Run the free check