Product Schema Markup: 2026 JSON-LD Guide for Ecommerce

Product schema markup is the JSON-LD that turns an ecommerce page into a price, in-stock label, star rating, review count, shipping window, and return-policy badge inside Google search results. It’s also the layer Perplexity, ChatGPT, and Gemini parse when answering "best [category] for [use case]" queries with shopping intent. Without it, your product pages compete naked. With it, they compete with five extra signals attached.

The required part is small. For a page that sells the product, Google needs a name, an image and an offer with a price and a currency. Everything past that is recommended, and the recommended layer is where the real work sits. Shipping and return details are the clearest case, since Google now accepts them once for the whole store instead of on every product page.

This guide covers product schema markup end to end: required and recommended fields, what changed in 2026, JSON-LD examples for physical and digital products, how Offer / AggregateRating / Review nest correctly, WooCommerce and Shopify implementation paths, validation, and the AI search citation angle that nobody is talking about yet. Every snippet validates green in Google’s Rich Results Test as of September 2026.

Want the product markup shipped for you? Gatilab’s AI search optimization service implements and validates it, then tracks the AI citations that follow.

What Product Schema Markup Is

Product schema markup is structured data using the schema.org Product type. You add it to a product page as JSON-LD inside a script tag. Search engines parse the markup and use it to populate the Shopping tab, the price snippet under blue links, the star ratings, and the rich product cards inside Google’s product knowledge panel. AI search engines parse it to identify your product, your brand, and your trustworthiness as a citable retail source.

Product is one of the deepest schema types because real products have many properties: identifiers (SKU, MPN, GTIN), commercial attributes (price, currency, availability), trust signals (rating, reviews), shipping logistics (delivery time, cost, region), and return logistics (return window, restocking fee). The schema covers all of it. Most sites use a fraction; the difference between a fraction and the full implementation shows up in rich-result win rate.

Google uses Product markup for two different features, and the difference decides which fields a page needs:

  • merchant listings, for pages where shoppers can buy the product, which need an Offer with a price and a currency
  • product snippets, for pages that review or compare a product, which need a name plus a review, a rating or an offer

Shipping and return details are recommended extras for merchant listings, and a page without them is still eligible. The full type catalog this fits into is mapped in our types of schema markup guide.

Google’s merchant listing documentation has 3 required Product properties and a long recommended list. Product snippets ask for less, so the fields below follow the merchant listing rules, the stricter of the two.

Required: name, image (one or more URLs) and offers, where each Offer carries price and priceCurrency. Merchant listings need an Offer; AggregateOffer only counts for product snippets.

Recommended: description, brand (with name), sku, mpn, gtin (or gtin8, gtin12, gtin13, gtin14), aggregateRating, review, category, color, size and weight, plus availability, itemCondition, url and priceValidUntil on the Offer.

Recommended, and best set once for the store: offers.shippingDetails and offers.hasMerchantReturnPolicy. Google recommends one store-wide shipping and return policy, under Organization markup or in Search Console, with product-level versions only for products that differ from it. The priceValidUntil property needs care on sale prices, because Google may not show a listing whose priceValidUntil date has already passed.

Product JSON-LD: Physical Product Example

