The Citation Report / Model Intelligence
Bypass Shopify's default schema to get product videos recommended by AI
Shopify storefronts relying on the native {{ product | structured_data }} filter strip out rich media assets, leaving product video walkthroughs completely invisible to generative search engines. Pendium tests reveal that multimodal models like ChatGPT, Gemini, and Google AI Overviews do not watch raw video files; they ingest structured JSON-LD data to verify product capabilities and visual demonstrations. To earn visual citations and recommendations in 2026, merchants must bypass Shopify's default filter and inject a custom VideoObject schema nested directly under the main Product entity using the subjectOf property. This direct code intervention maps raw MP4 endpoints, ISO 8601 durations, and high-resolution thumbnail frames so automated crawlers can index your media.
At Pendium, we monitor thousands of real AI conversations daily across seven major platforms, including Google AI Overviews, Claude, and DeepSeek. By tracking visibility scores and simulating 10 distinct customer personas per business, we see exactly which schema properties large language models require before they will cite a visual asset in a recommendation. For merchants competing in competitive categories like AI Visibility for DTC Brands, missing structured media data means lost placement on AI-generated buying shortlists.
Check your current Liquid template output
Most Shopify themes rely on a single, legacy Liquid tag to manage structured data: {{ product | structured_data }}. This filter takes the core product object and serializes it into a basic JSON-LD string. It captures the product title, primary image, SKU, and pricing offers, but it deliberately ignores interactive assets.
<!-- Standard Shopify theme output (snippets/product-schema.liquid) -->
<script type="application/ld+json">
{{ product | structured_data }}
</script>
When this filter executes, it queries the structured_data API documentation parameters, which only support flat image arrays and standard offer properties. Embedded product demonstration videos, customer unboxings, and instructional clips uploaded to your Shopify Files admin or media library disappear from the serialized output.
As noted in the technical guide on How to map Shopify video schema for AI search agents, crawlers from major LLMs cannot connect your product video to the page itself without explicit declarations. When an AI crawler like GPTBot or PerplexityBot parses your HTML, it skips past heavy JavaScript-driven media players. If the media lacks a machine-readable schema definition, the crawler categorizes your page as a static, text-only catalog entry.
The gap between what a human shopper sees and what an automated crawler ingests creates an immediate visibility deficit in generative search.
| Asset Type | What Your Human Shoppers Experience | What AI Crawlers Ingest via Default Filter |
|---|---|---|
| Product Video (MP4) | Interactive video player showing product sizing, materials, and real-time use. | Dropped entirely from the page schema; crawler sees no video asset. |
| YouTube / External Embed | Streamed responsive video player hosted in an iframe. | Skipped; crawlers do not execute third-party iframe scripts. |
| Primary Image | High-resolution hero image gallery. | Captured as a basic string URL inside the image array. |
| Variant Media | Dynamic image switching based on dropdown selections. | Often flattened or misattributed; see our guide to Map Shopify variant images to schema for AI visual search. |
To inspect what your store currently outputs, open any product page in your browser. Right-click, select View Page Source, and search for application/ld+json. If your product block lacks a VideoObject declaration, AI search engines cannot see your visual assets.
Construct the exact VideoObject payload
Fixing this visibility gap requires building a custom JSON-LD payload that maps your video metadata into fields defined by Schema.org. Generative search engines require explicit proof that a video exists, what it demonstrates, and where the raw source file resides.
To construct a valid VideoObject, you must extract and format five required properties from your Shopify backend:
- The absolute URL of the raw video file (
contentUrl) or player frame (embedUrl). - A high-resolution poster image (
thumbnailUrl). - An exact timestamp indicating when the asset was published (
uploadDatein ISO 8601 format). - The total run time formatted as an ISO 8601 duration (
duration, such asPT1M30S). - A clear, contextual title and description describing the video content (
nameanddescription).
Mapping the required properties
Shopify stores native video uploads within the product.media object. When a merchant uploads an MP4 directly to the product media gallery, Shopify generates multiple resolutions and a poster frame automatically.
You can extract these fields directly in Liquid without hardcoding external links. The code below queries the first video attached to a product, pulls its highest-resolution MP4 URL, formats the creation date, and converts the run time into the required ISO standard.
{% assign product_video = product.media | where: "media_type", "video" | first %}
{% if product_video %}
{% assign raw_mp4 = product_video.sources | where: "format", "mp4" | sort: "width" | last %}
{% assign duration_seconds = product_video.duration | divided_by: 1000 | round %}
{% assign duration_minutes = duration_seconds | divided_by: 60 %}
{% assign remaining_seconds = duration_seconds | modulo: 60 %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "VideoObject",
"name": {{ product.title | append: " - Product Demonstration" | json }},
"description": {{ product_video.alt | default: product.description | strip_html | truncatewords: 30 | json }},
"thumbnailUrl": [
{{ product_video.preview_image | image_url: width: 1200 | prepend: "https:" | json }}
],
"uploadDate": {{ product_video.created_at | date: "%Y-%m-%dT%H:%M:%S%z" | json }},
"duration": "PT{{ duration_minutes }}M{{ remaining_seconds }}S",
"contentUrl": {{ raw_mp4.url | json }}
}
</script>
{% endif %}
In this implementation, the duration calculation converts Shopify's native millisecond output into seconds, then constructs an ISO 8601 duration string like PT2M15S. LLMs require this format to determine whether a clip is a quick 15-second teaser or an in-depth 10-minute teardown.
Handling MP4 versus YouTube embeds
Shopify handles native MP4 uploads and third-party embeds through different data models. If your team embeds videos from YouTube or Vimeo, the product_video.sources array will return empty because Shopify does not host the source media files.
For external videos, Schema.org requires the embedUrl property instead of contentUrl. You must branch your Liquid logic based on media.media_type.
{% case media.media_type %}
{% when 'video' %}
{% assign mp4_source = media.sources | where: "format", "mp4" | sort: "width" | last %}
"contentUrl": {{ mp4_source.url | json }},
{% when 'external_video' %}
"embedUrl": {{ media | external_video_url | json }},
{% endcase %}
Using contentUrl for native MP4s gives AI crawlers direct access to the file container, which multimodal engines prefer for automated indexing. When using YouTube or Vimeo, verify that the embedUrl points directly to the embeddable player URL rather than the standard watch page.
Nest the new schema inside your product code
Emitting an isolated VideoObject script tag on your product page creates an architectural problem. AI crawlers will recognize that a video exists on the domain, but they will not know which specific product the video illustrates.
To solve this, the video data must nest directly inside the primary Product entity using Schema.org's subjectOf property. As explained in How to map Shopify video and 3D models to schema for AI search, nesting your media establishes an unequivocal entity relationship in the crawler's knowledge graph.
Replace your theme's call to {{ product | structured_data }} in sections/main-product.liquid or snippets/product-schema.liquid with a unified JSON-LD graph.
{% assign product_video = product.media | where: "media_type", "video" | first %}
{% assign current_variant = product.selected_or_first_available_variant %}
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": {{ product.title | json }},
"image": [
{{ product.featured_image | image_url: width: 1200 | prepend: "https:" | json }}
],
"description": {{ product.description | strip_html | truncatewords: 50 | json }},
"sku": {{ current_variant.sku | json }},
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
"offers": {
"@type": "Offer",
"priceCurrency": {{ cart.currency.iso_code | json }},
"price": {{ current_variant.price | divided_by: 100.00 | json }},
"availability": "https://schema.org/{% if current_variant.available %}InStock{% else %}OutOfStock{% endif %}",
"url": {{ shop.url | append: current_variant.url | json }}
}
{% if product_video %}
{% assign raw_mp4 = product_video.sources | where: "format", "mp4" | sort: "width" | last %}
{% assign duration_seconds = product_video.duration | divided_by: 1000 | round %}
{% assign duration_minutes = duration_seconds | divided_by: 60 %}
{% assign remaining_seconds = duration_seconds | modulo: 60 %}
,
"subjectOf": {
"@type": "VideoObject",
"name": {{ product.title | append: " - Video Walkthrough" | json }},
"description": {{ product_video.alt | default: product.title | strip_html | json }},
"thumbnailUrl": [
{{ product_video.preview_image | image_url: width: 1200 | prepend: "https:" | json }}
],
"uploadDate": {{ product_video.created_at | date: "%Y-%m-%dT%H:%M:%S%z" | json }},
"duration": "PT{{ duration_minutes }}M{{ remaining_seconds }}S",
"contentUrl": {{ raw_mp4.url | json }}
}
{% endif %}
}
</script>
This nested graph tells ChatGPT, Claude, and Gemini that the video is not general site background media. The video is explicitly declared as an authoritative demonstration of that exact SKU, price point, and inventory record.
The duplicate schema trap and how to avoid it
The most common failure when updating Shopify schema is leaving legacy code active alongside your new custom graph.
Merchants often paste a custom video script snippet into theme.liquid or install a third-party SEO app while leaving {{ product | structured_data }} running inside sections/main-product.liquid. This forces AI crawlers to parse two competing, contradictory JSON-LD blocks for the exact same page URL.
When an AI engine encounters conflicting schema entities—one declaring a bare-bones product without media, and another declaring a nested graph with video assets—it frequently invalidates both entries to prevent data corruption. Review our technical walkthrough on How to fix duplicate Shopify JSON-LD before AI engines drop your products to audit your theme files for overlapping markup.
Before pushing your changes live:
- Search your entire theme code for
structured_dataand remove or comment out any native filters generating product schema. - Check your installed Shopify Apps for automated schema generators and turn off their Product JSON-LD output.
- Validate your published URL in the Google Rich Results Test to confirm that only one unified
Productentity appears, containing your nestedsubjectOfproperty.
Verifying your video schema for AI indexing
Once your code is live, you must verify that generative search engines read and interpret the payload correctly. Standard web development tools only check for syntax validity; you must verify machine comprehension.
First, paste your updated product URL into the Schema Markup Validator. Verify that the Product entity contains zero fatal errors and that the subjectOf field maps directly to a recognized VideoObject. Check that thumbnailUrl, contentUrl, and duration display valid formatting.
Next, monitor how retrieval systems index your catalog. When automated crawlers parse your product pages, they ingest your structured specifications into their vector stores.
Pendium tracks these interactions in real time, analyzing how platforms like ChatGPT, Perplexity, Grok, and Google AI Overviews cite e-commerce brands during real customer buying conversations. When your video walkthroughs are structured correctly, conversational engines can extract precise demonstration timestamps and visual proof points to answer complex product comparison prompts.
Take control of how AI engines see your catalog. Use the Scan Your AI Visibility tool to run a diagnostic across every major generative engine and verify your rich media markup today.


