Product Schema Markup for E-commerce: A Step-by-Step Guide
Product schema is the one structured-data type where the rich result is unambiguously alive and worth chasing. While Google pruned FAQ and HowTo rich results out of existence, Product markup still earns the price, the star rating, and the "In stock" line that make a shopping result jump off the page, and it's the gateway to merchant listing experiences. This is the field manual: the fields Google requires, the ones that unlock the richer treatment, and the Search Console errors that quietly disqualify pages.
New to JSON-LD? The Schema Markup Guide covers the basics; the types overview shows where Product sits among the rest.
The required core
Google's minimum for a Product result is name, image, and an offers object carrying price and priceCurrency. Everything else is upgrade. Here's a clean, valid base:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Wireless Noise-Cancelling Headphones",
"image": [
"https://example.com/photos/1x1/headphones.jpg",
"https://example.com/photos/4x3/headphones.jpg",
"https://example.com/photos/16x9/headphones.jpg"
],
"description": "Over-ear wireless headphones with 30-hour battery and active noise cancellation.",
"sku": "WH-NC-200",
"brand": { "@type": "Brand", "name": "Acme Audio" },
"offers": {
"@type": "Offer",
"url": "https://example.com/headphones",
"priceCurrency": "USD",
"price": "199.00",
"availability": "https://schema.org/InStock"
}
}
Supply images in multiple aspect ratios (1:1, 4:3, 16:9) so Google can pick the best crop for each surface. The brand should be the product's manufacturer, not your store's Organization, a common and silent mistake.
The star rating: where the CTR lift lives
The visible win most merchants want is the star snippet, and that comes from aggregateRating (and optionally individual review items) nested in the Product:
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"reviewCount": "284"
},
"review": [{
"@type": "Review",
"reviewRating": { "@type": "Rating", "ratingValue": "5", "bestRating": "5" },
"author": { "@type": "Person", "name": "Maria S." },
"reviewBody": "Battery life is incredible. Pairs instantly with laptop and phone."
}]
The non-negotiable rule: the rating and reviews in the markup must be the ones a user can see on that page. Aggregating ratings from a third-party site, recycling reviews across products, or marking up stars that appear nowhere visible is a structured-data policy violation, and it's the kind of thing manual reviewers act on.
Availability values
availability takes a schema.org URL, not a bare word:
| Value | Meaning |
| --- | --- |
| https://schema.org/InStock | Available now |
| https://schema.org/OutOfStock | Currently unavailable |
| https://schema.org/PreOrder | Orderable, ships later |
| https://schema.org/BackOrder | Orderable, ships when restocked |
| https://schema.org/LimitedAvailability | Limited stock |
| https://schema.org/Discontinued | No longer sold |
Keep this in sync with real inventory. A page marked InStock that's actually sold out is the fastest way to get the rich result suppressed and erode trust.
Identifiers: GTIN, MPN, and merchant listings
For merchant listings (the richer shopping treatment, including free listings in the Shopping tab), Google leans heavily on a unique product identifier. Add at least one:
"gtin13": "0192876543210",
"mpn": "WH-NC-200"
- GTIN (UPC/EAN/ISBN, expressed as
gtin8/gtin13/gtin14) is the barcode number. Required-in-practice for branded products that have one; omitting it is the most common reason a real product fails to qualify for merchant listings. - MPN (manufacturer part number) is the fallback when no GTIN exists.
- For genuinely unique items with no GTIN (handmade, custom), set the
OfferfieldhasGTINsemantics aside and rely onbrand+mpn; identifier-exempt categories exist but are narrow.
Shipping and returns: now eligibility factors, not extras
Google moved shipping and return data from "nice to have" toward "expected" for shopping experiences. You can supply them in Merchant Center or in the markup; in markup they live on the Offer:
"shippingDetails": {
"@type": "OfferShippingDetails",
"shippingRate": { "@type": "MonetaryAmount", "value": "0", "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"
}
Missing these won't break a basic Product snippet, but it can keep you out of the fuller merchant listing experience where competitors show free shipping and 30-day returns right in the result.
Variants: don't flatten them
For size/colour variants, don't cram them into one Product. Either model each variant as its own Product with a distinct Offer and SKU, or use a parent ProductGroup with a hasVariant array and variesBy (the modern approach that scales and tells Google which attributes differ).
The Search Console errors you'll actually hit
These are the ones that show up in the Merchant listings and Product snippets reports, and what each really means:
| Error / warning | What's wrong |
| --- | --- |
| Either "offers", "review", or "aggregateRating" should be specified | Bare Product with none of the three; add an offers block. |
| Missing field "price" / "priceCurrency" | Offer lacks a price or currency; both are required. |
| Missing field "hasMerchantReturnPolicy" (warning) | No return policy; costs merchant-listing eligibility, won't block the basic snippet. |
| Missing field "shippingDetails" (warning) | No shipping data; same story, eligibility, not a hard error. |
| Missing field "gtin" / "identifier" (warning) | No unique identifier on a product that should have one. |
| Invalid value in "availability" | Used "InStock" instead of the full https://schema.org/InStock URL. |
Warnings don't disqualify the basic snippet, but they cap the richer treatment. Errors do disqualify. The split between the two is the thing most people misread.
Generate, validate, ship
Hand-writing this much nested JSON invites typos. Build it with our Schema Generator, then run the live URL through Google's Rich Results Test (Product is fully supported there) and watch the Merchant listings report in Search Console over the following weeks. Re-validate every time price, stock, or reviews change; drift between markup and page is the cardinal sin of Product schema.
Frequently Asked Questions
Do I really need GTIN, MPN, or another identifier?
For branded products that have a GTIN, effectively yes; omitting it is the top reason a real product misses merchant-listing eligibility. Genuinely unique items (handmade, custom) can rely on brand + mpn, but add an identifier whenever one exists.
What's the difference between a Product snippet and a merchant listing?
The Product snippet is the basic price-and-stars result, earned with name, image, and offers. Merchant listings are the richer shopping experience that also weighs identifiers, shipping, and return data. The required floor is lower than the floor for the full treatment.
Why does my Product show warnings but still validates?
Warnings (missing shipping, returns, or GTIN) reduce eligibility for richer results but don't block the basic snippet. Errors (missing price, currency, or an invalid availability value) do block it. Fix errors first; treat warnings as upgrades.
Must the schema reviews match the visible page reviews?
Exactly, yes. The aggregateRating and any review items must reflect ratings shown on that page. Recycling reviews across products or pulling stars from elsewhere violates Google's policy and risks a manual action.
Does Product schema work for digital or free products?
Yes. Software, e-books, courses, and SaaS all qualify; use Offer with a real priceCurrency. For free items set price to "0.00", though note some shopping surfaces favour priced products. For software specifically, SoftwareApplication adds type-specific fields.