Your product pages show up in Google as plain blue links. Your competitors get prices, stock status, star ratings and delivery costs. The usual reason is missing or broken product structured data. Product structured data is Schema.org markup (a standard set of labels), normally written in JSON-LD. It tells Google exactly what you sell, what it costs, whether it’s in stock, and what your shipping and return terms are.
Google uses that markup for two different result types: product snippets and merchant listings. The one you’re aiming for decides which properties you need. In this guide I cover both, with a working example and the checks I run before and after release.
What Product Structured Data Does
Google can often work out a product’s name and price from the page itself. But guessing isn’t reliable. Markup removes the guessing. It states each fact in a fixed format that Google can read without having to understand your layout.
Valid markup makes a page eligible for richer results. It doesn’t guarantee them, and it’s not a direct ranking factor. What it can change is how your result looks. A result that shows the price, stock and free delivery up front gives shoppers a reason to click before they even reach your page.
Depending on the page and the markup, Google can show:
- Price, and sometimes a price drop that Google works out itself from price changes it has seen
- Availability, such as in stock or out of stock
- Star ratings and review counts
- Shipping costs and delivery times
- Return windows and return costs
- Pros and cons, for editorial review pages
Product Snippets vs Merchant Listings
Most guides skip this difference. In my experience, it’s the one that causes the most confusion in Search Console.
Product Snippets
Product snippets are for pages about a product where the visitor can’t buy it from you. Think editorial reviews, comparison articles, or a maker’s page that links out to shops. These results add review details such as ratings, pros and cons to a normal text result.
The bar is low. You need a Product with a name, plus at least one of review, aggregateRating or offers. Offers can be a single Offer or an AggregateOffer that covers a price range from several sellers.
Merchant Listings
Merchant listings are for pages where shoppers can buy the product directly from you. They support much more shopping detail, including shipping, returns, member prices and clothing sizes. A page that only links to another seller doesn’t qualify.
The rules are stricter. You need name, image and offers. The offer must be a single Offer, because you have to be the seller. An AggregateOffer isn’t accepted here.
Which One Applies to You
If you run an online store, you’re aiming for merchant listings. The good news: a page that meets the merchant listing rules can also qualify for product snippets. So you don’t need two sets of markup.
If you publish reviews or buying guides, product snippets are your target. The positiveNotes and negativeNotes properties let you mark up pros and cons. Google only supports pros and cons on editorial product review pages, not on a shop’s own product pages.
Required and Recommended Properties
The table below covers the properties that matter most for a shop’s product page. It’s based on Google’s merchant listing documentation.
| Property | Where it goes | Status for merchant listings | Notes |
|---|---|---|---|
name |
Product | Required | The product name as shown on the page. |
image |
Product | Required | Crawlable image URLs that show the actual product. Several sizes and ratios help. |
offers |
Product | Required | Must be an Offer, not an AggregateOffer. |
price |
Offer | Required | Must be greater than zero for merchant listings. Can sit in priceSpecification instead. |
priceCurrency |
Offer | Required | Three-letter ISO 4217 code, such as GBP or USD. |
availability |
Offer | Recommended | One value, such as InStock, OutOfStock or PreOrder. |
itemCondition |
Offer | Recommended | New, refurbished or used. |
shippingDetails |
Offer | Recommended | Use for product-specific shipping. Site-wide shipping belongs at organisation level. |
hasMerchantReturnPolicy |
Offer | Recommended | Use for product-specific returns. Standard policies belong at organisation level. |
aggregateRating, review |
Product | Recommended | Only for genuine ratings and reviews that visitors can see on the page. |
brand, gtin, sku, mpn |
Product | Recommended | Identifiers help Google match your product to the same item elsewhere. Use the numeric GTIN, not a URL form. |
A few details catch people out:
- Price in two places. If you set both
offers.priceandpriceSpecification, Google usesoffers.price. - Sale dates. For a time-limited price, use
validFromwithvalidThroughorpriceValidUntilin ISO 8601 format (a standard date format). ApriceValidUntildate in the past can stop the listing from showing. I see this a lot after a sale ends and nobody updates the template. - Several currencies. If you sell in more than one currency, each currency needs its own URL.
- Single products only. Product rich results are for pages about one product, or one product with variants. Category and listing pages aren’t supported. So don’t add a
Productblock for every item in a category grid.
A Product Structured Data Example in JSON-LD
Here is a complete merchant listing example for a single product. It covers the required properties, the main recommended ones, and a rating.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Trail Runner 2 Waterproof Running Shoe",
"image": [
"https://www.example.com/images/trail-runner-2-1x1.jpg",
"https://www.example.com/images/trail-runner-2-4x3.jpg",
"https://www.example.com/images/trail-runner-2-16x9.jpg"
],
"description": "Waterproof trail running shoe with a grippy lugged sole.",
"sku": "TR2-BLK-42",
"gtin13": "5012345678900",
"brand": {
"@type": "Brand",
"name": "ExampleBrand"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.6,
"reviewCount": 87
},
"offers": {
"@type": "Offer",
"url": "https://www.example.com/trail-runner-2",
"price": 89.99,
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
}
}
</script>
Every value here must match what a visitor sees on the page. If the page says £89.99 and in stock, the markup must say the same. Mismatches are one of the main reasons Google ignores markup or flags problems.
Where you can, put the markup in the first HTML your server sends. Google’s documentation warns that markup added by JavaScript can make shopping crawls less frequent and less reliable. That matters when prices and stock change often. I always check this first on any store built with a heavy JavaScript front end.
Shipping and Return Policies at Organisation Level
Older guides tell you to add shippingDetails and hasMerchantReturnPolicy to every product. That still works. But it’s no longer what Google recommends for policies that apply across your whole store.
Google has supported return policy markup placed under Organization since June 2024. On 12 November 2025 it added the same option for shipping, using a ShippingService under the hasShippingService property. Google suggests using the OnlineStore subtype of Organization for online shops.
The idea is simple. Describe your standard policies once, on one page such as your homepage or policy page. Only use product-level properties for exceptions. I like this approach because there’s far less to keep in sync.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "OnlineStore",
"name": "Example Running Co",
"url": "https://www.example.com/",
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "GB",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
},
"hasShippingService": {
"@type": "ShippingService",
"shippingConditions": {
"@type": "ShippingConditions",
"shippingDestination": {
"@type": "DefinedRegion",
"addressCountry": "GB"
},
"shippingRate": {
"@type": "MonetaryAmount",
"value": 0,
"currency": "GBP"
}
}
}
}
</script>
Which Source Wins
Policies can now come from several places. So it helps to know the order Google uses, from strongest to weakest:
- Account-level shipping settings sent through the Content API for Shopping
- Shipping and returns settings in Merchant Center or Search Console
- Product-level markup (
shippingDetailsandhasMerchantReturnPolicyon the Offer) - Organisation-level markup
So if you’ve entered a returns policy in Search Console, that’s what Google uses, whatever your markup says. Keep all sources the same, or pick one and look after it properly.
The same 12 November announcement also means stores without a Merchant Center account can set shipping and returns directly in Search Console, where Google sees the site as an online shop. Google said this would roll out slowly over the following weeks, so you may not see it yet. For stores whose policies change often, settings are easier to keep current than markup.
Product-Level Overrides
Use Offer-level properties only when a product really differs from your standard terms. Examples are a bulky item with a delivery charge, or a final-sale item that can’t be returned. Product-level return policies support only some of the properties available at organisation level.
When several shipping conditions match a product, Google uses the lowest cost. If costs are the same, it uses the fastest delivery.
Product Variants With ProductGroup
A shoe that comes in five sizes and three colours is one product to a shopper. To your stock system it’s fifteen items. Variant markup tells Google how they connect.
You wrap the variants in a ProductGroup and give it:
namefor the parent productproductGroupID, usually the parent SKUvariesBy, listing what changes, such ashttps://schema.org/sizeorhttps://schema.org/colorhasVariant, holding each variant as aProductwith its ownskuorgtinand its ownoffers
Each variant keeps its own price and stock. So a sold-out size can be marked out of stock without affecting the others. Supported variesBy values are colour, size, suggested age, suggested gender, material and pattern.
How you build it depends on your URLs:
- Single-page setups, where variants are picked with query parameters on one URL, need one canonical URL for the whole group. Only here should the
ProductGroupcarry aurl. - Multi-page setups, where each variant has its own URL, repeat the
ProductGroupon each page. Each page has full markup for its own variant and refers to the other variants by URL only.
Variant URLs are also where duplicate content problems tend to start. Make sure your canonical tags (the tags that tell Google which version of a page is the main one) and your variant markup tell the same story.
Merchant Center vs Structured Data
You don’t have to choose. Google offers two routes for product data, and it recommends using both:
- Structured data on your pages, which Google reads when it crawls them.
- A Merchant Center feed, which you send directly and which can power free listings and the Shopping tab.
Using both gives you the widest reach and lets Google check one source against the other. Some results combine them. For example, a product snippet can use the price from your feed if the page markup doesn’t have one.
Since September 2022, pages without a Merchant Center account can still qualify for merchant listings through markup alone. That makes markup the starting point for smaller stores. Merchant Center is worth it when you want Shopping tab visibility, ads, or more control over how your catalogue is shown.
The main risk with two sources is drift (the two slowly getting out of step). If your feed says £79 and your page says £89.99, expect warnings in Merchant Center and less trust in your data. My rule is to build both from the same product database instead of updating them separately.
Testing and Monitoring Your Markup
Rich Results Test
Run one URL per template through the Rich Results Test before and after release. It shows which result types the page can get, such as merchant listings and product snippets. It also separates critical errors from non-critical warnings.
Critical errors stop a page from being eligible, so you must fix them. Warnings about missing recommended properties are optional. But they usually point to the extras you’re missing, such as shipping or returns.
Test the live page, not just a code snippet. That way you catch problems caused by templates, plugins and JavaScript.
Search Console Reports
Search Console has separate Merchant listings and Product snippets reports under the Shopping section. They list valid items, items with issues, and the specific issues affecting each group of URLs. If you’re new to the tool, my Google Search Console guide shows where these reports sit.
A habit I recommend: check the Merchant listings report after every theme update, plugin update or change to how prices are shown. A single template change can break markup on thousands of products at once.
After a fix, use Validate Fix in the report and the URL Inspection tool to confirm Google can crawl the updated page. Expect it to take days, not hours.
Common Product Structured Data Mistakes
- Zero or missing price. Merchant listings need a price above zero. Free or “price on request” products won’t qualify.
- Using AggregateOffer on your own store. It works for product snippets but not merchant listings.
- Old sale data. A past
priceValidUntil, or a sale price left in markup after the sale ends. - Markup that doesn’t match the page. Stock changes, price changes, or ratings that visitors can’t see.
- Duplicate blocks. A theme and an SEO plugin both adding
Productmarkup, often with different values. This is one of the first things I look for on WordPress and WooCommerce stores. - Markup on category pages. Product rich results are for single product pages.
- Policy conflicts. Search Console settings, Merchant Center settings and markup all saying different things about returns.
Structured data sits next to the rest of your product page work. It doesn’t replace it. My ecommerce product page SEO checklist covers titles, descriptions, images and internal links. Breadcrumb markup is another quick win on the same templates.
Conclusion
Product structured data comes down to three choices. Are you after merchant listings or product snippets? Which properties can your pages honestly support? And where should your shipping and return policies live? For most stores, that means a clean Product and Offer on every product page, organisation-level policies on one page, and ProductGroup wherever you sell variants.
Your next step: pick your busiest product template and run it through the Rich Results Test. Fix any critical errors first. Then open the Merchant listings report in Search Console and work through the issues, starting with the ones that affect the most items. Google’s merchant listing documentation has the full property list when you need it.



