Version 1.1 · Last verified
This blueprint ships two verified Product JSON-LD skeletons you can copy, a single product with shipping and returns and a multi-variant ProductGroup, and it explains which fields an AI shopping agent actually reads to select you.
Key takeaways
- The fields an agent can match and compare carry the selection weight: identifiers (gtin, sku, mpn), price, availability, shipping, and returns. Lead with those; brand prose can wait.
- Two skeletons ship here verbatim, a single product with shipping and returns and a multi-variant ProductGroup. Copy one, swap in your data, then validate before shipping.
- For products that vary by color, size, or material, Google documents ProductGroup with hasVariant as the compact representation, so shared fields are stated once instead of repeated on every variant.
- The GTIN in the single-product skeleton is a checksum-valid placeholder, and neither skeleton has been run through a validator yet, so validate both and replace the GTIN before you ship.
Which fields carry decision weight
An agent does not read Product JSON-LD to admire your copy. It reads it to answer a shopper's query and to line you up against competing offers, so the fields worth optimizing are the ones it can match and compare.
Google's merchant listing documentation defines the Product and Offer core an agent reads: name, image, description, sku, mpn, a GTIN property (gtin13 for a 13-digit code, or the generic gtin), and brand, alongside an offers object carrying url, priceCurrency, price, priceValidUntil, availability, and itemCondition.Spec-factGoogle, Merchant listing (Product) structured data The availability value is written as a canonical full schema.org URL, for example https://schema.org/InStock (opens in new tab)(opens in new tab), drawn from the ItemAvailability enumeration that also includes OutOfStock, BackOrder, PreOrder, PreSale, SoldOut, LimitedAvailability, and Discontinued.Spec-factschema.org, ItemAvailability For itemCondition, Google lists NewCondition, RefurbishedCondition, and UsedCondition, again as full schema.org URLs.Spec-factGoogle, Merchant listing (Product) structured dataThose are the field definitions. The ordering judgment is ours. Among these fields, the ones that plausibly carry the most selection weight are the ones an agent can match against a query and compare across stores: the identifiers that resolve the product to a single global identity, the price and its currency, the availability that says whether it can transact now, and the shipping and return terms that break ties between otherwise equal offers. Free-text description and brand voice are the least comparable, so they earn the least token budget.Hypothesis (our analysis) This is the same facts-per-token discipline the handbook argues for on the page itself, applied to the structured layer. See information density on machine surfaces for the underlying case.
Skeleton A: single product with shipping and returns
Copy this block, swap in your real values, and keep the shape. It carries the identifiers, the offer, an offer-level shipping model, and a return policy in one object.
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "Example Widget",
"image": ["https://example.com/photos/widget-1x1.jpg"],
"description": "One-line factual product description.",
"sku": "SKU-12345",
"mpn": "MPN-98765",
"gtin13": "9780201379624",
"brand": { "@type": "Brand", "name": "ExampleBrand" },
"offers": {
"@type": "Offer",
"url": "https://example.com/products/example-widget",
"priceCurrency": "USD",
"price": 29.99,
"priceValidUntil": "2026-12-31",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition",
"shippingDetails": {
"@type": "OfferShippingDetails",
"shippingRate": { "@type": "MonetaryAmount", "value": 3.49, "currency": "USD" },
"shippingDestination": { "@type": "DefinedRegion", "addressCountry": "US" },
"deliveryTime": {
"@type": "ShippingDeliveryTime",
"handlingTime": { "@type": "QuantitativeValue", "minValue": 0, "maxValue": 1, "unitCode": "DAY" },
"transitTime": { "@type": "QuantitativeValue", "minValue": 1, "maxValue": 5, "unitCode": "DAY" }
}
},
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "US",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
}
}
The two nested blocks are where most catalogs are thin, and they are exactly the tie-breakers. Google's merchant listing documentation defines the offer-level shipping shape as offers.shippingDetails of type OfferShippingDetails, carrying a shippingRate (a MonetaryAmount with value and currency), a shippingDestination (a DefinedRegion whose addressCountry is an ISO 3166-1 alpha-2 code), and a deliveryTime (ShippingDeliveryTime) that splits into handlingTime and transitTime, each a QuantitativeValue with minValue, maxValue, and unitCode DAY.Spec-factGoogle, Merchant listing (Product) structured data Google's return policy documentation (last updated 2025-12-10) defines offers.hasMerchantReturnPolicy of type MerchantReturnPolicy, with applicableCountry, a returnPolicyCategory enum (MerchantReturnFiniteReturnWindow, MerchantReturnNotPermitted, or MerchantReturnUnlimitedWindow), returnMethod such as ReturnByMail, and returnFees such as FreeReturn; merchantReturnDays is an integer and is required when the category is a finite window.Spec-factGoogle, Return policy structured data
We lead this skeleton with the identifiers and the offer, then shipping, then returns, because an agent resolves what the product is before it weighs how the product ships and how returns work. JSON-LD key order means nothing to a parser, so this sequence is for the humans who maintain the file; the load-bearing decision is which fields you populate, not their order.Hypothesis (our analysis)Skeleton B: multi-variant products (ProductGroup)
When a product varies by color, size, or material, do not emit one bloated Product per variant with every shared field repeated. Group them.
{
"@context": "https://schema.org/",
"@type": "ProductGroup",
"name": "Example T-Shirt",
"description": "Cotton t-shirt.",
"url": "https://example.com/products/example-tshirt",
"brand": { "@type": "Brand", "name": "ExampleBrand" },
"productGroupID": "TSHIRT-001",
"variesBy": ["https://schema.org/color", "https://schema.org/size"],
"hasVariant": [
{
"@type": "Product",
"sku": "TSHIRT-001-RED-M",
"name": "Example T-Shirt, Red, M",
"color": "Red",
"size": "M",
"inProductGroupWithID": "TSHIRT-001",
"offers": {
"@type": "Offer",
"url": "https://example.com/products/example-tshirt?variant=red-m",
"priceCurrency": "USD",
"price": 19.99,
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
}
}
]
}
The hasVariant array holds one Product per real variant; add more objects to it for each color and size permutation you sell. Google's product variant documentation (last updated 2026-05-20) documents ProductGroup with a productGroupID, a variesBy array naming the varying attributes (for example schema.org/color and schema.org/size), and a hasVariant array of Product objects, each carrying its varying attribute values, an inProductGroupWithID back-reference, and its own Offer; Google describes nesting variants under hasVariant as the most compact and natural representation.Spec-factGoogle, Product variant structured data
Before you ship
- Paste each skeleton into Google's Rich Results Test and into validator.schema.org, and resolve every error and warning before it goes near a product template.
- Replace
9780201379624with the product's real GTIN, and confirm the check digit is correct so validation passes. - Confirm
availabilityanditemConditionuse full schema.org URLs, not bare strings likeInStock. - For variant products, check that every entry in
hasVariantcarries its ownoffers, its varying attributes, and aninProductGroupWithIDthat matches the group'sproductGroupID. - Diff the facts in the JSON-LD against the visible page. Any price, availability, or policy that appears in one and not the other is an equivalence problem, and the fix is to make them match.
Related blueprints and chapters
These skeletons are the structured layer of a bigger picture. Pair this with Product schema for AI shopping for the full field walkthrough and the eligibility rules, and read information density on machine surfaces for why the fields an agent can match earn the token budget. When you also serve a Markdown copy of the page, keep the same facts in both by following the product Markdown mirror blueprint, and see the machine-readability score for how all of this rolls up into one measure of how usable your data is to an agent.
- 2026-07-08, v1.1: The validation guidance is complete; the skeletons remain unvalidated until you run them through the Rich Results Test and validator.schema.org.
- 2026-07-08, v1.0: Initial publication.