Product structured data on Shopify
This is the check the most stores fall short on. It is worth being precise about what it costs you, because the usual version of this claim is wrong: a shopping agent buying from a Shopify store does not need it. Anything reading the page itself does.
Across 2,954 Shopify storefronts we have scanned, 91% fall short on product structured data.
First, what this does not cost you
Nearly every page written about product schema and AI says some version of the claim that without it an assistant has no way to learn what you charge. We said it too. On a Shopify store it is not true, and we would rather say so than keep selling the better-sounding version.
Every Shopify store now answers an agent-commerce endpoint at
/api/ucp/mcp, advertised at /.well-known/ucp. It speaks the
Universal Commerce Protocol, and an agent that asks it
for your catalogue gets your price as an integer with a currency code, your variants,
your SKUs, your images and your product taxonomy — whether or not a single line
of JSON-LD exists on your site. We tested it against stores that fail this very
check. One of them returns
{"amount":3199,"currency":"USD"} for a
product whose page states no price a machine can read.
You did not switch that on and you cannot switch it off from this page. If your worry was that an AI shopping agent cannot find out what you charge, stop worrying: that is Shopify’s job now and Shopify is doing it.
What it does cost you
Two things, both real, neither of them the commerce path.
Google’s rich results. Price, availability and review stars in a search listing come from this markup and nothing else. That has been true for a decade and has nothing to do with AI. It is the larger of the two.
Being quoted accurately. The commerce endpoint answers an agent that has decided to shop your store. It is not what happens when someone asks an assistant a question and it reads pages — yours and your competitors’ — to answer. That reader gets your HTML, and in your HTML the price is a number in a layout rather than a stated fact. Layouts are where models make things up.
So the honest sizing is: this is a page-legibility problem, not a can-they-buy-it problem. Worth fixing, and cheap. Not an emergency, and anyone telling you it is an emergency has not checked.
What it is
A block of JSON-LD in your product page’s HTML that states, in a fixed vocabulary, what the item is. Trimmed to the parts that matter:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Merino Runner",
"brand": { "@type": "Brand", "name": "Your Store" },
"gtin13": "0123456789012",
"offers": {
"@type": "Offer",
"price": "98.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
}
Most of schema.org is optional. Those fields are not: without price, currency and availability an assistant cannot answer “what does it cost and can I get it”, which is most of what shoppers ask it.
Where Shopify already has the values
You almost never need to type product data twice. Shopify stores every field this needs, and themes can read them straight out of Liquid:
- Price and currency —
product.selected_or_first_available_variant.price. Use the selected variant, notproduct.price: on a store where variants are priced differently the latter reports the cheapest one, and the page then contradicts itself. - Availability — that same variant’s
available, mapped toInStockorOutOfStock. - GTIN — the variant’s
barcodefield. This is the one people miss. Shopify’s barcode field is where your UPC or EAN already lives; it simply is not published anywhere a machine reads unless the theme puts it there. A 12-digit value isgtin12, a 13-digit one isgtin13. - Brand —
product.vendor, on stores that use the vendor field for the actual brand.
Leave a field out entirely rather than emitting it empty. An empty
gtin13 is a claim that the product has no identifier, which is worse
than saying nothing.
How to check without fooling yourself
Open a product page, then view source — Ctrl+U,
or put view-source: in front of the URL — and search for
"@type":"Product".
Do not check this in the browser’s element inspector. The inspector shows the page after JavaScript has run, so schema injected at runtime by an app or a theme script looks present there and is entirely absent from what a crawler receives. This is the most common way a store passes its own inspection and fails ours: the data exists, just not in time for anyone to read it. View source shows the HTML as it was served, which is the only version a crawler sees.
If the block is there and you are unsure it is valid, Google’s Rich Results Test reads the same markup and will name the fields it rejects.
Fixing it
If your theme emits no Product JSON-LD at all, add it to the product
template. Most current free Shopify themes ship it already — look for a
snippet with structured-data or json-ld in the name before
writing your own, because two Product blocks on one page is its own problem.
If the block is there but incomplete, that is a small edit to that snippet rather than new code: add the missing field and read it off the variant as above.
If an app injects it client-side, the fix is to stop relying on the app for this and put the markup in the template, where it is served with the page.
Checking your own store
The scan below reads your storefront the way a crawler does and reports this check along with the rest. It is free and needs no account.
Free, no signup. Takes about ten seconds.
The other checks
- Allowing AI shopping crawlers on Shopify
- When your product page needs JavaScript to read
- llms.txt on Shopify
- What your agent endpoint serves
- When your Shopify sitemap is missing
- Page titles and descriptions on Shopify
- When your homepage is a JavaScript redirect
Installs on any Shopify store. The free plan needs no card, and the dashboard runs on sample data if you would rather look first. Shopify will note that the app has not been reviewed yet: the App Store listing is still in review, and that governs who can find Shelfglance, not who can install it.