Here’s a complete Product JSON-LD pattern for a physical product, with product-level shipping and return details included so every property sits in one place. The price is a plain decimal (a string here, though Google’s own examples use numbers such as 39.99, and both are valid), the currency is ISO 4217 and shippingDetails uses MonetaryAmount and DefinedRegion to set the cost and the destination country. A store with one standard policy can move the shipping and return blocks into Organization markup and drop them from every product.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Mechanical Keyboard Pro 75",
  "image": [
    "https://example.com/keyboard-front.jpg",
    "https://example.com/keyboard-side.jpg",
    "https://example.com/keyboard-typing.jpg"
  ],
  "description": "Hot-swap 75% mechanical keyboard with PBT keycaps and gasket-mounted aluminum case.",
  "sku": "KB-PRO-75-BLK",
  "mpn": "KBP75-2026",
  "gtin13": "5901234123457",
  "brand": {
    "@type": "Brand",
    "name": "Acme Keyboards"
  },
  "category": "Computer Peripherals > Keyboards",
  "color": "Charcoal Black",
  "weight": {
    "@type": "QuantitativeValue",
    "value": "1.2",
    "unitCode": "KGM"
  },
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/products/keyboard-pro-75",
    "priceCurrency": "USD",
    "price": "189.00",
    "priceValidUntil": "2026-12-31",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "seller": {
      "@type": "Organization",
      "name": "Acme Keyboards"
    },
    "shippingDetails": {
      "@type": "OfferShippingDetails",
      "shippingRate": {
        "@type": "MonetaryAmount",
        "value": "5.99",
        "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"
    }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "1240",
    "bestRating": "5",
    "worstRating": "1"
  }
}
</script>

Product Schema With Reviews and AggregateOffer

For a product sold by several retailers, AggregateOffer replaces the single Offer. It wraps a low and high price plus an offerCount. Google’s product snippets accept it, but merchant listings require a single Offer, so AggregateOffer fits comparison and marketplace pages better than a store’s own product page, and a store selling variants at different prices should mark them up as a ProductGroup instead. For pages displaying individual customer reviews, the review array nests Review objects with author, datePublished, reviewRating and reviewBody.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Wireless Earbuds Studio",
  "image": "https://example.com/earbuds.jpg",
  "brand": { "@type": "Brand", "name": "Acme Audio" },
  "offers": {
    "@type": "AggregateOffer",
    "priceCurrency": "USD",
    "lowPrice": "129.00",
    "highPrice": "189.00",
    "offerCount": "5",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.6",
    "reviewCount": "847"
  },
  "review": [
    {
      "@type": "Review",
      "author": { "@type": "Person", "name": "Sarah M." },
      "datePublished": "2026-04-12",
      "reviewRating": { "@type": "Rating", "ratingValue": "5" },
      "reviewBody": "Best sound quality I have heard in this price range."
    },
    {
      "@type": "Review",
      "author": { "@type": "Person", "name": "James K." },
      "datePublished": "2026-04-08",
      "reviewRating": { "@type": "Rating", "ratingValue": "4" },
      "reviewBody": "Battery life is solid. Touch controls take getting used to."
    }
  ]
}
</script>

What Changed in Product Schema Markup for 2026

None of Google’s recent changes made a new Product property required. They change where shipping and return details live, which source wins when two of them disagree and how a sale price should be dated. Google’s documentation dates each one:

  • June 2024: Google added support for a store-wide return policy, a MerchantReturnPolicy nested under Organization through hasMerchantReturnPolicy.
  • July 2024: Search Console gained shipping and returns settings, so a store can hand Google its policies without writing any markup.
  • November 2025: store-wide shipping arrived the same way, as a ShippingService nested under Organization through hasShippingService. It covers rates by destination and order value, delivery times and countries the store doesn’t ship to.
  • May 2026: hasAdultConsideration joined the merchant listing and product variant documentation, for stores that sell adult products.
  • July 2026: Google published sale-duration guidance. A sale price should carry validFrom for the start and validThrough or priceValidUntil for the end, in ISO 8601 with a time and timezone. Also that month, category started accepting a Google product category code (CategoryCode) as well as plain text.

The precedence order matters more than any single change. When the same policy reaches Google from more than one place, Google’s merchant shipping policy documentation ranks the sources from strongest to weakest:

  1. Content API for Shopping account settings
  2. Merchant Center or Search Console settings
  3. shippingDetails or hasMerchantReturnPolicy on the product’s Offer
  4. the same policies on Organization markup

