Product schema markup generator
Product is the type most often marked up and most often marked up wrongly, because one schema.org type feeds several different Google Search features and each one asks for something different.
Google Search features this type can produce
From the registry| Feature | Status | Surface | Last verified |
|---|---|---|---|
| Product snippet (opens in a new tab) | ACTIVE | search, images | 2026-08-29 |
| Merchant listing experience (opens in a new tab) | ACTIVE | search, shopping, images | 2026-08-29 |
| Product variants (opens in a new tab) | ACTIVE | search, shopping | 2026-08-29 |
| Return policy (opens in a new tab) | ACTIVE | search, shopping | 2026-08-29 |
Meeting a feature’s documented requirements does not guarantee that the feature appears. No tool can promise that, and any that does is describing something it cannot know.
Generate the markup
Runs in your browser<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Aeropress Go travel coffee press",
"description": "A compact coffee press that packs into its own mug, for brewing away from a kitchen.",
"image": [
"https://example.com/photos/aeropress-go.jpg"
],
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.7,
"reviewCount": 218
},
"brand": {
"@type": "Brand",
"name": "Aeropress"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/aeropress-go",
"availability": "https://schema.org/InStock",
"price": 39.95,
"priceCurrency": "GBP"
},
"sku": "AP-GO-001"
}
</script>Paste this into the <head> of the page it describes. Markup that describes a page must live on that page.
One type, four different sets of requirements
Most guides describe Product as though there were a single list of properties to satisfy. There is not. Google documents product snippets and merchant listing experiences as separate features, and the same Product entity is judged against whichever one applies to your page. A product snippet is the modest enhancement an ordinary product page can meet: a name, an image, and one of price, rating or review. A merchant listing is what a page that actually sells the product is judged against, and it asks for more — image becomes required rather than recommended, availability becomes required, and shipping and returns detail start to matter.
That distinction has a practical consequence. If your page is a review or a comparison rather than a shop, a tool that checks Product against merchant listing rules will tell you that you are missing shipping details you were never expected to provide. If your page is a shop, a tool that only checks product snippet rules will tell you everything is fine while the properties that matter for shopping surfaces are absent. Both answers are wrong, and both are common.
This generator keeps them apart. Fill in the fields you have, and the panel next to the output shows which features the resulting markup meets the documented requirements for, and which properties each one is still missing. Nothing here predicts whether a rich result will appear, because nothing can.
Price is the field that breaks most implementations
The price property takes a bare number. Not a currency symbol, not a thousands separator, not a range, not "from". The currency belongs in priceCurrency as a three-letter ISO 4217 code. Writing "$1,299.00" into price is the single most common Product markup error, and it is one that a plain schema.org validator will not object to, because schema.org itself does not constrain the format that tightly.
The second most common price problem is a mismatch with the page. Marked-up values must match what the visitor sees. A page that shows a discounted price while the markup carries the pre-discount one is a mismatch, whether or not it was intentional, and it is exactly the kind of thing a template change introduces silently three months after launch. If your prices are generated by a template, check the markup on a page with a sale price and on a page without one.
Where a product has several variants at different prices, an AggregateOffer with lowPrice and highPrice describes that honestly. A single Offer carrying the lowest price is a mismatch on every variant except one.
Ratings and reviews have to be real, and on the page
aggregateRating and review are what turn a plain product listing into something with stars attached, which is why they are the properties most often invented. The requirement is straightforward: the rating and the review count must be visible on the page, and they must describe the product the page is about. A rating that appears only inside the JSON-LD, with nothing corresponding to it in the page content, does not meet the documented requirement.
If you use a rating scale other than one to five, state it. bestRating and worstRating exist precisely so a four-out-of-ten is not read as a four-out-of-five. Where reviews are paginated or loaded on scroll, be careful that reviewCount describes the true total rather than the number that happened to render.
Reviews a business writes about its own products sit outside what the documentation describes review markup as being for. That is a policy question rather than a syntax question, and no generator can decide it for you — but it is worth knowing before you build a system around it.
Identifiers do more work than they look like they do
sku, mpn and the gtin family are not glamorous, but they are how a product on your page is matched to the same product elsewhere. For merchant listings in particular, providing a GTIN where one exists is documented as recommended, and the practical effect is that your listing can be understood as the same item that other sellers list rather than as an unknown product with a similar name.
Use the gtin property with the value you have; the older length-specific properties still work, but the general one is simpler to maintain. If a product genuinely has no GTIN — a handmade or own-brand item — omit it rather than inventing one. An identifier that matches nothing is worse than no identifier.
brand should be a nested Brand or Organization node with a name, not a bare string. The nested form is what allows the brand to be identified rather than merely mentioned, and it costs one extra line.
Mistakes worth avoiding
Marking up a category page as a Product
A page listing twelve products is not one product. Product markup on a category page describes something that does not exist, and the mismatch between the single marked-up entity and the twelve visible items is detectable. Use an ItemList if you want to describe the collection, and put Product markup on the individual pages.
Availability that never changes
InStock hard-coded into a template stays InStock when the product sells out. Availability must match what the page shows. If your stock state is dynamic and your markup is static, the markup is wrong for every out-of-stock product on the site.
priceValidUntil left in the past
A priceValidUntil date that has already passed suggests the price is stale. Either keep it current or omit it — it is recommended, not required, and an expired date is worse than an absent one.
Two Product entities on one page
A theme that outputs Product markup and a plugin that also outputs Product markup produce two entities describing the same thing, often with different prices. Consumers may read either. This is one of the most common findings on WordPress and Shopify stores, and the fix is to turn one of them off rather than to reconcile them.
Review markup with no visible review
An aggregateRating with a reviewCount of 47 on a page showing no reviews at all does not meet the requirement that marked-up content be visible. If the reviews live on a separate page, the rating belongs there too.
Check what you already have
If the page you are marking up already carries structured data, audit it before adding more. Duplicate entities from a theme and a plugin are the most common finding on established sites, and adding a third block makes it worse rather than better.