Structured Data

Product and Merchant Listing Structured Data in 2026: What Changed and How to Keep It Accurate

Google added product categories, sale dates, an adult content label and a stricter fake-review rule to its product markup documentation in 2026. Here's what to mark up, how to keep the page, the markup and the Merchant Center feed in step, and how to test it.

On this page
  1. What changed in 2025 and 2026
  2. Product snippet or merchant listing
  3. What to mark up on a product page
  4. Keep the page, the markup and the feed in step
  5. Reviews and ratings after July 2026
  6. Common mistakes
  7. How to test
  8. What DeepSpider.ai checks, and what it doesn't
  9. Frequently asked questions
  10. The short version
  11. Sources
A product page, its JSON-LD markup and a Merchant Center feed showing the same price, stock status and sale end date.

A small outdoor shop runs a Black Friday sale. The rucksack page says £79 in big orange numbers, the old £89 crossed out next to it. The product markup in the page template still says 89.00, because nobody told the developer about the sale. The Merchant Center feed was updated on Thursday and says 79.00 GBP, but the sale end date in the feed is in the wrong time zone. On Monday morning the shop owner finds a handful of products disapproved in Merchant Center and has no idea which of the three sources Google believed.

That's what product structured data looks like in practice. The markup itself isn't hard. Keeping it true is the hard part, and in 2026 Google added several properties that give you more to keep true: product categories, sale start and end dates, an adult content label, and a sharper rule about fake reviews. This guide covers what changed, what to mark up, how to keep the page, the markup and the feed in step, and how to test it.

In short
Use merchant listing markup (Product with an Offer) on pages where people can buy from you. Mark up only what shoppers can see, keep it identical to your Merchant Center feed, put it in the initial HTML, and give sale prices a start and end date. The 2026 additions are optional unless they describe your products, apart from the adult label, which Google says you must use for adult-oriented products.

What changed in 2025 and 2026

Google's structured data documentation changes quietly, so it's worth reading its changelog rather than relying on memory. These are the product-related entries since early 2025, in date order.

Product-related changes in Google's documentation
DateWhat changedWho should care
13 February 2025Examples for sale, strikethrough and member prices, with priceType and the beta validForMemberTier propertyShops with sale or member pricing
14 March 2025returnPolicyCountry added to return policy examples, as it is required for MerchantReturnPolicyAnyone marking up returns
10 June 2025Loyalty programme markup documented and return policies moved to their own pageShops with a loyalty scheme
11 July 2025Merchant-level return policies must sit under Organization markup; offer-level policies support only a subsetAnyone marking up returns
12 November 2025Merchant shipping policy markup (ShippingService under Organization) documentedShops with a standard shipping policy
20 May 2026hasAdultConsideration added to merchant listing and product variant markupShops selling adult-oriented products
7 July 2026Product.category with Text and CategoryCode, plus a new Sale duration sectionMost shops
24 July 2026Review snippet guideline against fake and undisclosed incentivised reviewsAnyone showing star ratings

Most of these entries say the same thing in different words: Google wants your markup to carry the same data as a Merchant Center feed. The July 2026 entries say so directly. Category "aligns with the Google Merchant Center feed specifications for the product_type and google_product_category attributes", and Sale duration "aligns schema.org usage with the Merchant Center feed attribute sale_price_effective_date". The adult property, according to the May entry, "brings parity with the Merchant Center feed specification for the adult property".

Barry Schwartz covered the July change at Search Engine Roundtable the same day, and Roger Montti at Search Engine Journal read it the same way: the new category property is "closing a gap between what's marked up on the page and what's submitted through the Merchant Center feed". Neither addition is needed for eligibility. Category is listed as a recommended property, and the sale dates are optional details for shops that run sales.

Product snippet or merchant listing

Google treats product markup as two features with different rules. Product snippets are for pages where people can't buy directly, such as an editorial review. Merchant listings are for pages where shoppers can buy from you, and they unlock the richer shopping surfaces: popular products, the shopping knowledge panel and product annotations in Google Images.

The merchant listing guide is strict about two things. "Only pages where a shopper can purchase a product are eligible for merchant listing experiences, not pages with links to other sites that sell the product." And the offer must be an Offer, not an AggregateOffer, because "the merchant has to be the seller of the product".

That second rule catches more shops than you'd think. Brodie Clark described a large ecommerce client whose product pages used AggregateOffer instead of Offer in their Product markup. The Rich Results Test showed only product snippets for those pages, so they weren't eligible for merchant listings at all. He recommended inProductGroupWithID instead, and later Google's product variant markup, which brings us to the next point.

What to mark up on a product page

Start with the required properties for a merchant listing: name, image and an offers object with price and priceCurrency. The price is a number and, unlike product snippets, merchant listings need it to be greater than zero. Then add what you actually know: availability, brand, a GTIN, SKU, description, condition, and the new category. Google's advice in its product markup introduction is to give as much accurate product information as you have, "without concern for the exact experiences that will use it".

