DEV Community

Cover image for Magento 2 Product JSON-LD Schema for AI Search — Complete Guide (2026)
Ievgenii Gryshkun
Ievgenii Gryshkun

Posted on • Originally published at angeo.dev

Magento 2 Product JSON-LD Schema for AI Search — Complete Guide (2026)

Even if your Magento store ranks well in Google, it may still not appear in AI-generated product results (ChatGPT, Gemini, Perplexity).

A common reason is missing or incomplete Product schema.

AI systems rely on structured data to extract pricing and availability. When that data is missing or inconsistent, products may not be eligible for inclusion. This guide covers what is typically missing, why, and how to fix it.

TL;DR:

  • Magento 2 outputs microdata by default; JSON-LD is generally more reliable and easier for parsers to consume
  • Missing offers.availability is a common issue that breaks product eligibility for AI-powered shopping results
  • Schema injected via GTM is often not visible to non-JavaScript crawlers
  • Hyvä Theme does not include product schema out of the box

What is AI Search?

By "AI search" we refer to systems like ChatGPT browsing (via OAI-SearchBot), Google AI Overviews, Gemini, and Perplexity that extract structured data from pages to generate answers and product listings.

Unlike traditional search engines that primarily rank links, these systems rely more heavily on structured data like JSON-LD to understand product details — price, availability, ratings — rather than inferring them from HTML layout.

Magento 2 Product JSON-LD Schema for AI Search — Complete Guide

Before vs After: Schema and AI Eligibility

Setup ChatGPT Shopping Gemini Perplexity
No schema ❌ Not eligible / rarely shown ❌ Not eligible ❌ Rarely cited
Microdata only (Luma default) ⚠️ Partially parsed ⚠️ Inconsistent ⚠️ Inconsistent
JSON-LD, missing offers.availability ⚠️ Often fails eligibility check ⚠️ May appear ⚠️ May appear
Full JSON-LD + availability + rating ✅ Eligible for Shopping results ✅ Product results ✅ Cited with price

Why Magento 2 Falls Short for AI Schema

Default Luma theme outputs microdata — the older itemscope/itemprop HTML format:

<!-- Default Magento Luma — often insufficient for AI search -->
<div itemscope itemtype="http://schema.org/Product">
  <span itemprop="name">Product Name</span>
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
    <span itemprop="price">49.99</span>
    <!-- offers.availability not present -->
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Three issues worth addressing for AI search:

1. Microdata vs JSON-LD. JSON-LD is generally more reliable because it is self-contained and easier to parse compared to microdata embedded in HTML. Microdata is also more fragile when themes or extensions modify markup.

2. offers.availability is missing. This field tells AI systems whether a product can actually be purchased. Without a valid value, products may not pass eligibility checks for AI-powered shopping results.

3. JavaScript-rendered schema may not be indexed. While Google Search processes JS-rendered content reasonably well, many AI crawlers rely on the initial HTML response. Schema injected after page load (e.g. via GTM) is often not visible to these crawlers.

On GTM specifically: While Google Search may process JavaScript-rendered schema, many AI crawlers rely on the initial HTML response. If all your structured data comes from GTM, there's a good chance AI systems aren't seeing it — even if Google Rich Results Test passes.


Real Example

A Magento store with correct pricing but missing offers.availability:

  • Passed Google Rich Results Test ✅
  • Had valid microdata with price and name ✅
  • Did not appear consistently in AI-generated product listings ❌

After adding JSON-LD with "availability": "https://schema.org/InStock" and validating via curl (server-side output), the product became eligible for AI Shopping results.

This is the most common schema issue across Magento stores — and it takes about five minutes to fix.


The Copy-Paste Product JSON-LD

A spec-compliant block that covers all the fields AI systems look for:

<script type="application/ld+json">
{
  "@context": "https://schema.org/",
  "@type": "Product",
  "name": "Alpine Hiking Jacket",
  "description": "Waterproof 3-layer shell for alpine conditions.",
  "sku": "WB-004",
  "image": ["https://yourstore.com/media/catalog/product/jacket.jpg"],
  "brand": { "@type": "Brand", "name": "TrailMaster" },
  "offers": {
    "@type": "Offer",
    "priceCurrency": "EUR",
    "price": "189.99",
    "priceValidUntil": "2026-12-31",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "url": "https://yourstore.com/alpine-hiking-jacket.html",
    "seller": { "@type": "Organization", "name": "Your Store Name" }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.6",
    "reviewCount": "38",
    "bestRating": "5",
    "worstRating": "1"
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Required and Recommended Fields

Field Status Common mistake
@type: Product ✅ Required
name ✅ Required HTML tags in name string
offers.availability ✅ Required Text string instead of URI
offers.price ✅ Required "€189" instead of "189.99"
offers.priceCurrency ✅ Required Symbol instead of code EUR
image ⚠️ Recommended Relative URL instead of absolute
sku ⚠️ Recommended Often missing entirely
aggregateRating ⚠️ Recommended Including when reviewCount is 0 — causes validation error
brand ⚠️ Recommended Plain string instead of {"@type":"Brand","name":"..."}

offers.availability — full URI required

Status ✅ Correct ❌ Often rejected
In stock https://schema.org/InStock "In Stock" / "instock"
Out of stock https://schema.org/OutOfStock "Out of Stock"
Pre-order https://schema.org/PreOrder "preorder"
Back order https://schema.org/BackOrder "backorder"

The full schema.org URI is what the spec requires. Short strings are technically invalid — parsers may accept them or silently ignore them depending on the implementation.


Configurable Products — Use AggregateOffer

When individual variants have different availability states, it's cleaner to use AggregateOffer so AI systems can understand the full option matrix:

{
  "@type": "Product",
  "name": "Running Shoe",
  "offers": {
    "@type": "AggregateOffer",
    "offerCount": 8,
    "lowPrice": "89.99",
    "priceCurrency": "EUR",
    "availability": "https://schema.org/InStock",
    "offers": [
      {
        "@type": "Offer",
        "sku": "RS-42-BLK",
        "name": "Size 42 / Black",
        "availability": "https://schema.org/InStock",
        "price": "89.99",
        "priceCurrency": "EUR"
      },
      {
        "@type": "Offer",
        "sku": "RS-43-BLK",
        "name": "Size 43 / Black",
        "availability": "https://schema.org/OutOfStock",
        "price": "89.99",
        "priceCurrency": "EUR"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Hyvä Theme — No Schema Out of the Box

Hyvä Theme is built on Alpine.js and does not include Luma's product templates. This means Hyvä Theme does not include product schema out of the box in its default templates — neither microdata nor JSON-LD.

Verify quickly:

curl -s https://yourstore.com/sample-product.html | grep -c 'application/ld+json'
# Returns 0 → no JSON-LD

curl -s https://yourstore.com/sample-product.html | grep -c 'itemscope'
# Returns 0 → no microdata either
Enter fullscreen mode Exit fullscreen mode

The fix is to inject JSON-LD via layout XML so it's server-side rendered. Create Vendor_Theme/layout/catalog_product_view.xml:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <block class="Magento\Framework\View\Element\Template"
               name="product.json.ld"
               template="Vendor_Module::product/json-ld.phtml"/>
    </head>
</page>
Enter fullscreen mode Exit fullscreen mode

Schema in <head> via layout XML is part of the initial HTML response — visible to crawlers that don't execute JavaScript.

Alternatively, you can use an open-source module like angeo/module-rich-data (MIT) to generate schema automatically for both Luma and Hyvä.


Diagnosing Your Current Schema

This one-liner checks what schema your product pages actually output:

curl -s https://yourstore.com/sample-product.html | \
  python3 -c "
import sys, json, re
body = sys.stdin.read()
blocks = re.findall(r'<script[^>]+type=\"application/ld\+json\"[^>]*>(.*?)</script>', body, re.DOTALL)
for b in blocks:
    try:
        d = json.loads(b)
        items = d if isinstance(d, list) else [d]
        for item in items:
            nodes = item.get('@graph', [item])
            for n in nodes:
                if n.get('@type') in ('Product', 'IndividualProduct'):
                    print('FOUND Product schema')
                    offers = n.get('offers', {})
                    if isinstance(offers, list): offers = offers[0] if offers else {}
                    print('availability:', offers.get('availability', 'MISSING'))
                    print('price:', offers.get('price', 'MISSING'))
                    print('aggregateRating:', 'present' if n.get('aggregateRating') else 'MISSING')
    except: pass
  "
Enter fullscreen mode Exit fullscreen mode
Output Meaning Next step
No output No JSON-LD schema Add it or install a module
availability: MISSING Offers block incomplete Fix the offers object
availability: InStock Text string (non-spec) Change to full URI
availability: https://schema.org/InStock ✅ Correct Validate at schema.org

Implementation Options

Option 1 — Module (5 min)

composer require angeo/module-rich-data
bin/magento setup:upgrade && bin/magento cache:flush
Enter fullscreen mode Exit fullscreen mode

MIT licensed, works on Luma and Hyvä. Adds Product, Organization, BreadcrumbList, WebSite, FAQPage schema — all server-side rendered.

Option 2 — Manual phtml

Create Magento_Catalog/templates/product/json-ld.phtml:

<?php
$product = $block->getProduct();
$schema = [
    '@context' => 'https://schema.org/',
    '@type'    => 'Product',
    'name'     => $product->getName(),
    'sku'      => $product->getSku(),
    'offers'   => [
        '@type'         => 'Offer',
        'price'         => (string) $product->getFinalPrice(),
        'priceCurrency' => $block->getCurrencyCode(),
        'availability'  => $product->isAvailable()
            ? 'https://schema.org/InStock'
            : 'https://schema.org/OutOfStock',
        'itemCondition' => 'https://schema.org/NewCondition',
    ],
];
?>
<script type="application/ld+json">
<?= json_encode($schema, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) ?>
</script>
Enter fullscreen mode Exit fullscreen mode

Option 3 — GTM

Worth mentioning because it comes up a lot: GTM-injected schema works for Google Search, which processes JavaScript. For AI crawlers that rely on the initial HTML response, GTM schema may not be seen. If you're specifically targeting AI search visibility, server-side rendering is safer.


Validation Checklist

  • [ ] JSON-LD format (not microdata)
  • [ ] offers.availability — full schema.org URI
  • [ ] offers.price — number string, no currency symbol
  • [ ] offers.priceCurrency — ISO 4217 code (EUR, USD, GBP)
  • [ ] image — at least one absolute URL
  • [ ] aggregateRating omitted if reviewCount is 0
  • [ ] Schema present in raw curl output (server-side rendered)
  • [ ] Hyvä: schema injected via layout XML <head>
  • [ ] Configurable products use AggregateOffer with per-variant Offer
  • [ ] Zero errors at validator.schema.org

Common Errors

"The value of offers.availability is not valid"
Using a text string. Change to "https://schema.org/InStock".

"Missing field: offers.priceCurrency"
Add "priceCurrency": "EUR" to the offers object.

"ratingValue out of range"
Value must be between 1 and bestRating. A value of 0 is invalid — omit aggregateRating entirely if no reviews yet.


Full AEO Check

Product schema is one of 13 signals that affect AI search visibility in Magento. To check all of them at once:

composer require angeo/module-aeo-audit
bin/magento setup:upgrade && bin/magento cache:flush
bin/magento angeo:aeo:audit
Enter fullscreen mode Exit fullscreen mode

Free CLI tool — weighted score across 13 signals, exact fix command for each failure.


If you're debugging Magento schema issues, the CLI audit tool above can save a lot of manual checking.

Originally published at angeo.dev

Top comments (1)

Collapse
 
angeo profile image
Ievgenii Gryshkun

Curious how others handle product schema in Magento.

Have you seen AI-generated results pick up microdata-only setups, or is JSON-LD a must in your experience?