A basic Product and Offer
For a product page where customers can purchase the item, Google recommends merchant listing markup. The Offer price, currency, availability and URL should agree with the visible page and checkout. Place the JSON inside a <script type="application/ld+json"> element in the product page HTML. This compact sample deliberately omits reviews and ratings rather than inventing them:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Trail mug, 350 ml",
"image": ["https://shop.example/products/trail-mug.jpg"],
"description": "Double-wall steel camping mug.",
"sku": "TM-350",
"brand": { "@type": "Brand", "name": "North Trail" },
"offers": {
"@type": "Offer",
"url": "https://shop.example/products/trail-mug",
"priceCurrency": "USD",
"price": "24.00",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
}
}
For an eligible Google merchant listing, the product’s name and an Offer with a price are important required data. Use a decimal point and ISO 4217 currency code. Google’s documentation explains merchant listing properties and the Product feature choices. Valid markup only makes a page eligible; Google decides whether and how a rich result appears.
A ProductGroup with variants
When options such as size or color are genuinely distinct purchasable variants, Google documents Product variant markup using ProductGroup and hasVariant. Give each variant its real attributes and Offer. The excerpt below is illustrative; production markup should include the required identifying properties and complete page URLs for your setup:
{
"@context": "https://schema.org",
"@type": "ProductGroup",
"name": "Trail shirt",
"productGroupID": "TS-BASE",
"variesBy": ["https://schema.org/size"],
"hasVariant": [
{
"@type": "Product",
"name": "Trail shirt, size M",
"sku": "TS-M",
"size": "M",
"offers": {
"@type": "Offer",
"url": "https://shop.example/products/trail-shirt?variant=medium",
"priceCurrency": "USD",
"price": "38.00",
"availability": "https://schema.org/InStock"
}
},
{
"@type": "Product",
"name": "Trail shirt, size L",
"sku": "TS-L",
"size": "L",
"offers": {
"@type": "Offer",
"url": "https://shop.example/products/trail-shirt?variant=large",
"priceCurrency": "USD",
"price": "38.00",
"availability": "https://schema.org/OutOfStock"
}
}
]
}
See Google’s Product variant structured data guide for variant URL and property requirements. Do not use AggregateOffer as a shortcut for a group of variants; Google explicitly distinguishes aggregate offers from variants.
Shipping and return details
Shipping information belongs on the relevant Offer as shippingDetails; a product-level return exception belongs in hasMerchantReturnPolicy. Example values below describe one fictional US product and must be replaced with your actual policy:
"offers": {
"@type": "Offer",
"url": "https://shop.example/products/trail-mug",
"priceCurrency": "USD",
"price": "24.00",
"availability": "https://schema.org/InStock",
"shippingDetails": {
"@type": "OfferShippingDetails",
"shippingRate": {
"@type": "MonetaryAmount",
"value": "5.00",
"currency": "USD"
},
"shippingDestination": {
"@type": "DefinedRegion",
"addressCountry": "US"
}
},
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "US",
"returnPolicyCategory":
"https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnFees": "https://schema.org/FreeReturn",
"returnMethod": "https://schema.org/ReturnByMail"
}
}
Google recommends a standard store return policy at the Organization level when it applies broadly; use product-level policy only for a real exception or when no standard policy exists. Review the current shipping and return requirements before publishing. Do not mark up a free return if customers pay return postage, or copy a one-size policy across countries where it differs.
Product schema without a price
Do not insert a guessed, zero, “from” or temporary price to silence a warning. If the product truly has no offer price on the page, omit offers rather than claiming one. A product snippet may still be eligible with qualifying review or aggregate-rating data, but a merchant listing that describes a purchasable product requires price information. If there is no genuine review data either, there may be no Product rich result eligibility to claim. Google’s product snippet requirements allow a review, aggregate rating, or offer for the snippet feature; its merchant listing feature has its own required Offer details.
For subscription, quote-based or variable-price products, model the real pricing arrangement supported by Google’s current documentation and visible to users. If the product is genuinely free, zero is different from an unknown price, but Google says merchant listing experiences require a price greater than zero. Do not expect a free item with a zero-price Offer to qualify for that feature.
Validate the page
- Confirm the JSON parses and appears in the fetched page HTML.
- Run the URL through Google’s Rich Results Test and inspect both errors and warnings.
- Compare the marked-up name, image, selected variant, price, currency, inventory, delivery and returns with the visible page and checkout.
- Check Merchant Center separately where you use a feed; page markup and feed data are distinct inputs.
- After edits, inspect the live URL again and watch Search Console for recrawl and structured data reports.
Google says structured data creates eligibility, not a guarantee of appearance. For Shopify theme behavior and the variant implementation context, read Product JSON-LD for Shopify and the ProductGroup variants guide.
Shipwork reads public product HTML and reports Product JSON-LD fields and nested variant markup it can observe. Free, no account, no signup. Paste your store address.
Check my product markupQuestions
Can Product schema have no price?
Should shippingDetails and returns be added to every product?
Does valid JSON-LD guarantee a rich result?
Can AggregateOffer describe size variants?
Keep reading