Home · The 15 signals · Product JSON-LD

Shopify Product JSON-LD

Product JSON-LD is the single largest signal in the CatalogScan score: 30 of 170 possible points, more than any other test. Every AI shopping agent — ChatGPT Shopping, Perplexity Shopping, Google AI Mode, Shopify's own Global Catalog — parses your product detail pages' JSON-LD first and only falls back to scraping the page body if the structured block is missing or malformed. This page explains the exact shape agents need, how to test what your theme is actually emitting, and how to fix it in product.liquid.

Last updated 2026-04-30 · Floor signal · 30 pts · Most points of any signal

30 / 170CatalogScan score weight
60%DTC stores fail it (10-store live sample)
18 ptsAvg points left on table per failing store
What this signal scores: a <script type="application/ld+json"> block on every product detail page (PDP) with "@type": "Product" describing price, availability, brand, GTIN, SKU, reviews, and description in machine-readable form. Either a single Product node (one PDP = one product) or a ProductGroup with hasVariant children (one PDP = a parent product with multiple variants). Anything else — Organization-only, WebPage-only, raw microdata, no JSON-LD at all — scores zero.

What it is

JSON-LD ("JSON for Linked Data") is the structured-data format Google, OpenAI, Anthropic, Perplexity, and every other major AI agent agree on for reading product pages without needing to parse raw HTML. On a Shopify product detail page, it lives in a <script type="application/ld+json"> block near the bottom of the page, and the load-bearing field is the top-level @type: only Product and ProductGroup count for AI-shopping discovery. Organization, WebSite, BreadcrumbList, WebPage can be present alongside but do not score this signal.

The minimum viable shape an AI agent expects on a single-variant PDP:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Wool Runner — Natural Black",
  "image": ["https://yourstore.com/cdn/shop/.../runner.jpg"],
  "description": "Merino-wool runner with cushioned EVA midsole...",
  "sku": "AB-WR-NB-09",
  "gtin13": "0012345678905",
  "brand": { "@type": "Brand", "name": "Allbirds" },
  "offers": {
    "@type": "Offer",
    "url": "https://yourstore.com/products/wool-runner",
    "priceCurrency": "USD",
    "price": "98.00",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "3284"
  }
}
</script>

For a multi-variant PDP (one parent with size/color options) a single Product node is the wrong shape — see our long-form on ProductGroup JSON-LD for the correct ProductGroup + hasVariant shape.

Why AI shopping agents care

Three reasons this signal weighs more than any other:

How to test it on your store

The fastest hand-test, on any one PDP, is two steps:

  1. View the page source (Cmd-U / Ctrl-U), search for application/ld+json.
  2. Inside the matching <script> blocks, find the one whose top-level @type equals "Product" or "ProductGroup". If neither exists, you score zero.

To validate the JSON parses correctly and matches the schema vocabulary AI agents expect, paste your full PDP URL into Google's Rich Results Test — any red X under "Detected items" means a malformed block, which silently disqualifies you even if the data looks right at a glance.

The fastest way to test all your PDPs at once is the free CatalogScan scan: it pulls a product from your /products.json feed, parses the live PDP, and reports whether the JSON-LD on that page is valid + which sub-fields (price, brand, GTIN, AggregateRating) it found.

How to fix it

Fixes ordered cheapest to most-involved:

Upgrade to Dawn 14+10 minfree

Dawn 14 (released March 2026) emits clean Product JSON-LD with aggregateRating, full offers, and the schema.org-URL form of availability. If you're still on Dawn 12 or earlier (including most "free Shopify themes" listed pre-2026), upgrading lifts you from a partial-credit shape to a full-credit shape with no other work. Theme update, swap to the new theme as live, done.

Patch sections/main-product.liquid20 minfree

For custom or modified themes: open sections/main-product.liquid, find the existing <script type="application/ld+json"> block, confirm the top-level @type is "Product". If it's missing entirely, paste in a Liquid-templated Product block — every variable you need ({{ product.title }}, {{ product.price | money_without_currency }}, {{ product.featured_image | image_url }}) is in the standard product object.

Install a structured-data app5 min$5–25/mo

If you can't or don't want to touch theme code, apps in the Shopify App Store ("JSON-LD for SEO," "Schema App," "Smart SEO") will inject a Product JSON-LD block via Liquid. Read reviews carefully — some apps inject malformed blocks that fail signal #15 (JSON-LD validity) and zero you out anyway. Test in Google's Rich Results Test after install.

CatalogScan Pro auto-fix2 min$49/mo

Pro reads your live PDP, identifies which sub-fields are missing or malformed, and writes a Liquid snippet you can paste into sections/main-product.liquid with one click — including the GTIN, MPN, and brand-entity fields most apps skip. See Pro pricing.

5 mistakes we keep finding

1. @type: "Organization" on the PDP, no Product block

Some themes emit only an Organization graph and assume Shopify "handles the rest." Shopify does not auto-inject Product JSON-LD; if your theme doesn't emit it, no one does. Search the page source for "Product" with quotes — if you don't find it, you're failing this signal regardless of how clean the rest of the page looks.

2. One Product per variant on a multi-variant PDP

A common Liquid loop emits one Product node per variant — five sizes = five Products on one PDP. Agents don't know which one is canonical and dedupe-or-discard varies by agent. Use a single ProductGroup with a hasVariant array. Full long-form: ProductGroup JSON-LD on Shopify.

3. Smart-quotes from a review-app injection break the JSON

Apps like Yotpo and Judge.me inject review excerpts into JSON-LD via string concatenation. An unescaped curly quote (") in a customer's review breaks the JSON parse — silently. Agents skip the entire <script> block. Fix is one Liquid filter: {{ review.body | strip_html | json }}.

4. Headless Shopify (Hydrogen, Next.js) ships without JSON-LD

The default Hydrogen and Next.js Storefront-API starters do not emit Product JSON-LD by default. Re-add it explicitly in your PDP component using <script type="application/ld+json">{ JSON.stringify(productJsonLd) }</script> — server-rendered, not client-injected. Client-injected JSON-LD is invisible to most AI crawlers, which read the initial server response only.

5. availability as "InStock" instead of full schema.org URL

The strict-parser form is "availability": "https://schema.org/InStock". The short form ("InStock") is rejected by some agents and gets you half credit on signal #11 (Offers availability). Use the full URL.

See also

Is your store passing Product JSON-LD?

Free 2-minute scan. Paste your store URL, get a color-graded scorecard with this signal — and 14 others — checked inline.

Scan my store → See Pro auto-fix pricing