So a store with one standard policy should set it once, in Search Console or as Organization markup, and keep Offer-level shippingDetails or hasMerchantReturnPolicy for the products that break the rule, such as oversized items or final-sale goods. Here is a store-wide policy as Organization markup, with 30-day free returns and free shipping on orders of $50 or more:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "OnlineStore",
  "name": "Acme Keyboards",
  "url": "https://example.com",
  "hasMerchantReturnPolicy": {
    "@type": "MerchantReturnPolicy",
    "applicableCountry": "US",
    "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
    "merchantReturnDays": 30,
    "returnMethod": "https://schema.org/ReturnByMail",
    "returnFees": "https://schema.org/FreeReturn"
  },
  "hasShippingService": {
    "@type": "ShippingService",
    "name": "Standard US shipping",
    "shippingConditions": [
      {
        "@type": "ShippingConditions",
        "shippingDestination": { "@type": "DefinedRegion", "addressCountry": "US" },
        "orderValue": { "@type": "MonetaryAmount", "minValue": 0, "maxValue": 49.99, "currency": "USD" },
        "shippingRate": { "@type": "MonetaryAmount", "value": 5.99, "currency": "USD" }
      },
      {
        "@type": "ShippingConditions",
        "shippingDestination": { "@type": "DefinedRegion", "addressCountry": "US" },
        "orderValue": { "@type": "MonetaryAmount", "minValue": 50, "currency": "USD" },
        "shippingRate": { "@type": "MonetaryAmount", "value": 0, "currency": "USD" }
      }
    ]
  }
}
</script>

Google recommends placing this markup on the one page that describes your shipping and return policy; it doesn’t need to be on every page. That single block replaces the shipping and return objects in every product’s JSON-LD. If the same policy also sits in Search Console, Google uses the Search Console version, so a store is better off keeping one source than two that drift apart.

WooCommerce Implementation for Product Schema Markup

WooCommerce already prints Product JSON-LD on every product page. Its WC_Structured_Data class has done that since WooCommerce 3.0, and the script lands in the page footer, so a look at the page source shows exactly what Google receives. On a current store, core outputs:

  • name, url, description (the short description, or the full one when the short one is empty) and the main product image
  • sku, which falls back to the product ID when the SKU field is empty
  • gtin, only when the product’s "GTIN, UPC, EAN, or ISBN" field holds a valid 8, 12, 13 or 14 digit code
  • brand, when the product has a WooCommerce brand (the first one only, with its logo if the brand has one)
  • an Offer with price, priceCurrency, priceValidUntil, availability (InStock, BackOrder or OutOfStock), url and seller, plus a priceSpecification that lists a sale price first and the regular price as the ListPrice
  • an AggregateOffer with lowPrice, highPrice and offerCount on a variable product whose variations have different prices
  • aggregateRating and the 5 most recent rated reviews, when star ratings are enabled and the product has ratings

It leaves these out:

  • mpn and itemCondition
  • shippingDetails and hasMerchantReturnPolicy
  • variant markup beyond that price range (no ProductGroup, hasVariant or variesBy)

Prices come out as decimal strings such as "189.00", which Google reads without trouble. So the job on a WooCommerce store is filling those gaps, and there are generally four ways to do it.

Path 1: Store-wide policies in Search Console or Organization markup. Google recommends one shipping and return policy for the whole business, so these should be set once. Search Console’s shipping and returns settings need no code and take priority over any markup. Organization markup, like the example in the section above, keeps the policy in your own code. Offer-level shippingDetails or hasMerchantReturnPolicy is then needed only for the exceptions.

Path 2: Extend core markup with WooCommerce’s filters. Core passes its Product array through the woocommerce_structured_data_product filter and each Offer through woocommerce_structured_data_product_offer, so missing properties can be added without replacing anything. The snippet below belongs in a small custom plugin or a child theme’s functions.php. It adds itemCondition to every Offer and an mpn from a custom field (_mpn here, so swap in the key your store uses for part numbers):