Here's a compact example for a product on sale. Every property exists on schema.org and in Google's merchant listing guide, and the category path is a real entry in Google's product taxonomy.

JSON-LD
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Ridge 40 Rucksack",
  "sku": "R40-GRN",
  "gtin13": "5012345678900",
  "image": [
    "https://www.example.com/img/ridge-40-1x1.jpg",
    "https://www.example.com/img/ridge-40-4x3.jpg",
    "https://www.example.com/img/ridge-40-16x9.jpg"
  ],
  "description": "A 40 litre walking rucksack with a ventilated back panel.",
  "brand": { "@type": "Brand", "name": "Example Outdoor" },
  "category": [
    {
      "@type": "CategoryCode",
      "inCodeSet": "https://www.google.com/basepages/producttype/taxonomy-with-ids.en-US.txt",
      "codeValue": "Luggage & Bags > Backpacks"
    },
    "Rucksacks > Day packs"
  ],
  "offers": {
    "@type": "Offer",
    "url": "https://www.example.com/products/ridge-40/",
    "price": 79.00,
    "priceCurrency": "GBP",
    "validFrom": "2026-11-20T08:00:00+00:00",
    "priceValidUntil": "2026-11-30T23:59:59+00:00",
    "priceSpecification": {
      "@type": "UnitPriceSpecification",
      "priceType": "https://schema.org/StrikethroughPrice",
      "price": 89.00,
      "priceCurrency": "GBP"
    },
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition"
  }
}

The GTIN above is a placeholder. Use your real one, in numerical form: Google says it doesn't support the URL form for GTINs.

Product category

The category property now takes plain text, a CategoryCode object, or a mix of both in one array. Plain text is your own category, the equivalent of product_type in a feed, and Google recommends keeping it under 750 characters. CategoryCode points at Google's product taxonomy: set inCodeSet to the taxonomy URL and codeValue to either the numeric ID or the full path with > between levels.

A category array mixing a CategoryCode object that maps to google_product_category with plain text values that map to product_type.
Plain text describes your own category tree. A CategoryCode points at Google's taxonomy. You can mix both in one array.

If you already send google_product_category and product_type in a feed, copy the same values into the markup rather than inventing a second classification. On schema.org, codeValue and inCodeSet are the only properties CategoryCode adds to the general DefinedTerm type, and they're the two Google's guide uses, so there's nothing else to fill in.

Sale duration

This is the change most shops will use. A sale price gets a start with validFrom and an end with either validThrough or priceValidUntil. Google's best practices are short: give both dates, make sure the start isn't after the end, and include the time and time zone.

Where you put the dates depends on where the sale price lives. If the price on the Offer is the current sale price, put the dates on the Offer, as in the example above. If the sale price sits in its own price specification next to a strikethrough price, put validFrom and validThrough on that specification, because "the priceValidUntil property isn't applicable to the PriceSpecification type".

A timeline for a sale price from 20 November 08:00 to 30 November 23:59 UTC, with validFrom and priceValidUntil or validThrough on the Offer, and validFrom and validThrough on a PriceSpecification.
Give the sale both dates, with a time and time zone. On a PriceSpecification, use validThrough rather than priceValidUntil.

One line in the property table is worth remembering: "Your listing may not display if the priceValidUntil property indicates a past date." A sale that ended in March but still sits in the template is lowkey easy to miss, because the page itself looks fine. If your platform can't remove the dates when a sale ends, it's safer to leave them out than to leave them stale.

Adult-oriented products

The hasAdultConsideration property is the one 2026 addition with a "must". If you sell products that Google's adult-oriented content policy covers, "you must use this property to label them as adult-oriented". Google Search supports one value, https://schema.org/SexualContentConsideration. Labelled products can still appear in Shopping ads and free listings, subject to age and country restrictions. If your shop sells nothing of the kind, leave it out.

Shipping, returns and loyalty

Policies that apply to the whole shop belong under Organization markup, usually on one page such as the homepage or a policy page: hasMerchantReturnPolicy for returns (Google made this explicit for merchant-level policies in July 2025), and hasShippingService with a ShippingService for shipping (documented since November 2025). Use offer-level shippingDetails and return policies only for products that differ from the standard, and know that they support fewer properties than the organisation-level versions. Loyalty programmes are defined once, separately, and the offer only carries the member price or points. Google says shipping and returns benefits for loyalty members aren't supported at the moment.

Google is also explicit about what wins when the same policy is defined in several places.

Google's order of precedence for shipping and return policies, from product-level Merchant Center feeds down to Organization-level markup.
When shipping or return policies are defined in several places, Google uses this order. Markup is the weakest source.

