How-to
Product data AI agents can read: Schema.org fixes that work
What complete Product and Offer markup looks like, the mistakes that hide products from AI shopping agents, and how to test the fix across a catalogue.
The tragent teamPublished 8 min read
A shopper asks an assistant for "a waterproof trail shoe under $150, in stock in size 10." The assistant reads dozens of product pages in seconds. It does not look at your photography or your layout. It looks for facts it can trust: a name, a price with a currency, an availability value, a delivery cost. Where those facts are machine-readable, your product is a candidate. Where they are not, it is skipped.
This guide shows what complete product markup looks like, the mistakes that most often hide products from agents, and how to test the fix.
Why does product structured data matter to AI agents?#
Because it is the only part of a product page an agent can read reliably and quickly. Agents can parse visible text, but text is ambiguous: "$120" might be the price, the was-price, a bundle price or a shipping threshold. Structured data says which.
Schema.org markup, usually embedded as a JSON-LD block, has carried this information for search engines for years. AI agents use it the same way, and so do the feeds that put products into AI assistants. Google's own guidance for merchant listings requires a product name, an image, and an Offer with a price and priceCurrency, and recommends availability, shipping and returns details.
The difference with agents is the cost of a gap. A search engine with incomplete data shows a plainer result. An agent with incomplete data cannot complete its task, so it moves to a store whose data is complete.
What does an agent need from a product page?#
Identity, price, stock, and the cost of delivery and returns, each stated as data and each matching what a shopper sees.
| Question an agent asks | Schema.org property | If it is missing |
|---|---|---|
| What is this exactly? | name, image, brand, gtin or mpn, sku | The agent cannot match it to the shopper's request, or to the same product elsewhere |
| What does it cost? | offers.price, offers.priceCurrency | The agent cannot compare it, so it is dropped from price-led searches |
| Can I buy it now? | offers.availability (an ItemAvailability value) | The agent risks recommending something you cannot sell, so it hedges or skips |
| What will delivery cost? | offers.shippingDetails | The agent cannot give a total, so "under $150 delivered" is unanswerable |
| Can it be returned? | offers.hasMerchantReturnPolicy | The agent cannot answer one of the most common pre-purchase questions |
What does complete Product markup look like?#
One JSON-LD block per product page, with a Product, its Offer, and the facts above. Step through each part:
1<script type="application/ld+json">2{3"@context": "https://schema.org",4"@type": "Product",5"name": "Ridgeline Waterproof Trail Shoe",6"image": ["https://shop.example/img/ridgeline-1x1.jpg"],7"description": "Waterproof trail running shoe with a 6 mm lug outsole.",8"brand": { "@type": "Brand", "name": "Ridgeline" },9"sku": "RL-TRAIL-WP-10",10"gtin13": "0012345678905",11"offers": {12 "@type": "Offer",13 "url": "https://shop.example/products/ridgeline-trail",14 "price": "129.00",15 "priceCurrency": "USD",16 "availability": "https://schema.org/InStock",17 "itemCondition": "https://schema.org/NewCondition",18 "shippingDetails": { "@id": "https://shop.example/#shipping" },19 "hasMerchantReturnPolicy": { "@id": "https://shop.example/#returns" }20}21}22</script>Step 1: One block, in the page's HTML. A single application/ld+json script describing the product on this page. It belongs in the HTML the server sends, so an agent that does not run JavaScript still sees it.
Step 2: What the product is. A specific name, a crawlable image, a description and a brand. Use the name a shopper would recognise, not an internal code.
Step 3: Identifiers that match it elsewhere. A GTIN (or an MPN when there is no GTIN) lets an agent recognise the same product across stores and compare like with like. The SKU identifies it in your catalogue.
Step 4: Price and currency, together. The price as a number in a string, with no currency symbol, and a three-letter ISO 4217 currency. A price without a currency is not comparable, and it must equal the price shown on the page.
Step 5: Stock, as a value an agent understands. availability must be a schema.org ItemAvailability URL such as InStock, OutOfStock, PreOrder or BackOrder. Text like 'In stock!' or 'available' is not understood.
Step 6: Delivery and returns. Shipping and return policy, here referenced from site-wide definitions. Google recommends a global policy on your Organization, with offer-level details only where a product differs.
The values here are an example. Use your own real ones, and never publish placeholders: markup that says "price": "0.00" or a stock status you did not check states something false about your catalogue, to every agent that reads it.
Which mistakes stop agents most often?#
A missing price block, a price that disagrees with the page, and availability written as text. These are the problems tragent's product-data checks look for first, each with a page explaining the fix:
- No
Offerat all. The product is described but not priced. Common on themes that output a bareProductwith name and image only. → Your product data has no price block - An
Offerwith noprice, or no currency. Often because a template variable was empty for products with variants. → Your product data has no price, no currency - Markup price differs from the page. A sale price shown to shoppers while the markup still carries the full price, or the reverse. → Your product data and your page disagree on price
- Availability as free text.
"availability": "in stock"instead ofhttps://schema.org/InStock. → Your stock status is not a value agents understand - Invalid JSON. A stray quote in a product description breaks the whole block, so the agent sees nothing. → Your product data block cannot be read
- No product markup on the page at all. → No machine-readable product data
The second and third are the dangerous ones, because the page looks fine to everyone who checks it by eye.
How should products with variants be marked up?#
Describe the variant the page is showing, or describe the family with ProductGroup and its variants. A shoe in five sizes and three colours is fifteen offers, and an agent asked for "black, size 10" needs to find that one.
Two approaches work:
- One product per variant page. If each variant has its own URL, give each page a
Productwith that variant's price, stock and identifiers. - A
ProductGroupwithhasVariant. For one page with selectable options, describe the family as aProductGroupwithvariesBy(for examplesizeandcolor) and list each variant as aProductunderhasVariant, each with its ownOffer. Google supports this pattern for product variants.
What breaks agents is the middle ground: one Offer whose price is the cheapest variant's, and availability that is InStock because one size is. The agent recommends a size you cannot ship.
Should the markup come from the server or from JavaScript?#
From the server, in the initial HTML. Google's merchant listing guidance says dynamically generated markup can make shopping crawls less frequent and less reliable, and recommends putting Product markup in the initial HTML. Agents are less forgiving still: many fetch the HTML and read it without running scripts.
If your storefront renders on the client, render the JSON-LD on the server even if the visible page is built later. On most commerce platforms this is a theme template, not application code.
How do you test product structured data?#
Validate the markup, then compare it with the page, across many products, not one. A single product that passes proves the template can work, not that every product's data is complete.
- Validate syntax and types. Google's Rich Results Test checks Google's requirements; the Schema Markup Validator checks against Schema.org.
- Check the HTML the server sends. View the page source, not the browser's inspector. If the JSON-LD only appears in the inspector, it is being injected by JavaScript.
- Compare with what shoppers see. For a sample of products across your catalogue, including discounted and out-of-stock ones, confirm the markup's price and availability match the page.
- Re-test after theme and app changes. Theme updates and apps that touch product pages are the usual way markup breaks. A check in your deploy pipeline catches it; see how to test your store with AI shopping agents in CI.
Frequently asked questions
- Do AI shopping agents use Schema.org structured data?
- Yes. Structured data is the most reliable way for an agent to read a product's name, price, currency and availability without guessing from page text, and the same facts feed the product listings in AI assistants.
- Which product properties are required?
- For Google merchant listings, a Product needs name, image and offers, and the Offer needs price and priceCurrency. Availability, shipping details, return policy, brand and a GTIN are strongly recommended for agents.
- Is JSON-LD better than microdata for AI agents?
- JSON-LD is easier to keep complete and valid because it sits in one block, separate from the page layout. Google supports both; most commerce platforms output JSON-LD.
- What value should availability have?
- A schema.org ItemAvailability URL, such as https://schema.org/InStock, OutOfStock, PreOrder or BackOrder. Free text such as 'in stock' is not understood reliably.
- Can structured data that disagrees with the page hurt me?
- Yes. An agent that finds one price in the markup and another on the page has no way to know which is true, and Google Merchant Center flags products whose data disagrees with their landing page.
Sources
- Merchant listing (Product, Offer) structured data — Google Search Central
- Product variant (ProductGroup, Product) structured data — Google Search Central
- Product — Schema.org
- Offer — Schema.org
- ItemAvailability — Schema.org
- Rich Results Test — Google
- Schema Markup Validator — Schema.org
Written by the tragent team and checked against the sources listed above on October 19, 2026. See how we write and correct posts in our editorial policy.