add_filter( 'woocommerce_structured_data_product_offer', function ( $offer, $product ) {
	$offer['itemCondition'] = 'https://schema.org/NewCondition';
	return $offer;
}, 10, 2 );

add_filter( 'woocommerce_structured_data_product', function ( $markup, $product ) {
	$mpn = $product->get_meta( '_mpn' );
	if ( $mpn ) {
		$markup['mpn'] = $mpn;
	}
	return $markup;
}, 10, 2 );

Both filters run before WooCommerce prints the script, so the new properties land in the same Product block instead of a second one. A store that sells used or refurbished stock should set itemCondition per product rather than hardcoding NewCondition.

Path 3: Hand product schema to an SEO plugin. Rank Math’s WooCommerce Product schema is free and pulls price, sale price, availability, images and ratings straight from the product. Brand and GTIN need setting up, and Pro adds custom brand values, a choice of GTIN key and variation support. Shipping and return policies aren’t automatic, though. Rank Math documents store-wide policies through Pro’s Schema Templates and product-level ones through code filters, and nothing syncs from WooCommerce shipping zones. The setup walkthrough is in our WordPress schema markup guide, and the broader plugin choice is covered in our best WordPress SEO plugins comparison. Whichever plugin runs the schema, the page source should show exactly one Product block afterwards.

Path 4: A custom plugin. Non-standard product types, complex variant pricing and B2B-only catalogs can outgrow both core and plugins. A custom plugin can remove core’s footer output and print its own JSON-LD from product meta, ACF fields and WC_Product methods. It gives full control and a full maintenance burden, since every WooCommerce change to prices, stock or variations becomes yours to follow.

For most WooCommerce stores we’d combine Path 1 and Path 2: policies set once for the store, core markup kept and extended. An SEO plugin earns its place when you need variant markup or already run one that replaces core’s output, and a custom plugin only when the catalog doesn’t fit either.

Shopify Implementation for Product Schema Markup

Shopify’s Online Store 2.0 themes (Dawn and most modern themes) ship Product JSON-LD by default. The default markup covers name, image, description, brand, offers (with price, currency, availability), and sku. It misses shippingDetails, hasMerchantReturnPolicy, and the structured review array.

Two paths to extend Shopify’s default product schema markup. First: edit the theme’s product-template.liquid (or the equivalent in your theme) and add the missing fields manually. The Shopify Liquid templating language exposes shop.address (for shippingDestination), settings (for return policy), and product.metafields (for custom data). Second: install a schema app like JSON-LD for SEO or Schema Plus. The apps inject the missing fields without requiring theme code changes. I default to the manual theme path on owned sites and the app path on agency client sites where theme access is restricted.

Whichever path, validate every product template (not just the default product) with the Rich Results Test. Variant products, gift cards, and digital downloads all use slightly different Liquid logic and the schema can drift between them.

Validation and Search Console Monitoring

Two validators, in this order:

  1. Schema.org Validator at validator.schema.org confirms your Product JSON-LD validates against the schema.org Product spec.
  2. Google Rich Results Test at search.google.com/test/rich-results confirms Google will treat the markup as eligible for the Product rich result, including the price snippet, star rating, and shipping/returns badges.

Run both on a representative sample: your bestseller, a low-volume product, a sale-priced product, a sold-out product, an out-of-stock-but-pre-order product. Each tests a slightly different schema configuration. After deploy, monitor the Merchant listings report in Google Search Console, plus the Product snippets report for review and comparison pages. Errors block rich-result eligibility; warnings flag missing recommended fields. Fix errors within a week. A missing shippingDetails or hasMerchantReturnPolicy is reported as a warning, not an error.

Probe AI search citations monthly. Search ChatGPT, Perplexity, and Gemini for "best [your category] under [price]" queries you should rank for. Check whether your brand and specific products appear in the citation list.

Common Product Schema Markup Mistakes

