Product structured data schema: the properties Google reads, and the supplier values that fail them
Product structured data tells Google what a page sells: a name, an image and an offer with a price and a currency, and ideally a stock status, a brand and a GTIN. Those values come from the same columns a supplier file gets wrong, and a price or stock status that disagrees with the page or the feed can get a product disapproved. Here is what each of Google’s two product markups requires, how one supplier row becomes markup, and what to fix first.
Have a supplier file like this? Send it exactly as it arrived and see the same products before and after.
What product structured data is, and which kind a shop page needs
A supplier’s spreadsheet arrives with a Trade column reading £312.50, an RRP column reading £625.00, a Stock column that says Yes and an EAN column that Excel has turned into 3.23457E+12. Each of those cells ends up on a product page, and in the structured data that tells Google what the page sells. The values that fail the markup are the values that were wrong in the file.
A product structured data schema is the set of schema.org types and properties that describe a product in a form a machine can read: a Product with a name, an image, a brand and identifiers, and nested inside it an Offer with a price, a currency and an availability. With it in place, Google says, “your product information can appear in richer ways in Google Search results”, price and availability included.1
Google reads two classes of product markup. Product snippets are for pages “where people can’t directly purchase the product”, such as an editorial review. Merchant listings are for pages “where customers can purchase products from you”, with more room for sizing, shipping and returns.1 A retailer’s product page is a merchant listing, and meeting its requirements “means that your product pages can also be eligible for product snippets”.1
The markup is one of two ways to give Google product data; a Merchant Center feed is the other. Providing both, Google says, “maximizes your eligibility to experiences and helps Google correctly understand and verify your data”.1 Verification is where a supplier’s values let the markup down.
What each kind requires, property by property
Google’s two feature pages each list required and recommended properties, and the lists differ. Items missing a required property “are not eligible for rich results”; recommended ones add detail.5 For a product snippet, Product needs a name, and “Product snippets require either review or aggregateRating or offers”.2 For a merchant listing, Product needs name, image and offers, and the offer must be an Offer, not an AggregateOffer, “as the merchant has to be the seller of the product”.3
Inside the Offer, price is required for both. Merchant listings add two conditions: “merchant listing experiences require a price greater than zero”, and priceCurrency, a three-letter ISO 4217 code, is required.3
| Property | Product snippet | Merchant listing |
|---|---|---|
| name | Required | Required |
| image | Not listed | Required; URLs must be crawlable and indexable |
| offers | One of offers, review or aggregateRating | Required, and an Offer rather than an AggregateOffer |
| offers.price | Required in the Offer; 0 is allowed | Required, and greater than zero |
| offers.priceCurrency | Recommended | Required, three-letter ISO 4217 |
| offers.availability | Recommended, one value | Recommended, one value |
| offers.itemCondition | Not listed | Recommended, one value |
| offers.priceValidUntil | Recommended | Recommended |
| offers.shippingDetails | Not listed | Recommended; a business-level policy is preferred |
| offers.hasMerchantReturnPolicy | Not listed | Recommended; a business-level policy is preferred |
| brand.name, gtin, mpn, sku | Not listed | Recommended |
| description | Not listed | Recommended, and strongly so |
Below the required line, the merchant listing page recommends the properties a shopper’s filters would recognise: brand.name, the identifiers gtin, gtin8, gtin12, gtin13, gtin14 and mpn, then sku, description, category, color, material, pattern and size.3 Three carry format rules that supplier data routinely breaks: the GTIN must be “in the numerical form”, the brand takes “at most one brand name”, and “The sku value must not contain any whitespace characters”.3
Shipping and returns sit on the Offer as shippingDetails and hasMerchantReturnPolicy, both recommended, but for each Google would rather have one policy for the business, “under Organization markup instead”, keeping the Offer-level property for products whose policy differs. For the shipping details enhancement, an Offer-level shippingDetails needs a deliveryTime, a shippingDestination and a shippingRate in the offer’s currency.3
A supplier row becoming markup
Here is one product from a bathroom supplier’s file, with the property each column feeds and the value the markup needs. The supplier and brand are invented; the damage is the kind real files carry.
| Supplier column | Property | Supplier sent | In the markup |
|---|---|---|---|
| Description | name | S/E BATH 1700X700 WHT 0TH | Rivelin 1700 × 700 mm single ended bath, white, no tap holes |
| Brand | brand.name | RIVELIN BATHS LTD | Rivelin |
| EAN | gtin13 | 3.23457E+12 | 3234567890126 |
| Part no. | mpn | RB-1770-SE followed by a space | RB-1770-SE |
| Your SKU | sku | BATH 1770 SE | BATH-1770-SE |
| Trade and RRP | offers.price | £312.50 and RRP £625.00 | 479.00 |
| none | offers.priceCurrency | blank | GBP |
| Stock | offers.availability | Yes | InStock |
| none | offers.itemCondition | blank | NewCondition |
| Category | category | BATHS>S/E | Bathroom > Baths > Single ended baths |
Merchant Center pairs each feed attribute with its schema.org property, id with sku, title with name, image_link with image, and says “Your structured data must be matched to the corresponding values and attributes.”9 The category row is the retailer’s own tree as plain text, which Google reads as a custom product type, “similar to the product_type attribute in product feeds”; a Google product category goes in the same property as a CategoryCode.3
Why supplier data fails the markup
The same five faults come back file after file, whether a theme, a plugin or a template of your own writes the markup. The generator copies what the catalogue holds; it does not clean it.
- Prices with symbols, separators or the wrong figure. A cell reading £1,499.00 fails twice. Merchant Center wants the price “Submitted without currency symbols, thousand separators or spaces”.9 Schema.org adds a full stop, not a comma, for the decimal point.7 And a supplier’s price column holds a trade price or an RRP, while the page needs your selling price, which for UK shoppers includes VAT.12
- Stock as free text. Yes, Y, In stock (3-5 days), Call, Due w/c 12th. The availability property takes one value from Google’s list of 10: “Don’t specify more than one value.”3 A lead time is not an availability, and Yes is not a value.
- Identifiers damaged in transit. Scientific notation, dropped leading zeros, a GTIN typed with spaces, a SKU with spaces inside it. Schema.org defines a valid GTIN as “an all-numeric string of either 8, 12, 13 or 14 digits” with a valid check digit, or a GS1 Digital Link URL built on one, which Google does not support.63 How each of those breaks is in GTIN, EAN, UPC and MPN explained.
- Brand missing, or two in one cell. The brand sits in the title in capitals, or the supplier’s company name stands in for it, or a spare part that fits two makes gets both. The property takes one name.3
- Images that are thumbnails, or unreachable. For a merchant listing the image is required and “Image URLs must be crawlable and indexable”; Google recommends several high-resolution images of at least 50,000 pixels in area, in the ratios 16:9, 4:3 and 1:1.3 A gallery thumbnail behind a dead link meets none of that.
The markup must match the page, and the feed
Everything above is about format. The rule that costs listings is about truth. Google’s general guidelines rule out marking up “content that is not visible to readers of the page”, and require the markup to be “a true representation of the page content”.5 Merchant Center is blunter: “Structured data must match the values that are shown to the customer.”9
Merchant Center also reads the markup back against the feed, using the landing page’s price, availability and condition to correct it: a product uploaded at 4 USD whose page says 3 USD becomes 3 USD.10 Where prices or stock change often, such as more than once a day, “The product may be disapproved instead of updated if we detect a mismatch.” And wrong markup can be switched off: “schema.org annotations may be disabled if they aren’t accurate”.10
A price in the feed must “match the price on your landing page and the checkout pages”, or the product is disapproved.12 Availability has to agree across the landing page, the structured data, the checkout and the feed: “A mismatch in one of these will trigger a product disapproval.”13
| Feed value | Schema.org values Merchant Center maps to it | Supplier wording it has to absorb |
|---|---|---|
| in_stock | InStock, LimitedAvailability, OnlineOnly | Yes, Y, Available, In stock |
| out_of_stock | OutOfStock, SoldOut, InStoreOnly, Discontinued | No, N, 0, Sold out |
| preorder | PreOrder, PreSale | Coming soon, Pre-order |
| backorder | BackOrder | Awaiting stock, Due in, 3 to 4 weeks |
How to prepare the values, step by step
Fix the columns, not the template, in this order.
- Decide which markup the page qualifies for. Only a page where a shopper can buy is a merchant listing, “not pages with links to other sites that sell the product”, and it should focus on one product or its variants.3
- Build the price from your own pricing. A plain number with a full stop for the decimal point and no more than two decimal places, and GBP in priceCurrency.129
- Read the Stock column into one availability value. List every wording in the column, decide which of Google’s values each means, and apply that mapping to every row. It is normalising an attribute value with a fixed target list, and the page has to agree: “For ‘in stock’ offers, the ‘buy’ button should be active.”13
- Restore and check every GTIN. Undo scientific notation, put back the leading zeros Excel dropped, strip spaces and dashes, and make sure “the check digit is present and correct”.11 Do not pad a GTIN out to 14 digits, which schema.org says “is not required or encouraged”, and use the most specific property for its length.63
- One brand, one SKU, no spaces. Fold the brand’s spellings onto one name, and replace spaces inside a SKU with a hyphen in the store too, so page and feed agree.
- Point image at a full-size, crawlable file. Fetch every link before the page is built; a dead link is a missing required property.
- Add itemCondition, and priceValidUntil only when a sale really ends. NewCondition for new stock.
- Test one page, then a few. Validate with the Rich Results Test and “fix any critical errors”, then deploy a few pages and check them with the URL Inspection tool.3 Search recommends putting the markup in the initial HTML; Merchant Center requires it: the markup “can’t be generated with JavaScript after the page has loaded”.39
When it is ambiguous
Variants. A bath in three lengths is three products, and each variant “has its own GTIN”.11 Google’s variant markup groups them under a ProductGroup with variesBy, hasVariant and productGroupID, and each variant needs a unique ID, such as its sku or gtin.4 Do not reach for AggregateOffer: “Don’t use AggregateOffer to describe a set of product variants.”2 In the feed the grouping is item_group_id, which Merchant Center maps to inProductGroupWithID.9
Own-brand products with no GTIN. “If you’re the only seller of a product or if your product is a shop brand, it may not have a GTIN, and if so you don’t need to submit one.” Brand and MPN go in instead.11
Made to order. Schema.org’s ItemAvailability list has 12 members, and two of them, MadeToOrder and Reserved, are on neither of Google’s lists.8313 A made-to-order bath needs a value Google reads, and the page has to say the same.
Where the values come from, and what to do next
Structured data is the last stop for product data enrichment, not a separate project: the store, the feed, the page and the markup should all be built from the same cleaned attributes. Product data for AI shopping follows those values into ChatGPT’s product feed and Google’s AI Mode.
That is the work RefynData’s product data enrichment service does on a supplier file. Each SKU or barcode is matched to the real product and checked against a page that shows it, the full-size original is found behind each thumbnail, spelling variants are folded onto one value, and each value keeps a link to its source. On one percussion and pro-audio catalogue we checked 874 of 901 products against a named source, and listed the other 27 as unchecked rather than guessing at them: Google’s GTIN rule, applied to every value.
Start with one product page. Run it through the Rich Results Test and read the merchant listings section, not just the pass mark; Google does not guarantee a rich result even for markup the test accepts.95 Check that the page and its markup give the same price, currency and stock, then that the feed row does too. Where they disagree, the supplier file is usually where the wrong value came in, and the cheapest place to put it right.
Questions
What are the required properties for product structured data?
For a merchant listing, the page where a shopper buys from you, Google requires a name, an image and an Offer with a price above zero and a priceCurrency. A product snippet needs a name plus one of offers, review or aggregateRating. For merchant listings, brand, GTIN, MPN, SKU and description are recommended, not required.
What is the difference between a product snippet and a merchant listing?
Product snippets are for pages where the product cannot be bought directly, such as an editorial review. Merchant listings are for pages where the shopper buys from you, with room for shipping and returns. A retailer’s product page is a merchant listing, and meeting its requirements can also make it eligible for product snippets.
Is GTIN required in product structured data?
No. Google recommends it for merchant listings, in the most specific form, such as gtin13 for an EAN, but does not require it. Give it as digits with a correct check digit. A product without one, such as an own-brand line, takes the brand and MPN instead, and a GTIN should never be guessed.
Does the price in structured data have to match the page?
Yes. Google’s guidelines say structured data must be a true representation of the page, and Merchant Center says it must match the values shown to the customer. A price or availability that disagrees across the page, the markup and the feed can get the product disapproved.
Do I need product structured data if I already have a Merchant Center feed?
Google accepts either, and says providing both maximises eligibility and helps it verify your data. Merchant Center also reads the markup on your pages to check and correct the feed’s price, availability and condition. The markup is worth having alongside a feed, as long as both carry the same values.
Sources
- Google Search Central. Introduction to Product structured data. Read .
- Google Search Central. Product snippet (Product, Review, Offer) structured data. Read .
- Google Search Central. Merchant listing (Product, Offer) structured data. Read .
- Google Search Central. Product variant structured data (ProductGroup, Product). Read .
- Google Search Central. General structured data guidelines. Read .
- schema.org. Product. Read .
- schema.org. Offer. Read .
- schema.org. ItemAvailability. Read .
- Google Merchant Center Help. Supported structured data attributes and values. Read .
- Google Merchant Center Help. Allow Merchant Center to update product information automatically. Read .
- Google Merchant Center Help. GTIN [gtin]. Read .
- Google Merchant Center Help. Price [price]. Read .
- Google Merchant Center Help. Availability [availability]. Read .