There is no single correct schema stack for a SaaS pricing page. The right model depends on three things you have to settle before writing any JSON-LD: what the page is actually about, whether your plans are commercial offers for one product or genuinely separate products, and which named Google Search feature - if any - you are trying to become eligible for. The three Google features that touch pricing have different and partly incompatible requirements, and satisfying one of them is not the same thing as modelling your pricing correctly. This page settles the type selection. It does not settle what happens after that, and it cannot tell you which type gets your page quoted in an AI answer - a question addressed further down.
Two labels mark evidence boundaries on this page. Documented means the guidance that follows is directly supported by published Schema.org or Google documentation, quoted. Recommendation means a modelling choice, threshold or workflow that CoreAEX prescribes and the documentation does not. Statements from platform staff in interviews, forums or podcasts are attributed in the prose rather than tagged - they are first-party, but they are not documentation. Untagged text is ordinary explanation or a conclusion following from something already labelled.
Start with the Entity, Not the Markup
Before choosing a type, name the things on the page and how they relate. For most B2B SaaS pricing pages there are five, and conflating any two of them is where bad markup starts:
- The application - the software itself.
- The service - what the customer is actually buying access to, which for SaaS is usually a subscription to use the application rather than a copy of it.
- The plans - Starter, Growth, Enterprise. Commercial packages, each with its own price and included scope.
- The seller - the legal entity taking the money, which is not always the brand name in the header.
- The page - a pricing page is usually a comparison surface for several plans, not a page about one purchasable item. That distinction matters more than it looks.
Documented Google's general structured data guidelines are direct about the relationship between markup and page: Put the structured data on the page that it describes, unless specified otherwise by the documentation
, and Don't mark up content that is not visible to readers of the page
. Both rules bite on pricing pages specifically, because pricing pages hide things: annual rates behind a monthly toggle, per-seat minimums in a footnote, enterprise pricing behind a form.
Two Questions, Not One: Validity and Eligibility
"Is my schema right?" is two separate questions that have separate answers, and a page can pass either one while failing the other.
Schema.org validity asks whether your markup parses and uses the vocabulary as defined - whether the types exist, whether the properties are declared on those types, whether the values are the right kind of thing. Google eligibility asks whether your markup meets the documented requirements for one named Google Search feature. The vocabulary is large; the set of things Google documents consuming is much smaller. Perfectly valid markup can be eligible for nothing, and markup that clears a Google feature's requirements can still describe your commercial terms badly.
Documented Eligibility is also not display. Google's guidelines make the feature-level requirement explicit - Specify all required properties listed in the documentation for your specific rich result type
- and eligibility can be withdrawn: FAQ rich results were supported for years and are no longer listed in Google's structured data gallery as of its June 15, 2026 update. Product and Software app are both still listed.
Documented There is a third question people usually mean to ask, and it is worth answering plainly here so the rest of the page isn't read as an answer to it. Choosing a schema type is not a lever for getting cited in AI answers. Google's current guidance says so in its own words: Structured data isn't required for generative AI search, and there's no special schema.org markup you need to add
, followed immediately by However, it's a good idea to continue using it as part of your overall SEO strategy, as it helps with being eligible for rich results on Google Search
(last updated July 10, 2026). That is the whole documented position: eligibility yes, generative-AI requirement no. Google's separate AI features page adds that Making sure your structured data matches the visible text on the page
remains among the SEO fundamentals worth doing. Neither sentence licenses a type choice made to win citations, and the evidence on whether schema affects AI citation at all is covered on its own page in this cluster rather than summarised here.
SoftwareApplication
Documented Schema.org defines SoftwareApplication in three words: A software application.
Its position in the class hierarchy is the part that surprises people. It is Thing > CreativeWork > SoftwareApplication. It is not a subtype of Product. (Verified against the published Schema.org vocabulary, version 30.0, released March 19, 2026.)
That does not stop it carrying commercial terms. The offers property is declared on CreativeWork, Product and Service alike, so a SoftwareApplication node can hold one or many Offer nodes without any type gymnastics.
Documented Google's requirements for the Software app rich result are where a typical B2B SaaS pricing page runs into a wall. Google's documentation requires three things: name, offers.price, and either aggregateRating or review. The rating or review is not optional, and it is not one of several alternatives - it is a required element alongside the price. Google also documents the free case directly: If the app is available without payment, set offers.price to 0
.
Recommendation So a SaaS pricing page with accurate, complete price markup and no ratings on it is not eligible for the Software app rich result. That is worth sitting with, because it is the exact point at which teams start inventing review data. Don't. Google's guidelines prohibit marking up irrelevant or misleading content, such as fake reviews or content unrelated to the focus of a page
, and structured-data abuse can cost a page its rich-result eligibility or attract a manual action. If you hold genuine, policy-compliant review data and it is displayed on the page, use it. If you don't, accept that this particular rich result is not available to you and choose your model on other grounds - the markup still describes the entity correctly, which is the durable reason to have it.
Recommendation Use SoftwareApplication when the page is genuinely about the application: a product page, a feature page, an app's own landing page. On a page whose subject is the commercial packaging rather than the software, it is a defensible but weaker fit.
Product and Service
Documented Advice in this area frequently claims that Product is wrong because software is not a physical product. Schema.org's own definition says otherwise, explicitly: Any offered product or service. For example: a pair of shoes; a concert ticket; the rental of a car; a haircut; or an episode of a TV show streamed online.
Three of the five examples are intangible. Product covers offered services by definition.
There is a real tension in the vocabulary here, and it is better to name it than to paper over it. The Product definition text covers services, but the class hierarchy puts Service somewhere else entirely: Thing > Intangible > Service. Service is not a subclass of Product, and Product is not a subclass of Service. They are independent branches - and not symmetric ones: Product sits directly under Thing, while Service sits two levels down, under Intangible. So the sentence "a service is a kind of product" is true of the definition text and false of the graph, and you should not write markup that depends on it being true of the graph.
What makes the choice tractable is that all three candidate types can carry offers, so the decision is about what you are describing rather than about where the price can go:
SoftwareApplication- the software itself, as a thing with a version, a category and an operating system.Product- the offered thing, tangible or not. The broadest defensible fit for a commercial page.Service- a service provided by an organisation. Fits managed, done-for-you or professional-services tiers better than it fits self-serve software.
Documented Google documents two distinct features for Product markup, and the difference between them decides whether Product is a realistic choice for your page.
Product snippets are the reason Product is often the pragmatic answer on a pricing page. They apply to pages that focus on a single product (or multiple variants of the same product)
and do not require the page to be purchasable. The requirement is name plus one of review, aggregateRating or offers - so offers alone satisfies it, with no ratings needed. A price of 0 is permitted.
Merchant listings are the harder gate. Google states that Only pages where a shopper can purchase a product are eligible for merchant listing experiences, not pages with links to other sites that sell the product
, and that Unlike product snippets, merchant listing experiences require a price greater than zero.
The test is the transaction, not the wording on the button. A "Talk to sales" page normally does not satisfy the purchasability gate, since the flow produces a conversation rather than a purchase. "Start free trial" is not decisive on its own: some trial flows take a card and complete a self-serve purchase for the marked-up offer, and others create a free account with no transaction at all. Treat the CTA as a screening signal, then follow the actual path and check whether a shopper can complete a purchase, with a visible price and checkout terms, for the offer you are marking up. A requirements checklist that can be technically satisfied does not make the page eligible in substance.
One boundary worth stating because we checked it and found nothing: Google's merchant listing documentation contains no sentence excluding services or subscriptions by name. The constraint it does state is purchasability, plus a price greater than zero and a required image property. Do not repeat the widely circulated claim that Google's documentation bars subscriptions from merchant listings - as of this page's review date, that sentence is not in the documentation.
Recommendation Choosing Product because it is the easiest route to a rich result, on a page that is not really about a product, is the failure mode this whole section exists to prevent. Google's relevance guideline is the governing rule: structured data must accurately represent the page's content. Pick the type that describes the page; then find out which feature that makes you eligible for. Not the reverse.
Offer
Documented Offer describes commercial terms for an item. It does not replace the item. Schema.org's definition: An offer to transfer some rights to an item or to provide a service - for example, an offer to sell tickets to an event, to rent the DVD of a movie, to stream a TV show over the internet, to repair a motorcycle, or to loan a book.
The vocabulary adds a note that matters for anything sold rather than leased: As the businessFunction property, which identifies the form of offer (e.g. sell, lease, repair, dispose), defaults to http://purl.org/goodrelations/v1#Sell; an Offer without a defined businessFunction value can be assumed to be an offer to sell.
The property that connects an offer to its item is itemOffered, whose declared range includes Product, Service and CreativeWork - which is how an Offer can point at a SoftwareApplication. In practice you will more often go the other direction, attaching offers to the item, which is equivalent and easier to read.
This is where plan tiers live. Starter, Growth and Enterprise are three sets of commercial terms for access to one application. Modelling them as three Offer nodes on one item preserves what they actually are, keeps each plan's name and URL attached to its own price, and does not require you to decide whether a plan is a separate product.
Recommendation The rule that matters most on a B2B pricing page concerns the plan you cannot price. A price of 0 means the offer is free. It does not mean the price is unknown. For an enterprise or contact-sales tier, do not write 0, do not invent a range, and do not guess an availability status that implies self-serve purchase. An Offer with a name, a URL, a seller and no price is valid vocabulary; it simply isn't eligible for the price-based Google features, which is the accurate outcome rather than a problem to be engineered around.
AggregateOffer, and Why a Pricing Table Usually Isn't One
Documented This is the most frequently misapplied type in SaaS pricing markup, and the vocabulary is unusually specific about what it is for. Schema.org defines it as: When a single product is associated with multiple offers (for example, the same pair of shoes is offered by different merchants), then AggregateOffer can be used.
Read that definition precisely, because it is easy to over-read in either direction. The condition it states is a single product associated with multiple offers. The different-merchants case is the illustration Schema.org chose, introduced by "for example" - it is not a required condition, and a price range across several offers on one item is not invalid markup.
The objection to using it for a plan table is therefore a modelling objection, not a vocabulary one. A pricing table where Starter is $29 and Growth is $79 does describe multiple offers associated with one product. But compressing them into a lowPrice of 29 and a highPrice of 79 discards exactly what the page exists to communicate: which plan costs what, what each one includes, and where each one's signup lives.
Recommendation Use separate Offer nodes when Starter, Growth and Enterprise need distinct names, URLs, prices or entitlements. An AggregateOffer range can be vocabulary-valid for multiple offers on one item, and Google's product snippet documentation accepts it; the reason to prefer separate offers is that they preserve plan-level detail, not that the range is forbidden. If you have a genuine reason to publish a range as well, publish it alongside the individual offers rather than instead of them.
The clearest fit for AggregateOffer in a SaaS context remains the multi-seller one: the same edition of your product listed by several resellers or marketplaces, aggregated on the page you are marking up.
Plans, Editions and Variants
The obvious next thought is that plan tiers are variants of one product, the way a t-shirt comes in three sizes. That intuition is not semantically absurd, but Google's variant feature does not support it - and the distinction between "Google doesn't document this" and "the vocabulary forbids this" is worth keeping, because they lead to different decisions.
Documented Google's product variant documentation (last updated May 20, 2026) describes variants as products sold in different variations (for example various sizes, colors, materials, or patterns)
, and the properties it supports for distinguishing them through variesBy are color, size, suggestedAge, suggestedGender, material and pattern. Every one is a physical-goods attribute. None expresses "includes SSO" or "up to 10 seats."
Documented Schema.org's own model is broader than Google's implementation of it. ProductGroup covers products that "vary only in certain well-described ways, such as by size, color, material etc." - "such as" introducing examples, not a closed list - and variesBy accepts DefinedTerm or Text, which does not restrict you to physical attributes. So the accurate conclusion is narrower than "plan tiers aren't variants": Google does not document plan entitlements as a supported variant dimension, and its feature additionally assumes directly selectable variants. Whether a software edition can be modelled as a ProductGroup in Schema.org terms is a separate question the vocabulary leaves open.
Documented Worth knowing before you build on it: ProductGroup, hasVariant and variesBy are all marked new
in Schema.org's vocabulary as of version 30.0 - recently added to the released core vocabulary, not unreleased or speculative. Schema.org's own framing: This term is in the "new" area - implementation feedback and adoption from applications and websites can help improve our definitions.
That is a lighter caveat than "pending" would imply: these terms are already part of the official release, just recent enough that their definitions may still be refined. Google documents its own support for them independently of that status, but the two things are separate and neither implies the other.
Recommendation Treat plan tiers as offers on one item by default. Treat editions as separate products only when they genuinely are separate products - different applications sold under one brand, each with its own page, its own feature set and its own identity, rather than three service levels of the same application. The test we apply: if a customer would describe them as "the same tool on a bigger plan," they are offers. If they would describe them as "a different product," they are products.
Decision Matrix
Each row names the semantic model first and the Google feature second, in that order deliberately. The "feature available" column reflects what the documentation requires, not what will appear.
| If the page is… | Model it as | Google feature its requirements can meet | Watch out for |
|---|---|---|---|
| A pricing page for one self-serve SaaS product with tiered plans | Product with several Offer nodes, one per plan | Product snippet - offers alone satisfies the "one of" requirement | Not a merchant listing: a trial or demo button is not a purchase |
| A product or feature page about the application itself | SoftwareApplication with offers | Software app - but only with genuine ratings or reviews displayed on the page | The rating or review is required, not recommended. Do not manufacture one |
| Quote-led, with no public price | Product or SoftwareApplication, with an Offer carrying name, URL and seller but no price | None of the price-based features | Don't write 0, a placeholder or an invented range for unknown pricing |
| A free plan alongside paid tiers | One item, several Offer nodes; the free plan's price is 0 | Product snippet; Software app if ratings exist | 0 must describe a genuinely free plan, not a time-limited trial |
| Several genuinely distinct applications under one brand | A separate Product or SoftwareApplication per page | Per page, as above | Not documented for Google's product-variant feature; use separate entity pages when these are genuinely distinct applications |
| A managed service or professional-services tier | Service with offers | No rich result for Service appears in Google's structured data gallery | Semantic fit can be right even when no feature is available. That is an acceptable outcome |
| Your edition as listed by several resellers, aggregated here | AggregateOffer over that one edition | Product snippet | The clearest fit for the type, though the vocabulary requires one item with multiple offers rather than multiple sellers specifically |
Documented One entry in Google's gallery that is not what its name suggests. Scanning Google's structured data gallery for something that fits a subscription business turns up a feature called "Subscription and paywalled content." It is not about subscription pricing. Its documentation covers marking paywalled articles with isAccessibleForFree so that gated content can be crawled and indexed without being treated as cloaking, and it mentions no price properties at all. It is a publisher feature, not a pricing feature.
Minimal Graph Patterns
These are conceptual skeletons for type selection, deliberately stripped of the billing detail a real subscription needs. Anything involving billing periods, seat minimums, usage tiers, trials, discounts or multiple currencies belongs on the complex-pricing page in this cluster, not here.
Pattern A - Application with Tiered Offers
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"@id": "https://example.com/#app",
"name": "Northwind Analytics",
"applicationCategory": "BusinessApplication",
"operatingSystem": "Web browser",
"url": "https://example.com/",
"offers": [
{
"@type": "Offer",
"name": "Starter",
"url": "https://example.com/pricing#starter",
"price": 29,
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"seller": { "@type": "Organization", "name": "Northwind Software Ltd" },
"priceSpecification": {
"@type": "UnitPriceSpecification",
"price": 29,
"priceCurrency": "USD",
"referenceQuantity": {
"@type": "QuantitativeValue",
"value": 1,
"unitCode": "C62",
"unitText": "user"
},
"billingDuration": 1,
"unitCode": "MON"
}
},
{
"@type": "Offer",
"name": "Growth",
"url": "https://example.com/pricing#growth",
"price": 79,
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"seller": { "@type": "Organization", "name": "Northwind Software Ltd" }
}
]
}
Vocabulary: valid against Schema.org v30.0 - every type and property exists and is declared on the type carrying it. Note: billingDuration is marked new
in Schema.org's vocabulary (already released, recently added), and no Google documentation reviewed for this page states that Google consumes it. It is included to show where the property belongs, not as a claim that anything reads it. Google eligibility: this pattern fails at least one documented Software app requirement, because it carries no aggregateRating or review. It may fail on a second count that the documentation does not settle: Google's Software app guide illustrates a single Offer and describes the property as An offer to sell the app
in a marketplace sense, without documenting how an array of SaaS plan offers is interpreted. Adding a rating would remove the first obstacle; it would not establish that this multi-offer shape is consumed as intended. Test the final live implementation before making any eligibility claim about it.
Pattern B - Product with a Single Offer
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Northwind Analytics",
"description": "Self-serve product analytics for B2B software teams.",
"url": "https://example.com/pricing",
"brand": { "@type": "Brand", "name": "Northwind" },
"offers": {
"@type": "Offer",
"name": "Starter",
"price": 29,
"priceCurrency": "USD",
"url": "https://example.com/pricing#starter",
"availability": "https://schema.org/InStock",
"seller": { "@type": "Organization", "name": "Northwind Software Ltd" }
}
}
Vocabulary: valid; no pending or new terms used. Google eligibility: meets the documented product snippet requirements - name plus offers as the "one of" element. It does not meet merchant listing requirements, which additionally need a purchasable page and image.
Pattern C - Contact-Sales Plan with No Public Price
{
"@context": "https://schema.org",
"@type": "Offer",
"name": "Enterprise",
"url": "https://example.com/pricing#enterprise",
"category": "Enterprise plan",
"seller": { "@type": "Organization", "name": "Northwind Software Ltd" },
"itemOffered": { "@id": "https://example.com/#app" },
"eligibleQuantity": {
"@type": "QuantitativeValue",
"minValue": 50,
"unitCode": "C62",
"unitText": "user"
}
}
Vocabulary: valid; no pending or new terms used. Google eligibility: none of the price-based features, by design - there is no price because there is no public price. This is the correct representation of a quote-led plan, and the absence of eligibility is the accurate result rather than a defect to be worked around.
Recommendation Note what this pattern omits. There is no availability. It would validate, and it is tempting as a completeness field, but availability is an assertion about a real commercial state - and asserting InStock for a plan you can only get by talking to a salesperson is the guess this page warns against two sections above. Add availability when the page visibly communicates a real status the publisher can keep current, and leave it out otherwise. The same restraint applies to every field you might add to look thorough.
Pattern D - the AggregateOffer Anti-Pattern
{
"@context": "https://schema.org",
"@type": "AggregateOffer",
"lowPrice": 29,
"highPrice": 79,
"priceCurrency": "USD",
"offerCount": 3,
"itemOffered": { "@id": "https://example.com/#app" }
}
Vocabulary: valid. Every property exists and is declared on AggregateOffer. That is precisely the point of including it. Why we would not ship it for a plan table: validity is not fit. Nothing in the vocabulary forbids this - three plans are three offers on one product, which is what AggregateOffer describes. The objection is that it discards plan names, plan URLs and the mapping between plan and price, which is the information the page exists to communicate. A validator will not flag any of that, which is the point of showing it.
How These Were Checked, and What Was Not Checked
Each pattern above was parsed as JSON and checked programmatically against the published Schema.org vocabulary release, version 30.0 (released March 19, 2026): every @type confirmed to exist as a class, every property confirmed to exist and to be declared on the type carrying it, every enumeration value confirmed, and every term checked for its release status (core, new, or pending).
Two things that check does not establish, stated because the distinction is the whole subject of this section. It does not establish Google feature eligibility, which is assessed above against each feature's documented required properties rather than by a validator. And it is not a Rich Results Test run: these patterns are illustrative and are not deployed at a live URL, and Google's own guidance is to test with a URL rather than pasted code. Run both checks against your own staged URL before release. The tooling split - which tool proves what - is covered by the implementation and validation page in this cluster.
What This Page Doesn't Settle
Type selection is the first decision, not the whole job. Three things this page deliberately leaves alone:
Modelling the commercial terms. Monthly versus annual billing, per-seat and per-unit pricing, usage tiers with allowances and overage, free trials as distinct from free plans, discounts and their validity dates, multiple currencies and tax treatment. Schema.org can express a good deal of this, several of the relevant properties are marked new in its vocabulary, and Google documentation consuming them is a separate question from whether they exist.
Documented Getting it onto the page reliably and keeping it true. JSON-LD placement and graph identifiers, generating markup and visible pricing from one source of truth, server versus JavaScript delivery, and what each validator actually proves. Google documents one caveat that bears directly on pricing, scoped specifically to Shopping crawls: Using Product markup? Be aware that dynamically-generated markup can make Shopping crawls less frequent and less reliable, which can be an issue for fast-changing content like product availability and price.
That is not a general statement about AI crawlers or about Google Search as a whole.
Whether any of this affects what AI answers say about your pricing. That is an empirical question, it is the subject of original CoreAEX research currently in progress, and it is not answerable from a type-selection page. Nothing above should be read as a claim about AI citation.
Not sure which model your pricing page needs?
If your plans don't fit cleanly into offers or products - usage-based billing, hybrid tiers, or a mix of self-serve and quote-led - that's a short conversation. Book a session.
Sources
Sources:
Vocabulary definitions and class hierarchies are from Schema.org, verified against the published vocabulary release version 30.0 (released March 19, 2026) and the corresponding term pages: Product, SoftwareApplication, Service, Offer, AggregateOffer. The new
status for ProductGroup, hasVariant, variesBy and billingDuration was confirmed on each term's own page, where Schema.org describes the new area as one where implementation feedback and adoption from applications and websites can help improve our definitions.
Google feature requirements are from Google Search Central: Software app structured data (last updated December 10, 2025), Product snippet (last updated December 10, 2025), Merchant listing (reviewed 28 August 2026; a specific last-updated date could not be independently confirmed for this page), Product variants (last updated May 20, 2026), General structured data guidelines (last updated July 10, 2026), Search gallery (last updated June 15, 2026), and JavaScript-generated structured data (last updated December 10, 2025).
Google's position on structured data and generative AI is from Optimizing for generative AI (last updated July 10, 2026) and AI features and your website (last updated December 10, 2025).
Schema.org's broader variant model is from ProductGroup and variesBy. Google's documentation is revised frequently - several of the pages above were updated during 2026 - so the last-updated dates are given for each rather than a single review date.
About the author
Zarko Zivkovic is the founder of CoreAEX, building technical SEO, AEO, and AI-visibility systems for B2B SaaS companies. Connect on LinkedIn.