These mistakes pass a JSON syntax check, which is why they survive launch. Each one makes the markup say something the page doesn’t, or something Google’s guidelines don’t allow.

  • A price formatted for people: "$1,299.00" or "1.299,00" in price instead of 1299.00. Schema.org asks for a plain decimal with a period and the currency in priceCurrency. A number and a string are both valid, so there’s no need to cast either way.
  • Ratings shoppers can’t see: reviews or an aggregateRating in the markup that aren’t visible on the page, or ratings pulled in from another website. Google’s review guidelines rule out both, and a structured data manual action removes the page’s rich result eligibility without touching its web ranking.
  • Markup that disagrees with the page: availability says InStock while the page says sold out, or the markup carries last week’s price. Hardcoded values in a custom template and stale cached HTML both cause it.
  • A priceValidUntil date in the past: Google says a listing may not display once that date has passed, which is what happens when a sale ends and the markup keeps the old end date.
  • AggregateOffer on your own product page: product snippets accept it, but merchant listings need a single Offer from the seller, so a price range on a store’s own product page gives up merchant listing eligibility.
  • The same policy in two places: shipping and return details repeated in every product’s markup and also set in Search Console. Google uses the Search Console version, so edits to the markup change nothing and the two copies drift apart.
  • Identifiers left out: Google lists gtin, mpn and brand as recommended. A product with a real GTIN should carry it, and a product without one should leave the property out rather than invent a value.

Product Schema Markup Field Reference

This is the working spec sheet for a product schema markup deployment. It compresses Google’s merchant listing and product snippet documentation and the schema.org Product properties into one table, with the Required column following the merchant listing rules.

PropertyTypeRequired?Notes
nameTextRequiredProduct display name. Should match what’s visible on the page.
imageURL or array of URLsRequiredOne image minimum. Google recommends several high-resolution images (at least 50K pixels) in 16×9, 4×3 and 1×1.
offersOffer or AggregateOfferRequiredMerchant listings need Offer. AggregateOffer (lowPrice/highPrice) works for product snippets only.
descriptionTextRecommendedPlain text, no HTML.
brandBrand or OrganizationRecommendedBrand with a name. WooCommerce adds it when the product has a WooCommerce brand.
skuTextRecommendedInternal stock-keeping unit. Helps variant tracking.
mpnTextRecommendedManufacturer part number. Stronger than SKU for cross-reference.
gtin / gtin8 / gtin12 / gtin13 / gtin14TextRecommendedGlobal trade item number. Identifies the same product across retailers.
categoryText or CategoryCodeRecommendedPlain text under 750 characters, or a Google product category code.
color, size, weightVariousOptionalweight uses QuantitativeValue with unitCode (KGM, LBR, etc.).
aggregateRatingAggregateRatingRecommendedOnly ratings shoppers can see on the page. Google sets no minimum count.
reviewArray of ReviewRecommendedAuthor, datePublished, reviewRating, reviewBody. Match visible reviews.
offers.priceNumber or TextRequiredPlain decimal with a period, no currency symbol. Google’s examples use numbers such as 39.99.
offers.priceCurrencyText (ISO 4217)RequiredUSD, GBP, EUR, INR, etc.
offers.availabilityItemAvailabilityRecommendedInStock, OutOfStock, PreOrder, Discontinued, BackOrder.
offers.itemConditionOfferItemConditionRecommendedNewCondition (default), UsedCondition, RefurbishedCondition, DamagedCondition.
offers.priceValidUntilDateRecommendedA past date can stop the listing showing. ISO 8601, with time and timezone for sales.
offers.shippingDetailsOfferShippingDetailsRecommendedBest set store-wide under Organization or in Search Console; use here for exceptions. shippingRate + shippingDestination.
offers.hasMerchantReturnPolicyMerchantReturnPolicyRecommendedSame pattern: store-wide first, Offer-level for exceptions. applicableCountry + returnPolicyCategory (+ merchantReturnDays for a finite window).