So if you configure returns in Merchant Center or Search Console, that setting beats your markup. Brodie Clark notes a practical consequence: Search Console may still flag missing hasMerchantReturnPolicy or shippingDetails in the merchant listings report when those settings live in Merchant Center. If Merchant Center is set up correctly, he says, you can safely ignore those warnings.

Variants

If a product comes in sizes or colours, use ProductGroup with hasVariant, variesBy and productGroupID, and give each variant its own Product with a unique SKU or GTIN. Google's variant guidelines, which Jasmine Drudge-Willson walks through at Schema App, add a requirement that's easy to miss: each variant must be selectable directly with its own URL, showing the right image, price and availability. She notes that before this markup existed, her team handled variants with AggregateOffer, with one Offer per SKU, or with ProductModel. The first of those is exactly the pattern Brodie Clark saw blocking merchant listings.

Keep the page, the markup and the feed in step

Merchant Center checks your feed against your pages. Its help page on price mismatches explains that Googlebot "routinely crawls your website landing pages and compares the price [price] attribute in your data source with the prices on your landing page or in your structured data markup". When they disagree, "the product with the mismatch may be disapproved". After you fix it, products are reapproved once Googlebot crawls them again, which can take hours or days.

The visible page and the markup show 89.00 pounds while the Merchant Center feed still says 79.00 GBP, a mismatch that can get the item disapproved.
Here the page and the markup agree, but the feed is out of date. Fix whichever source is wrong rather than forcing the others to match it.

A few rules from that page and the merchant listing guide decide most cases:

  • Use the HTML your server returns. Merchant Center says Googlebot "crawls the data present in the HTML returned from your web server", and data added by JavaScript after load "will trigger an error". The merchant listing guide warns that JavaScript-generated markup "can make Shopping crawls less frequent and less reliable".
  • Mark up what shoppers see. "Prices reflected in the HTML need to match exactly the prices uploaded in Merchant Center." The same page lists a price shown to users that differs from the price crawled by bots as a common cause of mismatches.
  • Get the sale timing right. The same help page lists a wrong sale_price_effective_date start date or time zone as a common cause of an incorrect price. Copy one set of dates into both the feed and validFrom and priceValidUntil.
  • One currency per URL. If you sell in pounds and euros, use a separate URL for each currency.
  • Watch for duplicates. Shopify's structured_data Liquid filter outputs a Product, or a ProductGroup when the product has variants. If an SEO or reviews app adds its own Product block on top, you can end up with two blocks that say different things. Pick one source and remove the other.

If you're curious why Shopping crawls matter for the rest of your site, our piece on how every Google crawler now shares one crawl budget explains how Storebot-Google draws on the same capacity as Googlebot.

Reviews and ratings after July 2026

The review snippet guidelines now say: "Don't include fake or undisclosed incentivized reviews on your page or in your structured data markup." The examples are reviews "that aren't based on a genuine experience" and reviews given for "money, discounts, vouchers, or free products" without clear, prominent disclosure. Schwartz pointed out that this brings the structured data rules more in line with Google Business Profile guidelines.

Note the word "undisclosed". The guideline doesn't ban reviews from people who got a free sample, as long as that's clearly said. A star rating built on freebies nobody mentions, though, is a big yikes.

The older review rules still apply too. The reviews and the aggregate rating you mark up must be visible on the page, you shouldn't aggregate reviews from other websites, and since November 2025 Google asks you to avoid "multiple ways of indicating what's being reviewed". Nest the review inside the Product, or use itemReviewed, not both.

Common mistakes

Mistakes worth checking for first
MistakeWhat happensFix
AggregateOffer on a page you sell fromPage only eligible for product snippets, not merchant listingsUse one Offer, and ProductGroup for variants
Price with a currency symbol or comma, such as "£149.00" or "24,99"Google expects a number in pricePut a plain number in price and the code in priceCurrency
Old priceValidUntil left in the templateListing may not displayRemove or update the date when the sale ends
Dates like 30/11/2026Not the ISO 8601 format Google asks forUse 2026-11-30T23:59:59+00:00
Markup added by JavaScript after loadLess frequent, less reliable Shopping crawls and possible mismatchesPut Product markup in the initial HTML
Markup price differs from visible or feed priceItems may be disapproved in Merchant CenterUpdate all three from one source of truth
Returns and shipping repeated on every offerMore to maintain, offer level supports fewer propertiesDefine standard policies once under Organization
Theme and app both output ProductTwo conflicting blocks for one productKeep one source and remove the other

How to test

Google's own steps are a good order to work in. Validate a few templates with the Rich Results Test, fix critical errors, and consider the non-critical ones. Then publish and use URL Inspection to see the page as Google sees it, including whether it's blocked by robots.txt, noindex or a login. After that, watch the merchant listings and product snippets reports in Search Console. Google suggests checking them after the first deployment, after every template release, and when analysing traffic. Google notes that a drop in valid items without a matching rise in invalid items may mean the markup is no longer on your pages.

