· Right On Tech · Search Engine Hacks

Structured Data for Product Pages: The Schema.org Markup That Earns Rich Results

Pair of navy and white sneakers on a linen-covered table next to a laptop showing blurred lines of code, by a bright window

When a product shows up in Google with its price, a star rating, “In stock” and free shipping right in the search result, that extra detail usually comes from structured data: a small block of code on the product page that describes the product in a format search engines understand. It doesn't change how the page looks to visitors, but it can change how your listing looks in search.

This guide covers the schema.org types that matter for product pages, what Google requires, and copy-ready JSON-LD examples. Everything is based on Google's product structured data documentation.

Two kinds of product results

Google distinguishes between two experiences, and the one you're eligible for depends on whether people can buy on your page:

Product snippetsMerchant listings
ForPages about a product where people can't buy directly (reviews, comparisons, catalogs)Pages where customers can buy the product from you
Requiredname, plus at least one of review, aggregateRating or offersname, image, and an Offer with a price above zero and a currency
Can showRatings, reviews, price range, pros and cons (editorial reviews only)Price, availability, shipping, returns, sale prices, ratings

If you run an online store, aim for merchant listings. Google notes that including the required merchant listing properties can also make the page eligible for product snippets. See merchant listing and product snippet requirements.

Why JSON-LD

Structured data can be written in a few formats, but Google recommends JSON-LD: a separate script block in the page, so you don't have to weave attributes through your HTML. Most store platforms and SEO plugins generate it automatically; your job is to check that it's complete and accurate.

The core: Product and Offer

Here's a minimal product page that meets Google's merchant listing requirements, plus a few recommended identifiers:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Canvas Low-Top Sneaker, Navy",
  "description": "Lightweight canvas sneaker with a cushioned rubber sole.",
  "image": [
    "https://www.example.com/images/sneaker-navy-1x1.jpg",
    "https://www.example.com/images/sneaker-navy-4x3.jpg",
    "https://www.example.com/images/sneaker-navy-16x9.jpg"
  ],
  "sku": "CLS-NAVY-42",
  "gtin13": "0012345678905",
  "brand": { "@type": "Brand", "name": "Example Brand" },
  "offers": {
    "@type": "Offer",
    "url": "https://www.example.com/sneakers/canvas-low-top-navy",
    "price": 59.00,
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition"
  }
}
</script>

A few things to get right:

  • One Offer, not AggregateOffer, for merchant listings. Google requires an Offer because the merchant must be the seller. AggregateOffer (a price range across sellers) is only for product snippets.
  • price must be greater than zero, and priceCurrency uses a three-letter ISO 4217 code such as USD.
  • Images: Google recommends several high-resolution images in 16:9, 4:3 and 1:1 ratios.
  • Identifiers: gtin (or gtin8/gtin12/gtin13/gtin14), mpn, sku and brand are recommended. Use the numeric GTIN, not a URL.
  • Availability uses schema.org values like InStock, OutOfStock, BackOrder or PreOrder. Keep it in sync with real stock.
Illustration of a product box linked by a glowing line to a card showing five stars, a price tag and a delivery truck
Structured data describes the product, price, rating and delivery details behind a rich result.

Shipping and returns

Shipping cost, delivery time and return terms are some of the most useful details in a product result. You can add them to the Offer:

"offers": {
  "@type": "Offer",
  "price": 59.00,
  "priceCurrency": "USD",
  "availability": "https://schema.org/InStock",
  "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": 2, "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"
  }
}
  • shippingRate holds one rate. Use 0 for free shipping. The currency must match the offer.
  • deliveryTime uses whole days for handlingTime (before it ships) and transitTime (in the carrier's hands).
  • returnPolicyCategory is one of MerchantReturnFiniteReturnWindow, MerchantReturnNotPermitted or MerchantReturnUnlimitedWindow. A finite window also needs merchantReturnDays.

Better: set store-wide policies once

If the same shipping and return terms apply to most products, Google recommends defining them once at the organization level instead of repeating them on every product. Return policies go in hasMerchantReturnPolicy and shipping policies in hasShippingService, nested under your Organization (or OnlineStore) markup, typically on your home page or policy page:

{
  "@context": "https://schema.org",
  "@type": "OnlineStore",
  "name": "Example Store",
  "url": "https://www.example.com/",
  "hasMerchantReturnPolicy": {
    "@type": "MerchantReturnPolicy",
    "applicableCountry": "US",
    "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
    "merchantReturnDays": 30,
    "returnMethod": "https://schema.org/ReturnByMail",
    "returnFees": "https://schema.org/FreeReturn"
  }
}

Product-level details then only need to cover exceptions, such as an oversized item with different shipping. You can also set these policies in Google Merchant Center or Search Console; Google documents that settings there take precedence over on-page markup.

Sale prices

To show a sale with the original price crossed out, put the current price in price and the original price in a UnitPriceSpecification with priceType set to StrikethroughPrice:

"offers": {
  "@type": "Offer",
  "price": 44.00,
  "priceCurrency": "USD",
  "availability": "https://schema.org/InStock",
  "priceSpecification": {
    "@type": "UnitPriceSpecification",
    "priceType": "https://schema.org/StrikethroughPrice",
    "price": 59.00,
    "priceCurrency": "USD"
  }
}

Don't put a priceType on the active sale price itself. If the sale has an end date, add priceValidUntil or validThrough to the offer.

Ratings and reviews

Star ratings are what most store owners want. Add aggregateRating for the overall score and review for individual reviews shown on the page:

"aggregateRating": {
  "@type": "AggregateRating",
  "ratingValue": 4.6,
  "reviewCount": 38
},
"review": [
  {
    "@type": "Review",
    "author": { "@type": "Person", "name": "Example Reviewer" },
    "reviewRating": { "@type": "Rating", "ratingValue": 5, "bestRating": 5 },
    "reviewBody": "The review text exactly as it appears on the page."
  }
]

Google's review snippet guidelines set the rules:

  • aggregateRating needs ratingValue plus ratingCount or reviewCount.
  • Each Review needs an author with a real name (a person or organization, under 100 characters) and a reviewRating with a ratingValue. The scale is assumed to be 1 to 5 unless you set bestRating and worstRating.
  • Only mark up reviews visitors can see on the page. Ratings in the markup must match what's displayed.
  • No self-serving reviews of your own business. Star snippets aren't shown for LocalBusiness or Organization markup when the business controls reviews about itself. Product reviews on your product pages are a different case and are allowed.
  • Pros and cons (positiveNotes/negativeNotes) are only for editorial review pages, not merchant product pages or customer reviews.

Product variants

If a product comes in sizes or colors, Google supports product variant markup using ProductGroup with hasVariant, variesBy and productGroupID. Each variant gets its own Product with its own offer, SKU and GTIN. If your store has one URL per variant or uses a variant selector, it's worth implementing.

Testing and monitoring

  1. Test a page with Google's Rich Results Test. It shows which rich results the page is eligible for and flags errors and warnings.
  2. Fix errors first (missing required properties), then warnings (missing recommended ones) where you have the data.
  3. Check Search Console. The Merchant listings and Product snippets reports show valid and invalid items across your whole site after Google recrawls.
  4. Re-test after theme or plugin updates. A template change can silently remove markup from every product page.

Common mistakes

  • Markup that doesn't match the page: a different price, “in stock” when the page says sold out, or ratings that aren't displayed.
  • Two plugins both outputting Product markup, creating duplicate or conflicting data.
  • Prices in the wrong format (include only the number in price, no currency symbols or commas).
  • Using AggregateOffer on a regular store page and losing merchant listing eligibility.
  • Missing GTINs on products that have them.

One last expectation to set: Google says that valid structured data makes a page eligible for rich results but doesn't guarantee they'll appear. Accurate, complete markup simply gives Google the best information to work with.

Need it checked?

Right On Tech builds and maintains online stores and can audit your product markup, fix template-level issues, and connect it with your Merchant Center feed so everything matches. If your product results look plain or Search Console is reporting errors, contact us for a review.

#Structured Data#e-commerce#seo

Read more from the journal