A gap in the Required column makes the page ineligible for merchant listings. A gap in a Recommended row leaves the listing eligible, just with less detail for Google to show.

Product Schema Markup Gotchas

These five problems don’t show up as errors in a validator. They come from the way a live store changes after the markup is written, so it is wise to check each one on purpose.

Variable products get a price range. When a variable product’s variations have different prices, WooCommerce core puts an AggregateOffer with lowPrice, highPrice and offerCount on the parent page. That qualifies for product snippets but not for merchant listings, which need a single Offer. From WooCommerce 11.0, a URL that selects every variation attribute, such as ?attribute_pa_size=large&attribute_pa_color=red, gets a single Offer with that variation’s price and an inProductGroupWithID set to the parent’s SKU (or ID). Full variant markup, a ProductGroup with hasVariant and variesBy, still needs an SEO plugin or custom code.

Sale dates without a start or a time. Google’s sale-duration guidance asks for validFrom at the start of a sale and validThrough or priceValidUntil at the end, in ISO 8601 with a time and timezone. WooCommerce core writes a sale’s end date as a plain date such as 2026-11-30, never adds validFrom and sets priceValidUntil on a regular price to December 31 of next year. A store that runs short, timed sales should add validFrom and a full end timestamp through the Offer filter.

Cached pages freeze price and stock. JSON-LD sits inside the page HTML, so a full-page cache or a CDN keeps serving the old price and availability until that page is purged. The worst case is a sale that ends while the cached page still shows the sale price with a priceValidUntil date that has now passed. The cache should purge a product’s URL whenever its price or stock changes, and that is worth testing with a scheduled sale, not only with a manual edit.

Search Console settings win silently. When shipping or returns are set in Search Console or Merchant Center, Google uses those settings and ignores the markup for the same policy. Edits to shippingDetails then change nothing, which looks like a caching bug until someone opens the settings.

Two sources print Product markup. WooCommerce core prints its Product block in the footer, and an SEO plugin, a theme or a review widget can print another. The two can disagree on price or rating, and whether an extra source removes core’s output depends on that plugin. The page source should show exactly one Product block inside its JSON-LD scripts.

Product Schema Markup Priorities by Site Size

Every product schema markup plan should start with catalog size. A 50-SKU DTC brand needs different priorities than an 8,000-product Shopify storefront. The triage rule: small sites should ship complete schema on every product. Medium sites (500-2000 products) should ship complete schema on the top 100 revenue drivers first, then automate the rest. Large catalogs should focus on category templates and audit the parent-variant schema relationships.

The risk on large catalogs is treating every product the same. A store’s own sales report shows which products bring in most of the revenue, and those deserve the manual checks first: identifiers, variant markup, visible reviews and any shipping or return exceptions. The long-tail products can run on a default schema template.

Product Schema Markup FAQs

What’s required for product schema markup?

It depends on the Google feature. For merchant listings, on pages where shoppers can buy the product, Google requires name, image and offers, and each Offer needs price and priceCurrency. For product snippets, on review and comparison pages, Google requires name plus one of review, aggregateRating or offers. The shippingDetails and hasMerchantReturnPolicy properties are recommended rather than required, and Google prefers one store-wide policy under Organization markup to a copy on every product.

Does WooCommerce add product schema markup automatically?

Yes. WooCommerce core has printed Product JSON-LD on product pages since version 3.0. It covers name, description, image, SKU, price, currency, availability and seller, plus GTIN when the product has a valid one, brand when it has a WooCommerce brand, and aggregateRating with up to 5 reviews when star ratings are enabled. It doesn’t add mpn, itemCondition, shippingDetails or hasMerchantReturnPolicy, so those come from Search Console settings, Organization markup, WooCommerce’s structured data filters or an SEO plugin.

Should I use Product or IndividualProduct for schema markup?

