Implementing Product Schema for Google Merchant Listings
Schema markup for products is one of the more concrete structured data applications because the data involved — prices, reviews, availability — is specific enough to verify and important enough to display in search results.
When implemented correctly, product schema can add star ratings, price information, and availability status directly to your search listings. When implemented incorrectly, it either does nothing or, in worse cases, triggers a manual action for misleading markup.
What Product Schema Can Do in Search Results
Google can use product schema to show several types of rich results:
- Price and availability shown directly under the page title in search results
- Star ratings from aggregate review data
- Product shipping information and return policies in some search contexts
- Merchant listing experience in Google Shopping surfaces
None of these are guaranteed by adding markup. Google decides whether to show rich results based on the quality of the page, the accuracy of the markup, and other factors. But accurate markup that matches visible page content is a prerequisite for any of these to appear.
The JSON-LD Structure You Need
The recommended implementation uses JSON-LD in a tag in your page's . Here's the core structure:
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "Product Name Here",
"image": ["https://example.com/image1.jpg"],
"description": "A description of the product.",
"brand": {
"@type": "Brand",
"name": "Brand Name"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/product-page",
"priceCurrency": "USD",
"price": "49.99",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.4",
"reviewCount": "89"
}
}
This covers the most commonly used fields. Each field needs to match what's actually displayed on the page.
The Matching Requirement That Causes the Most Problems
Google's Product schema guidelines have a strict requirement: the data in your markup must match the information visible to users on the page.
This causes real problems for e-commerce sites with dynamic pricing, inventory that changes, and sale prices that start and stop. If your schema says the price is $49.99 and the visible page says $39.99 during a sale, that's a mismatch.
The technical solution is to generate your schema markup dynamically from the same data that populates your visible page content. If your e-commerce platform updates the displayed price, the schema should update simultaneously.
For Shopify, this is usually handled through the product template's Liquid code. For WooCommerce, through PHP in the theme or a plugin. For custom builds, through whatever server-side templating generates the page.
Never hardcode prices or availability in schema markup. It will be wrong within days.
Review and Rating Schema: What's Allowed
The most common schema mistake on e-commerce sites is adding aggregate rating markup to products that have no visible reviews on the page.
This violates Google's guidelines. The rating in your markup must reflect reviews that are actually visible and readable on the page. If your product page shows "4.3 stars from 67 reviews" with actual review content visible, you can mark that up. If your page has no visible reviews but you add a 4.8-star rating to the schema hoping to get stars in search results, you're likely to get a manual action for spammy structured data.
The fix is either to import and display actual reviews on your product pages, or to not include aggregateRating in your markup until you do.
Handling Variants
Variable products — a t-shirt that comes in five colors and four sizes, for example — need schema treatment that represents the actual product being viewed on each page.
If each variant has its own dedicated URL (common on platforms like Shopify), each URL's schema should reflect that specific variant's price and availability, not the price range across all variants.
If variants are handled on a single URL with JavaScript selection, the schema needs to reflect the base product. Some platforms handle this by marking up the price range ("$29-$49") or the lowest price, which is acceptable but less useful for rich results.
Don't mark up a product as "InStock" if only some variants are in stock. Use the specific availability status that reflects the variant being viewed.
Testing and Monitoring
Google's Rich Results Test (search.google.com/test/rich-results) evaluates a URL and shows you what markup it detects, what rich results the page is eligible for, and any errors or warnings in the implementation.
Run this before you launch product schema on any significant number of pages. The most common issues that appear:
- Missing required fields (offers and name are required; markup without them won't produce rich results)
- Type values that don't match Schema.org vocabulary (availability must use the full schema.org URL, not just "InStock")
- Price values that contain currency symbols (the price field should be a numeric string; currency goes in priceCurrency)
After launch, monitor the Enhancements section of Google Search Console. The Product enhancements report shows pages with markup errors, pages eligible for rich results, and pages where rich results have been discovered. Watch for a sharp increase in error counts after any site changes that touch product templates.
When Product Schema Genuinely Helps
The impact of product schema on click-through rates varies significantly by industry and query type. In competitive e-commerce categories where multiple listings appear with star ratings and price information, being the one listing without this data is a disadvantage.
In less competitive categories, or for products with query patterns that are more informational than transactional, the visible impact on CTR can be minimal.
Run it anyway. The implementation cost is low if done properly, the downside risk from correct markup is near zero, and the upside when it does produce rich results is meaningful. The risk is only from incorrect implementation.