# Auditing a product page's structured data

> How to audit a product page's structured data: the published JSON-LD against what shoppers see, the Rich Results Test and Search Console.

URL: https://clione.ai/guides/audit-product-page-structured-data/
Lang: en

A tool that says "valid" hasn't looked at your store: it has looked at your code. Auditing the [structured data](/glossary/structured-data/) on a [product page](/glossary/product-page/) is something else, and the part that matters most isn't done by any tool: checking that the [JSON-LD](/glossary/json-ld/) says the same thing as the page the shopper sees, because that's what decides whether the page is eligible for Google's [rich results](/glossary/rich-results/). This guide goes in order: which rule each check applies, how to read the markup on a published product page, what to compare property by property, what the [Rich Results Test](/glossary/rich-results-test/) and the official validator for [schema markup](/glossary/schema-markup/), the [Schema Markup Validator](/glossary/schema-markup-validator/), each validate, and what [Search Console](/glossary/google-search-console/) tells you about the whole site.

## The rule you audit against

Before you open anything, it helps to know what you're comparing against. Google sums it up in its general guidelines: **your structured data has to be a true representation of the page content**, and you don't mark up what readers can't see. Its example comes from another field, but it applies to a store as-is: if the JSON-LD describes an artist, the body of the page has to describe that same artist. You also don't mark up misleading content, like fake reviews, and you have to include every required property for the result type, because an item that's missing one isn't eligible for a [rich result](/glossary/rich-results/).[^google-sd-policies]

For a product page, the type is [Product](/glossary/product-schema/) and the experience that matters is [merchant listings](/glossary/merchant-listings/): the price, availability, shipping and returns that Google can show in Search, in the shopping knowledge panel or in Google Images.[^google-merchant-listing] Google separates two cases. Product snippets are for pages where you can't buy directly, such as reviews; merchant listings are for pages where the customer buys from you. If your markup has what merchant listings ask for, the page can also be eligible for product snippets.[^google-product-intro]

And one warning that saves arguments: **correct markup doesn't guarantee a rich result**. Google doesn't promise to show structured data even when the Rich Results Test says it's fine, and among the reasons it gives are markup that doesn't represent the main content, content hidden from users, and an error the test can't detect.[^google-sd-policies]

## Step 1: open the published product page, not the admin panel

Your platform's admin panel shows what you have saved. What Google and any agent read is what the page publishes, and between the two sit the theme, the apps and the cache. Pick a product page that can be bought, open it in a private window and write down, with the date and time, what a shopper sees:

- the product name, as it appears in the [page title](/glossary/meta-title/);
- the price, with its currency, and whether there's a strikethrough price;
- the stock message: in stock, out of stock, preorder;
- the average rating and the number of reviews, if they're visible;
- the barcode or the reference number, if the product page shows them.

That list is your reference for step 3. If the product page has variants, note which one is selected on load: Google only supports product results on pages focused on a single product or on several variants of the same product.[^google-merchant-listing] How to mark up variants is covered in the [product variants](/glossary/product-variants/) entry.


## Step 2: find the markup in the page's HTML

Open the product page's source ("View page source", or `view-source:` in front of the URL) and search for `application/ld+json`. Note how many blocks there are, which `@type` each one declares, and whether any of them uses [`@graph`](/glossary/graph/) or [`@id`](/glossary/id-json-ld/) to link nodes. Google supports three formats, JSON-LD (the one it recommends), [microdata](/glossary/microdata/) and [RDFa](/glossary/rdfa/),[^google-sd-policies] so search for `itemprop` and `itemscope` too: a theme may carry the markup in microdata, spread across the HTML.

Two format checks before you read the content:

- **Is it in the server's HTML, or does JavaScript add it?** Google processes the markup it finds in the DOM when it [renders the page](/glossary/javascript-rendering/),[^google-js-sd] but it recommends putting Product markup in the initial HTML, and it warns that JavaScript-generated markup can make Shopping crawls less frequent and less reliable, which is a problem for data that changes fast, like [price and availability](/glossary/price-and-availability/).[^google-merchant-listing] The difference between the source and the rendered page, and how to see it, is in [the guide to what AI crawlers see in your store](/guides/what-an-ai-crawler-sees/), which also explains what an AI [crawler](/glossary/web-crawler/) is.
- **Are there comments inside the JSON-LD?** The Rich Results Test ignores them, but the JSON-LD standard doesn't support them and they can cause errors in real use. Google asks you to remove them before you publish.[^google-rrt]

If you find more than one Product node for the same product page, for example one from the theme and another from an app, note it and audit each one in the next step: each node is a statement about the same product, and all of them have to match the page.

## Step 3: compare each property with what the shopper sees

This is the step no tool does. Put your list from step 1 next to the JSON-LD and go through the properties that merchant listings ask for, using the names from the [Schema.org](/glossary/schema-org/) vocabulary. The required part is short: the Product needs a name (`name`), an image (`image`) and `offers`, which holds an [Offer](/glossary/offer-schema/) with a price and a currency.[^google-merchant-listing]

| Property                                      | What Google or Schema.org asks for                                                                                                                                                                                                                                                    | What you check on the product page                                                 |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `name`                                        | Required: the product's name.[^google-merchant-listing]                                                                                                                                                                                                                               | That it's this page's product, not another variant's.                              |
| `image`                                       | Required. The image has to represent the marked-up product, and its URL has to be crawlable and indexable.[^google-merchant-listing]                                                                                                                                                  | That the URL opens a photo of this product.                                        |
| `offers`                                      | Required. Merchant listings require an Offer, because the merchant has to be the one selling.[^google-merchant-listing]                                                                                                                                                               | That there's an offer and that it's yours.                                         |
| `price`                                       | **Greater than zero** for merchant listings; product snippets accept a price of zero.[^google-merchant-listing][^google-product-snippet] Schema.org asks for a period as the decimal separator and the currency in its own property, not a symbol inside the value.[^schemaorg-offer] | That it's the price the shopper pays today.                                        |
| `priceCurrency`                               | A three-letter ISO 4217 code. If you sell in several currencies, Google asks for a separate URL per currency.[^google-merchant-listing]                                                                                                                                               | That it's the currency charged at that URL.                                        |
| `availability`                                | Recommended. A single value from Schema.org's list, such as `InStock`, `OutOfStock`, `PreOrder` or `BackOrder`; Google accepts the short name without the URL.[^google-merchant-listing][^schemaorg-availability]                                                                     | That it says the same thing as the stock message.                                  |
| `priceValidUntil`                             | Recommended. If the date has passed, the listing may not be shown.[^google-merchant-listing]                                                                                                                                                                                          | That it isn't the end date of a sale that's already over.                          |
| `gtin`, `gtin8`, `gtin12`, `gtin13`, `gtin14` | Recommended. Google advises using the most specific one and only accepts the numeric form, not the URL.[^google-merchant-listing] Schema.org asks for 8, 12, 13 or 14 digits with a valid check digit.[^schemaorg-gtin]                                                               | That it's the code on the packaging or in your catalog, with no spaces or letters. |
| `aggregateRating`                             | Recommended. It carries `ratingValue` and either `ratingCount` or `reviewCount`, and **the rating has to be visible on the page**.[^google-review-snippet]                                                                                                                            | That the average and the number of reviews match what's visible.                   |
| `brand.name`, `sku`, `mpn`                    | Recommended. A single `brand` value; the `sku` with no spaces.[^google-merchant-listing]                                                                                                                                                                                              | That they match the product page and your catalog.                                 |

The [GTIN](/glossary/gtin/) deserves a separate look: compare it digit by digit with the one in your catalog, where you may have it stored as an [EAN code](/glossary/ean-code/). One digit too many or too few doesn't show on the page, but it leaves the code without a valid check digit. The same goes for the [SKU](/glossary/sku/) and the [MPN](/glossary/mpn/). How price and availability line up across the page, the markup and the [feeds](/glossary/product-feed/) is covered in [price and availability](/glossary/price-and-availability/), and the average rating in [AggregateRating](/glossary/aggregaterating/).