One page at a time isn't enough for a shop with thousands of products, because structured data problems usually live in templates. Geoff Kennedy at Sitebulb put it simply: a single template implementation "might populate structured data across thousands of pages", so you want a crawler that tests the whole site and groups issues by template. Screaming Frog's structured data tutorial makes a useful distinction there too: errors cover Schema.org problems and missing or broken required properties, while warnings are for recommended ones that "do not disqualify you from being eligible for rich snippets". Fix errors first, then decide which warnings describe information you actually have.

For the feed side, Merchant Center's Needs attention page is where mismatches are reported. A site crawler sees your pages, not your feed.

What DeepSpider.ai checks, and what it doesn't

DeepSpider.ai checks structured data on every audited HTML page, in the Structured Data / Schema topic. For product pages, these checks matter most:

  • Invalid JSON-LD (Error): JSON-LD blocks in the source HTML that don't parse.
  • Invalid property values (Warning): non-numeric prices and malformed dates in priceValidUntil, datePublished, dateModified and dateCreated. A price written as "£149.00" or a date written as "30/11/2026" shows up here.
  • Type-specific field issues (Needs review): a Product without a name, or without at least one of offers, review or aggregateRating.
  • Rating and review inconsistencies (Warning): rating values outside their declared range, reversed bounds, or negative and non-integer counts.
  • Unknown schema types or properties (Needs review): typos such as "priceValidUnitl", checked against a bundled Schema.org vocabulary that includes CategoryCode and hasAdultConsideration.

Two informational views, Structured data inventory and Pages without detected structured data, show which types each page carries and where markup is missing. Errors and Warnings count towards the Website Health Score; Needs review checks and the informational views don't.

DeepSpider.ai check Invalid property values for the fictional shop larchmoor-outdoor.test, listing three product pages with a price containing a currency symbol, a price with a decimal comma and priceValidUntil dates that are not valid ISO dates, with How to fix it guidance above.
The Invalid property values check in DeepSpider.ai on a fictional test shop, larchmoor-outdoor.test. Two prices contain a currency symbol or a comma, and two priceValidUntil dates aren't valid ISO dates.

It's just as important to know the limits:

  • DeepSpider reads JSON-LD in the HTML your server returns. It doesn't run JavaScript, so markup injected later isn't checked. That's also roughly how Merchant Center reads prices, but it means a JavaScript-only setup shows up as missing.
  • Microdata and RDFa are detected, not validated.
  • It doesn't compare your markup with the visible price or with your Merchant Center feed, and it doesn't check whether a priceValidUntil date is in the past, whether validFrom and validThrough are valid dates, or whether the sale starts before it ends. Use Merchant Center and the Rich Results Test for those.
  • It isn't Google's rich result eligibility test. Passing every DeepSpider check doesn't guarantee a merchant listing, and missing optional properties such as category aren't reported as failures.

If you want more on reading an audit like this without chasing every count, see why technical SEO audits need context, not just issue counts.

When to leave it alone
If your products already appear correctly in Shopping and your Merchant Center feed carries category, sale dates and policies, adding the same values to markup is good housekeeping, not an emergency. Do it at the next template change, and test before you ship.

Frequently asked questions

Do I still need markup if I have a Merchant Center feed?

Google recommends both. It says providing structured data and a feed "maximizes your eligibility to experiences and helps Google correctly understand and verify your data". Markup also helps pages that aren't in your feed, and some experiences combine the two.

Does product markup improve rankings?

Google doesn't describe it as a ranking factor. It makes pages eligible for richer results and shopping surfaces, which can change how many people click. Treat any claim of a direct ranking boost with caution.

Should I add Product markup to category pages?

Google recommends focusing on individual product pages rather than pages that list products. A category page isn't a specific product, so it isn't eligible for product rich results.

Should I use validThrough or priceValidUntil?

On the Offer, either works for the end of a sale. On a PriceSpecification, use validThrough, because priceValidUntil isn't a property of that type.

Is the new category property required?

No. It's a recommended property. It's most useful when you already maintain Google product categories and product types in a feed and want the page to say the same thing.

Can I mark up reviews from people who received a free product?

Yes, if the incentive is clearly and prominently disclosed. What the July 2026 guideline rules out are fake reviews and incentivised reviews that hide the incentive.

The short version

Product markup in 2026 is mostly about agreement. Google added category, sale dates and an adult label so your markup can carry the same facts as your Merchant Center feed. Use one Offer per product you sell, ProductGroup for variants, Organization markup for shop-wide shipping and returns, and put it all in the initial HTML. Give sales a start and an end, and remove the dates when the sale is over. Then test templates rather than pages, and let Merchant Center tell you when the feed and the page drift apart.

Sources