Product Schema Markup: Required and Recommended Properties for Google
Which Product properties Google needs for product snippets and merchant listings, how to mark up price, availability, reviews and variants, and the mistakes that cost you the rich result.

Product markup is the structured data with the most visible payoff: price, availability, star rating and shipping details can all appear directly in Google Search. It is also the markup with the most rules, because Google runs two separate features on it: product snippets (price and reviews in regular results) and merchant listings (the shopping-style results with images and shipping). This guide covers what each one needs.
The minimum that works
Every product page needs a Product with a name and at least one of offers, review or aggregateRating. In practice you always want offers, because price and availability are what shoppers look at.
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Executive Anvil",
"image": [
"https://example.com/photos/anvil-1x1.jpg",
"https://example.com/photos/anvil-4x3.jpg",
"https://example.com/photos/anvil-16x9.jpg"
],
"description": "Sleeker than ACME's Classic Anvil, the Executive Anvil is perfect for the business traveller.",
"sku": "0446310786",
"brand": { "@type": "Brand", "name": "ACME" },
"offers": {
"@type": "Offer",
"url": "https://example.com/anvil",
"priceCurrency": "USD",
"price": 119.99,
"priceValidUntil": "2027-11-20",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
},
"aggregateRating": { "@type": "AggregateRating", "ratingValue": 4.4, "reviewCount": 89 }
}
Properties Google looks at
| Property | Product snippet | Merchant listing | Notes |
|---|---|---|---|
name |
required | required | Must match the visible product name |
image |
recommended | required | Absolute URLs; several aspect ratios help |
offers.price |
required for the price snippet | required | A number, not “$119.99” |
offers.priceCurrency |
required with price | required | ISO 4217 code such as USD, EUR |
offers.availability |
recommended | required | A schema.org URL such as https://schema.org/InStock |
aggregateRating / review |
one of these or offers |
optional | Ratings must be from real users on your site |
brand, sku, gtin |
recommended | recommended | Identifiers help Google match the product |
offers.shippingDetails |
ignored | recommended | Enables the shipping line in listings |
offers.hasMerchantReturnPolicy |
ignored | recommended | Enables the return line in listings |
Merchant listings additionally require that the page sells the product directly: the offer must be yours, with a buy button on the page. Affiliate or comparison pages get product snippets only.
Price done right
price is the field most often broken by templates. The value must be a plain number with a dot as the decimal separator: 119.99, not "119,99", not "$119.99", not "119.99 USD". Put the currency in priceCurrency. If the product has a price range, use AggregateOffer with lowPrice and highPrice instead of inventing a string.
Sale prices are a common trap. Google wants the price the customer pays now in price. If you want to show the old price too, use priceSpecification with a UnitPriceSpecification whose priceType is https://schema.org/ListPrice.
Availability and condition
Use the full schema.org URLs: https://schema.org/InStock, OutOfStock, PreOrder, BackOrder, Discontinued. The bare words are accepted by Google but produce warnings in strict validators and are easy to misspell. The same applies to itemCondition (NewCondition, UsedCondition, RefurbishedCondition).
Keep availability in sync with the stock system. Marking a sold-out product as in stock is a policy violation and can get the rich result removed for the whole site.
Variants
A page that sells one product in several sizes or colours has two valid options. The simple one: a single Product whose offers is an AggregateOffer covering all variants. The precise one: a ProductGroup with hasVariant listing each variant as its own Product with its own sku, size, color and offers. Use the second form if each variant has a distinct price or stock level and its own URL (or URL with a parameter).
Reviews and ratings
aggregateRating needs ratingValue and either reviewCount or ratingCount. The values must come from reviews on your site about this product, and the rating must be visible on the page. Reviews copied from a third-party platform, or a site-wide rating pasted onto every product, are the most common reason a store loses its stars in a manual action.
If you include individual review objects, each needs an author with a name and a reviewRating with a ratingValue.
Mistakes we see most often
- Price as a string with a currency symbol. Google reads
"$119.99"as not-a-number and drops the price. - Missing
priceCurrency. Price without currency is incomplete; no snippet. - Relative image URLs.
/img/anvil.jpgfails the image check on most validators and sometimes in Google. availabilityas free text like"in stock".- Empty arrays such as
"review": []left by a template when a product has no reviews. Omit the property instead. - One
Productblock copied to category pages. Category pages should haveItemList, not a fake product.
A site scan finds all of these across a catalogue in minutes: every issue is grouped by type and property, so a broken price template shows up as one line with the list of affected pages, not as five hundred separate errors.