A fictitious example to train your eye. The product page shows "$34.90", "Out of stock" and no stars; the markup says something else in three places:


```json
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Example sneaker",
  "image": "https://your-store.example/img/sneaker.jpg",
  "gtin13": "8400000000000",
  "offers": {
    "@type": "Offer",
    "price": 29.9,
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": 4.6,
    "reviewCount": 12
  }
}
```


The price isn't the one being charged, the availability says the product is in stock when the page says it isn't, and the rating describes something the shopper can't see. **Neither of the tools in the next steps would flag these three mismatches as errors**: the code is correct and has everything required. The mismatches only show up when you put the markup next to the page.

## Step 4: run the URL through the Rich Results Test

The Rich Results Test answers which Google rich results your page can generate.[^google-test-sd] Use it with the published URL, not by pasting code: Google recommends that because code mode has limits with JavaScript.[^google-js-sd] Here's how to read what it sees:

- **The crawl.** The test comes in as Google-InspectionTool, without your credentials, and as a smartphone by default. If a [`robots.txt`](/glossary/robots-txt/) rule or a [`noindex`](/glossary/noindex/) stops Google from crawling the page, it can't be tested. If the test can't load some resources, it warns you, and the result may change from one run to the next.[^google-rrt]
- **What it detects.** It lists the rich result types it finds, with a status per URL: valid items, valid items with warnings, invalid items, or structured data with syntax errors.[^google-rrt] On a product page, you'd expect to see merchant listings and product snippets.
- **Errors and warnings.** When you open one, the code explorer takes you to the line in the code, and that code is the rendered version, not the original HTML.[^google-rrt] Google asks you to fix the critical errors; the non-critical ones are worth fixing because they improve the quality of your markup, but you don't need to fix them to be eligible.[^google-merchant-listing]


Google also writes down what this test doesn't do: content problems that aren't syntax problems, like markup that doesn't match the page, **it can't identify**.[^google-merchant-listing] That's why it comes after step 3, not instead of it.

## Step 5: run the same URL through the Schema Markup Validator

