VideoObject
VideoObject is the schema type for any video embedded on or linked from a web page. It powers the video rich result in Google Search (a thumbnail card with duration badge), the key moments carousel (clickable timestamps under the result), the live badge (via BroadcastEvent publication), and the learning video enrichment (via LearningResource dual-typing). It is also one of the best-structured signals you can give AI crawlers for multimedia content.
The example below is the screen-printing walkthrough video for XooTee Classic shirts, filmed at Xoo Code Shop Dunmore. It follows the schema.org/VideoObject vocabulary with five Clip key moments in hasPart, an interactionStatistic view count, and the publisher cross-referencing the Dunmore LocalBusiness entity via @id.
contentUrl vs embedUrl
Google accepts either contentUrl (a direct link to the video file, e.g., an .mp4 URL) or embedUrl (the URL of an embeddable player, e.g., a YouTube embed link), but recommends providing both when possible. If you can only provide one, contentUrl is preferred because Google can crawl the file directly for duration and thumbnail extraction. embedUrl is what you get from platforms like YouTube, Vimeo, and Wistia. Never use a page URL in either field; these must point at the video itself or its player, not at a page that contains the video.
ISO 8601 duration
The duration property uses ISO 8601 duration format: PT4M50S means 4 minutes and 50 seconds. The leading PT stands for "period of time." Developers frequently write "4:50" or "290" instead, both of which are invalid. Google drops the duration badge from the rich result when the format is wrong. The M in a duration is minutes (not months, which would appear before the T separator). PT90M is valid and equivalent to PT1H30M.
Full example of schema.org/VideoObject json-ld markup
The markup is verified as valid with Rich Results Test from Google.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "VideoObject",
"name": "How XooTee Classics are screen-printed",
"description": "Watch the full screen-printing process at Xoo Code Shop Dunmore. From selecting the blank shirt to quality checking the finished print, this walkthrough covers every step of how each XooTee Classic gets its water-based design.",
"thumbnailUrl": "https://xoocode.com/media/xootee-print-thumb.jpg",
"uploadDate": "2025-09-15",
"contentUrl": "https://xoocode.com/media/xootee-print.mp4",
"embedUrl": "https://www.youtube.com/embed/xootee-print-2025",
"duration": "PT4M50S",
"publisher": {
"@type": "Organization",
"name": "XooCode",
"@id": "https://xoocode.com/shop/dunmore-1290"
},
"interactionStatistic": {
"@type": "InteractionCounter",
"interactionType": {
"@type": "WatchAction"
},
"userInteractionCount": 12450
},
"inLanguage": "en-CA",
"hasPart": [
{
"@type": "Clip",
"name": "Selecting the blank shirt",
"startOffset": 0,
"endOffset": 45
},
{
"@type": "Clip",
"name": "Preparing the screen",
"startOffset": 45,
"endOffset": 90
},
{
"@type": "Clip",
"name": "Mixing the water-based ink",
"startOffset": 90,
"endOffset": 135
},
{
"@type": "Clip",
"name": "Printing the design",
"startOffset": 135,
"endOffset": 210
},
{
"@type": "Clip",
"name": "Quality check and packaging",
"startOffset": 210,
"endOffset": 290
}
]
}
</script>Minimal valid version
The smallest markup that still produces a valid VideoObject entity. Use it as the floor. Reach for the advanced example above when you want search engines and AI agents to understand more about your content.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "VideoObject",
"name": "How XooTee Classics are screen-printed",
"description": "Watch the full screen-printing process at Xoo Code Shop Dunmore.",
"thumbnailUrl": "https://xoocode.com/media/xootee-print-thumb.jpg",
"uploadDate": "2025-09-15"
}
</script>Google rich results this unlocks
Markup matching this example makes your page eligible for the following Google Search rich results. The primary target drives the required / recommended property classification in the advanced code block above.
- Google docsVideoprimary
Common VideoObject mistakes
Mistakes that pass validation but silently fail to earn rich results or mislead consumers walking the graph. Avoid these and your markup will be ahead of most sites in the wild.
- 01
Duration as a plain string instead of ISO 8601
Wrong"duration": "4:50"Right"duration": "PT4M50S"ISO 8601 duration format requires the PT prefix (period of time) followed by numeric values with unit suffixes. <code>PT4M50S</code> means 4 minutes and 50 seconds. Developers frequently write clock-style strings like <code>4:50</code> or plain seconds like <code>290</code>, both of which Google rejects. The duration badge disappears from the video rich result and the value is silently dropped. Always use the full ISO form.
- 02
thumbnailUrl pointing to the page instead of the image
Wrong"thumbnailUrl": "https://example.com/videos/my-video"Right"thumbnailUrl": "https://example.com/media/my-video-thumb.jpg"thumbnailUrl must be a direct link to an image file, not a page that contains the video. Google needs to fetch and render the image as the thumbnail card in search results. A page URL returns HTML, which Google cannot render as an image. Use the full URL to a .jpg, .png, or .webp file at least 60x30 pixels.
- 03
Clip startOffset as a string instead of a number
Wrong"startOffset": "45"Right"startOffset": 45schema.org defines <code>startOffset</code> and <code>endOffset</code> as <code>Number</code> values (seconds from the start of the video). A quoted string like <code>"45"</code> technically passes JSON validation but is semantically wrong and some parsers reject it. Always use unquoted numeric values for offsets. The generator handles this automatically.
- 04
Using a page URL for contentUrl or embedUrl
Wrong"contentUrl": "https://example.com/videos/my-video"Right"contentUrl": "https://example.com/media/my-video.mp4"contentUrl must point at the video file itself (.mp4, .webm, .ogg), not at a page that embeds the video. embedUrl must point at an embeddable player endpoint (like a YouTube /embed/ URL), not at a watch page. Google uses contentUrl to crawl the video directly for duration extraction and thumbnail generation. A page URL in either field produces a broken rich result.
Compare your markup against this exampleruns in your browser
Schema properties in this example
Comments
Loading comments...