Product is the one to use. Google’s merchant listing and product snippet documentation is written around Product, and variants belong in a ProductGroup with hasVariant and variesBy, which is Google’s variant markup. IndividualProduct describes one specific physical item, such as a numbered art print, so it suits one-off items rather than a normal catalog.

How many images should product schema markup include?

Google requires at least one image and recommends several high-resolution ones, each at least 50,000 pixels when width is multiplied by height, in 16×9, 4×3 and 1×1 aspect ratios. The image URLs must be crawlable and indexable, and the images must show the product being marked up. Google’s guidelines ask for crawlable URLs, not same-domain hosting, so a CDN works as long as Googlebot can fetch from it.

Why aren’t my product star ratings showing in Google?

Google publishes no minimum review count for stars. The causes its guidelines point to are ratings in the markup that shoppers can’t see on the page and ratings pulled in from another website. A page with two Product blocks can also carry two conflicting ratings. The Rich Results Test and the Merchant listings report in Search Console show which of these applies. Valid markup still doesn’t guarantee stars, because Google decides per search whether to show a rich result.

Can I use Product schema markup for digital products and SaaS?

Yes. Ebooks, courses and downloads can use Product with an Offer, and shipping markup doesn’t apply to them. SaaS pages are usually better described with SoftwareApplication, which adds operatingSystem, applicationCategory and softwareVersion alongside offers.

What’s the difference between Offer and AggregateOffer in product schema markup?

Offer is one price from one seller. AggregateOffer summarizes several offers with lowPrice, highPrice and offerCount, which suits comparison and marketplace pages. Google’s product snippets accept either, but merchant listings require an Offer from the seller. A store’s own product page should use Offer, and variants at different prices belong in ProductGroup markup rather than a price range.

Do I need GTIN, MPN and SKU all together?

Add whichever identifiers the product has. Google lists gtin (and gtin8, gtin12, gtin13, gtin14), isbn, mpn and sku as recommended. A GTIN identifies the same product at every retailer, the MPN is the manufacturer’s part number and the SKU is your own stock code. A product with no GTIN should leave the property out. WooCommerce outputs gtin from its GTIN, UPC, EAN, or ISBN field and uses the product ID as sku when the SKU field is empty.

How long does product schema markup take to show in Google?

There’s no fixed timeline, because Google has to recrawl the page before it uses new markup. The Rich Results Test shows eligibility straight away, and URL Inspection in Search Console can request a recrawl for important pages. Once Google reprocesses the page, the Merchant listings report updates. Even then Google doesn’t guarantee a rich result, since it decides per search whether to show one.

Does product schema markup help with AI search citations?

Nobody outside the AI companies has published how their shopping answers weigh Product JSON-LD, so any multiplier you read is a guess. Google says AI Overviews and AI Mode need no special schema.org markup, and it lists structured data that matches the visible page among ordinary SEO best practices. Accurate price, availability, brand and GTIN still help any system identify the exact product and store, and that is the part worth getting right.

Ship Product Schema This Week

Default WooCommerce and Shopify markup already covers the required fields, so the work left for this week is the recommended layer. The store-wide shipping and return policies come first, because one setting covers the whole catalog.

WooCommerce: set your standard shipping and return policies in Search Console or as Organization markup, add missing properties through WooCommerce’s structured data filters or your SEO plugin, then run a bestseller, a sale item, a sold-out product and a variable product through the Rich Results Test. Shopify: check what your theme’s Product JSON-LD already prints, set the store-wide policies the same way and use a schema app only for what the theme can’t output. On either platform, watch the Merchant listings report in Search Console for 30 days after the change.

From there, the maintenance is light. Re-check one product page each quarter to confirm that the price, availability and reviews in the markup still match what shoppers see, and re-validate after any theme, plugin or caching change, since those are the changes that quietly break markup that was right at launch.

Tell Google you want more of this.

Add Gatilab as a preferred source

One tap, and this site shows up more often in your own Top Stories, AI Overviews and AI Mode. Remove it any time.