The [Schema Markup Validator](https://validator.schema.org/)[^smv] validates any type of Schema.org markup without Google's own checks.[^google-test-sd] It's useful for what Google's test doesn't look at: types Google doesn't use for rich results and properties that don't appear in its documentation.

Running the same product page through both tools shows where they disagree, because each one validates against a different rule. Two examples from the sources:

- **The GTIN as a URL.** Schema.org accepts a GTIN written as a GS1 Digital Link URL;[^schemaorg-gtin] Google only accepts the numeric form.[^google-merchant-listing] The validator passes it and Google doesn't use it.
- **The zero price.** A `"price": 0` works for product snippets,[^google-product-snippet] but merchant listings require a price greater than zero.[^google-merchant-listing]

Neither tool checks that what you validate is what has been published across all your product pages, or that it matches what the shopper sees. That distinction has a page of its own: [validating markup is not the same as verifying it's published](/comparisons/validate-vs-verify/).

## Step 6: check the product reports in Search Console

Both tests look at one URL; Search Console looks at the whole site. The product reports are under Shopping, not under Enhancements, where the other rich result reports are.[^gsc-rich-report] There are two:[^google-merchant-listing][^gsc-shopping]

- **Merchant listings**, for pages where the shopper can buy. It includes the product snippet checks that apply to items with an Offer.
- **Product snippets**, for other product pages, such as reviews or comparison sites. For a store, you only need to look at it for the pages that aren't merchant listings.

Each report shows a chart of valid and invalid items and two tables: critical issues, which block the rich result, and non-critical ones, which affect how it looks. **The numbers count items, not pages**, and the report is a sample, not the full list: for a URL that isn't there, Google points you to the URL Inspection tool.[^gsc-rich-report] If you don't see the report at all, Google hasn't found valid markup of that type on your site.[^gsc-shopping]

How to read the changes, according to Google: after a new template, **a rise in invalid items** may mean the template doesn't work; **a drop in valid items without a matching rise in invalid ones** may mean the page has stopped carrying the markup. When you fix something, inspect the live URL to see whether the problem persists, and request validation from the report.[^google-merchant-listing]

URL Inspection closes the audit of a specific product page. Its enhancements section shows the rich results Google found the last time it indexed the page, with their errors and warnings; "View crawled page" shows the HTML your server returned, and the live test checks the current version, with a screenshot of what it rendered.[^gsc-url-inspection]


## The mismatches the rules look for

If you only have ten minutes per product page, these are the mismatches that Google's and Schema.org's own rules point to, and where they show up:

- **The price in the markup isn't the one on the page.** It breaks the true-representation rule.[^google-sd-policies] If you use [Google Merchant Center](/glossary/google-merchant-center/), its automatic updates read the price, availability and condition on the page to correct the feed: its example is a product at $4 in the feed and $3 on the page, which becomes $3. But they may not work for products whose price or availability changes often, for example more than once a day, and if they detect a mismatch, the product may be disapproved instead of updated.[^mc-automations]
- **The availability doesn't match the stock message**, or carries more than one value.[^google-merchant-listing]
- **A rating that isn't visible on the page.** If you use `aggregateRating`, the shopper has to be able to see that rating on the product page.[^google-review-snippet] How Google treats them is covered in [product reviews](/glossary/product-reviews/).
- **An expired `priceValidUntil`**, which can keep the listing from being shown.[^google-merchant-listing]
- **A GTIN in URL form, or with a check digit that doesn't add up.**[^google-merchant-listing][^schemaorg-gtin]
- **Shipping and returns declared in several places.** Google applies an order, from strongest to weakest: the Merchant Center feed, the Content API for Shopping settings, the Merchant Center or Search Console settings, the offer markup on the product page and, last, the [Organization](/glossary/organization-schema/) markup.[^google-merchant-listing] If the product page and the organization say different things, the product page wins. The details are in [OfferShippingDetails](/glossary/offershippingdetails/) and [MerchantReturnPolicy](/glossary/merchantreturnpolicy/).

## What we tested

- **The product page.** A real one, with the store's written permission (the page then adds `real-example`), or one built for the test and labeled "fictitious example". Public, purchasable or with a purchase that can be simulated, with a price greater than zero, a stock message, visible ratings and a GTIN. Note the platform and its version, the theme and its version, the apps that touch the markup, and the date.
- **The six steps, in order.** The step 1 list with the time; the number of JSON-LD blocks and Product nodes from step 2, and whether they're in the server's HTML; the step 3 table filled in, property by property, with what the markup says and what the page says; the Rich Results Test and Schema Markup Validator results for the same URL on the same day; and the two Search Console reports with their date.
- **The screenshots, dated.** The three this guide has set up: the product page with the data highlighted, the Rich Results Test and the merchant listings report. With the date visible or in the caption, and no customer data.
- **What gets published.** The table below, any mismatch found with its fix, and the limits: one product page, one day, and whatever Google shows that day.

Tested on: platform and version, theme and version, date.

| Check                                | What the page says | What the markup says | Do they match? | Tool that catches it |
| ------------------------------------ | ------------------ | -------------------- | -------------- | -------------------- |
| Name                                 |                    |                      |                |                      |
| Price and currency                   |                    |                      |                |                      |
| Availability                         |                    |                      |                |                      |
| GTIN                                 |                    |                      |                |                      |
| Average rating and number of reviews |                    |                      |                |                      |
| Rich Results Test                    |                    |                      |                |                      |
| Schema Markup Validator              |                    |                      |                |                      |
| Search Console: merchant listings    |                    |                      |                |                      |
