Product schema: markup for product rich results

·4 min read

Product schema is schema.org Product markup, usually JSON-LD, that tells Google a page describes a product and gives it the name, price, currency, availability and ratings in a form it can read. It makes the page eligible for product rich results, either a product snippet (stars, price) or a merchant listing (the shopping experiences for pages that sell). Pages where people can buy need the stricter merchant listing rules. Review and comparison pages only need the product snippet rules.

New to the format? Read what JSON-LD is first.

Product snippets vs merchant listings

Google documents product structured data on two pages, and the one you follow depends on whether the visitor can buy on your page. The product snippet guide covers "product reviews and aggregator sites". The merchant listing guide covers "pages where shoppers can buy products".

Product snippet Merchant listing
For Reviews, comparisons, aggregators Pages that sell the product
Required on Product name, plus one of offers, review or aggregateRating name, image, offers
Required on Offer price price and priceCurrency
Price of zero Allowed Not allowed, price must be above zero

If you sell, write to the merchant listing spec and you satisfy both.

Offer schema: price, currency and availability

The Offer node carries the commercial facts, and most product markup bugs live here.

  • price as a plain number, 29.99, not "$29.99" or "29,99 USD".
  • priceCurrency as an ISO 4217 code such as USD or EUR.
  • availability as a schema.org URL, such as https://schema.org/InStock or https://schema.org/OutOfStock. "Ships in 2 days" is not a valid value.
  • priceValidUntil only if the price really expires. A date in the past tells Google the offer is stale.

Use AggregateOffer with lowPrice and highPrice only when several sellers offer the same product, as on a comparison site. A shop selling its own product in five sizes should mark up each variant's real price, not a range.

Product structured data for identifiers and brand

Google lists gtin (or gtin8, gtin12, gtin13, gtin14), brand.name, mpn and sku as recommended for merchant listings. They are optional, but they are how Google matches your page to the same product elsewhere. If the item has a barcode, mark it up.

Check the GTIN before you publish it. The last digit is a check digit calculated from the others, and a typo or a truncated leading zero produces a number that matches no product. I have seen whole catalogs lose the leading zero of every UPC in a spreadsheet export.

brand takes a Brand node with a name, not a bare string. For your own products, the brand is your company. For resold goods, it is the manufacturer.

Merchant listing schema for shipping and returns

Google recommends setting one shipping policy and one return policy for the whole business under Organization markup, not repeating them on every product. The merchant listing guide says offer-level shippingDetails and hasMerchantReturnPolicy are for products that differ from the standard policy, and they support fewer properties than the organization-level version.

You can skip markup for policies altogether. The return policy guide says you can configure returns in Merchant Center or as account-level settings in Search Console, and the merchant listing guide says the same for shipping. When both exist, Google ranks the sources from strongest to weakest:

  1. Product feeds and Content API settings in Merchant Center
  2. Merchant Center or Search Console settings
  3. Product-level markup
  4. Organization-level markup

If your Merchant Center account already sets policies, Google ignores your organization-level policy markup. Keep it accurate or remove it.

Product schema JSON-LD example

This is a merchant listing for one product, with policies left to Organization markup or Merchant Center.

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Trail Runner 2 Waterproof Jacket",
  "image": "https://example.com/img/trail-runner-2.jpg",
  "description": "Packable waterproof running jacket, 180 g, taped seams.",
  "sku": "TR2-BLK-M",
  "gtin13": "4006381333931",
  "brand": { "@type": "Brand", "name": "Example Outdoor" },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": 4.6,
    "reviewCount": 212
  },
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/trail-runner-2",
    "price": 129.00,
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition"
  }
}

Put it in a <script type="application/ld+json"> block in the HTML your server sends, not in a tag manager, so every tool can see it. For where this fits among other types, see the schema markup guide.

Common product schema mistakes

Most failures I see come down to markup that disagrees with the page or sits on the wrong page.

  • Price drift. The page says 119.00 after a sale, the JSON-LD still says 129.00. Google requires markup to match visible content, so generate both from the same source.
  • A category page marked as one Product. A list of 24 products is not a product. Mark up each product page instead.
  • A rating outside the product. An aggregateRating must sit inside the Product, and it needs ratingValue plus ratingCount or reviewCount.
  • Several products on one page under one Product node.
  • A missing image or currency on a page that sells, which passes the snippet rules and fails the merchant listing ones.

The Rich Results Test shows which of the two features Google detects on a URL and flags missing required fields. It does not check that your marked-up price is the one on the page.

Check your product schema

Our Product Schema Checker reads the Product markup, in JSON-LD or microdata, from the HTML your server sends for one product page URL. It tests it against Google's product snippet and merchant listing requirements and reports what is missing or wrong. That covers a price that is not a plain number or is zero, a missing or non-ISO currency, an availability that is not a schema.org value, an expired priceValidUntil, a missing identifier or an invalid GTIN check digit, no brand, no shipping details or return policy, incomplete ratings, and a marked-up price that does not appear in the page's HTML.

It does not run JavaScript, so markup added by client-side code is not seen, and it does not check Merchant Center feeds. Each run checks one page and costs 5 credits.

Keep reading