GUIDE № 03 · STRUCTURED DATA
The Five JSON-LD Schemas Your Shopify Store Needs for AI
There's a hidden layer on your product pages: JSON-LD structured data. AI agents and search engines parse JSON-LD as the authoritative product description — when it's present, they tend to weigh it more heavily than free-form copy on the page. It's a machine-readable block of code that tells crawlers and AI agents exactly what your product is, how much it costs, whether it's in stock, and who makes it.
You can't see it on the page. But AI agents can. And if it's incomplete or missing, they get an incomplete picture of your products.
What is JSON-LD?
JSON-LD stands for JavaScript Object Notation for Linked Data. It's a standard way to embed structured data in web pages using schema.org vocabulary.
In practice, it's a <script> tag in your page's HTML that contains a JSON object describing the page content. Search engines and AI agents read this block to understand your page without having to guess from the visible text.
What fields matter for product pages
A complete product JSON-LD block should include:
- name — the product title
- description — the product description (plain text, not HTML)
- image — one or more product image URLs
- brand — the brand or vendor name
- sku — the product SKU
- offers — price, currency, and availability (InStock, OutOfStock, etc.)
- url — the canonical URL of the product page
Here's what a minimal but complete product markup looks like. First, the Liquid template you'd add to your theme (usually sections/main-product.liquid):
Liquid template
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"description": {{ product.description | strip_html | truncate: 500 | json }},
"image": "{{ product.featured_image | image_url: width: 1200 }}",
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
"sku": {{ product.selected_or_first_available_variant.sku | json }},
"offers": {
"@type": "Offer",
"price": "{{ product.selected_or_first_available_variant.price | divided_by: 100.0 }}",
"priceCurrency": "{{ shop.currency }}",
"availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}",
"url": "{{ shop.url }}{{ product.url }}"
}
}
</script>
Rendered output
When Shopify renders the template above for a real product, the output looks like this:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Organic Cotton Throw Blanket",
"description": "100% GOTS certified organic cotton. 50 x 60 inches.",
"image": "https://cdn.shopify.com/s/files/1/example/blanket.jpg",
"brand": {
"@type": "Brand",
"name": "Your Store Name"
},
"sku": "BLANKET-ORG-50x60",
"offers": {
"@type": "Offer",
"price": "49.0",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"url": "https://yourstore.com/products/organic-cotton-throw"
}
}
</script>
Beyond products: other schema types
Product schema is the most important, but there are other types that help AI agents understand your store. Below are Liquid code examples for each one.
Organization schema
Tells AI agents your business name, logo, and contact info — it's how AI distinguishes your brand from resellers. Add this to your layout/theme.liquid file, inside the <head> tag. It only needs to render on the homepage.
Liquid template
{%- if request.page_type == 'index' -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": {{ shop.name | json }},
"url": "{{ shop.url }}",
"logo": "{{ shop.brand.logo | image_url: width: 600 }}",
"contactPoint": {
"@type": "ContactPoint",
"contactType": "customer service",
"url": "{{ shop.url }}/pages/contact"
},
"sameAs": [
{%- comment -%} Replace with your actual social URLs, or remove the array entirely if not applicable {%- endcomment -%}
{{ "https://facebook.com/your-brand" | json }},
{{ "https://instagram.com/your-brand" | json }}
]
}
</script>
{%- endif -%}
Rendered output
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Homestead & Co.",
"url": "https://homestead.co",
"logo": "https://cdn.shopify.com/s/files/1/example/logo.png",
"contactPoint": {
"@type": "ContactPoint",
"contactType": "customer service",
"url": "https://homestead.co/pages/contact"
},
"sameAs": [
"https://facebook.com/your-brand",
"https://instagram.com/your-brand"
]
}
</script>
The request.page_type == 'index' condition ensures this only renders on your homepage. If your theme doesn't have brand metafields set up, you can hardcode the logo URL and social links instead.
BreadcrumbList schema
Shows AI agents your site structure — "Home > Skincare > Moisturizers" tells AI exactly where a product fits. Add this to your product template (usually sections/main-product.liquid).
Liquid template
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "{{ shop.url }}"
}
{%- if product.collections.size > 0 -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ product.collections.first.title | json }},
"item": "{{ shop.url }}/collections/{{ product.collections.first.handle }}"
}
,{
"@type": "ListItem",
"position": 3,
"name": {{ product.title | json }},
"item": "{{ shop.url }}{{ product.url }}"
}
{%- else -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ product.title | json }},
"item": "{{ shop.url }}{{ product.url }}"
}
{%- endif -%}
]
}
</script>
Rendered output
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://homestead.co"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blankets & Throws",
"item": "https://homestead.co/collections/blankets"
},
{
"@type": "ListItem",
"position": 3,
"name": "Organic Cotton Throw Blanket",
"item": "https://homestead.co/products/organic-cotton-throw"
}
]
}
</script>
This uses the product's first collection for the middle breadcrumb. If your store has nested collections (e.g., Home > Bedding > Blankets), you may need to customize the logic to include the full hierarchy.
FAQPage schema
Marks up FAQ content so AI agents can directly answer customer questions about your products. Add this to your product template, but only if your product pages actually have FAQ sections. If you don't have FAQ content yet, consider adding a few common questions per product — AI agents use these to answer queries like "is this cruelty-free?" or "how long does shipping take?"
Liquid template
If you store FAQ content in product metafields, you can render it dynamically. This example uses a JSON metafield (custom.faq) containing an array of question/answer pairs:
{%- if product.metafields.custom.faq != blank -%}
{%- assign faq_items = product.metafields.custom.faq.value -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{%- for item in faq_items -%}
{
"@type": "Question",
"name": {{ item.question | json }},
"acceptedAnswer": {
"@type": "Answer",
"text": {{ item.answer | json }}
}
}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
}
</script>
{%- endif -%}
If you don't use metafields, you can hardcode FAQ items directly. Here's a simpler version with static questions:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What materials is this made from?",
"acceptedAnswer": {
"@type": "Answer",
"text": "100% GOTS certified organic cotton, grown without pesticides."
}
},
{
"@type": "Question",
"name": "What is the return policy?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Free returns within 30 days of purchase. Items must be unused."
}
}
]
}
</script>
Rendered output
The static version above is already rendered output. For the metafield version, the rendered result looks the same — with the Liquid tags replaced by real question and answer text from your product data.
MerchantReturnPolicy schema
Lets AI agents tell customers "30-day free returns" instantly instead of sending them to find your policy page. Add this to layout/theme.liquid so it's available on every page, or to your product template if you prefer.
Liquid template
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "MerchantReturnPolicy",
"name": {{ shop.name | append: " Return Policy" | json }},
"url": "{{ shop.url }}/policies/refund-policy",
"applicableCountry": "US",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
</script>
Rendered output
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "MerchantReturnPolicy",
"name": "Homestead & Co. Return Policy",
"url": "https://homestead.co/policies/refund-policy",
"applicableCountry": "US",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
</script>
Adjust merchantReturnDays, returnMethod, and returnFees to match your actual policy. Common values for returnFees include FreeReturn, ReturnShippingFees, and RestockingFee. If you accept returns in-store, use ReturnInStore for the returnMethod.
How to check what your store has now
- Open any product page on your store
- Right-click and select View Page Source
- Search for
application/ld+json - You should find at least one JSON-LD block. Look at what fields are included.
Many Shopify themes include some structured data, but it's often incomplete. Common missing fields: brand, sku, description, and availability.
You can also use Google's Rich Results Test to check any page. Paste your product URL and it shows you exactly what structured data Google can find.
How to fix it
This is a developer task. The structured data lives in your Shopify theme's product template (usually sections/main-product.liquid or a similar file).
What to tell your developer:
- Audit the current JSON-LD on product pages — check which fields are present and which are missing
- Add missing fields:
brand,sku,description,availability - Add
Organizationschema to the homepage or layout - Add
BreadcrumbListschema to product and collection pages - If you have FAQ content, add
FAQPageschema - If you have a return policy, add
MerchantReturnPolicyschema
This is one of the highest-impact changes you can make. It's invisible to customers. A developer can implement it in a few hours. And it directly improves how AI agents read and recommend your products.
Open Graph tags
While you're checking structured data, also look at your Open Graph (OG) tags. These are <meta> tags in the page <head> that AI agents and social platforms use when your pages are shared or referenced.
The important ones:
og:title— the product nameog:description— a short product descriptionog:image— the primary product imageog:url— the canonical URLog:type— should be "product" for product pages
Most Shopify themes handle these, but customizations and apps can break them. Check by viewing the page source and searching for og:.