
Video SEO is the work of getting your videos found, indexed and shown in Google's video results. Google doesn't watch your video to understand it. It reads the watch page, VideoObject structured data, a video sitemap and a crawlable thumbnail, so those are what you optimize.
AIOZ Stream doesn't generate that markup for you, but its video API returns every field you need to build it. This guide covers the Google requirements first, then shows how each one maps to AIOZ Stream fields.
TL;DR:
name, thumbnailUrl and uploadDate. Google recommends contentUrl (or embedUrl), description and duration on top.duration from seconds to ISO 8601 before you use it.Updated Nov 4, 2026: new sections on watch pages, video sitemaps, key moments with Clip markup, thumbnail rules and Search Console checks, plus a labelled field-mapping diagram and a tested duration converter.
Google first has to find the video, and according to Google's video SEO best practices, it looks for a <video>, <embed>, <iframe> or <object> element in the rendered HTML. If you inject the player with JavaScript, check that it shows up in the URL Inspection tool, and don't make it wait for a click or swipe.
Then Google decides whether the video can be indexed: the page that embeds it must be indexed and performing well in Search, and the video must be visible on that page with a valid thumbnail at a stable URL. If any of these fail, your page can still rank as a text result, but the video won't get video features.
The page Google wants is a watch page, where watching one video is the main reason you'd visit. A product page with a demo clip or a blog post that reviews an embedded video doesn't count. Give each watch page its own title and description, and use the same video on other pages if you like.

Video SEO also sits inside a wider accessibility topic, and our guide to what WCAG requires for video covers captions and transcripts, which help both viewers and search engines. If a term in this post is new, the video streaming glossary defines it.
Google's VideoObject reference lists three required properties: name, thumbnailUrl and uploadDate in ISO 8601 format. Without them, Google may not be able to extract any information about your video.
The recommended list is longer, and you should start with contentUrl, the URL of the video bytes, which Google calls the most effective way to fetch your file; if you can't, give embedUrl for the player instead. Add description and duration as well, plus expires only when a video is due to go away.
The AIOZ Stream get-video-detail response has a field for each of these. title, description, created_at and duration sit on the video object. The assets object holds thumbnail_url, hls_url, mp4_url, hls_player_url and dash_player_url, and the AIOZ Stream developer guide shows how to call it. Here is the template we'd render into each watch page:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "VideoObject",
"name": "{{ video.title }}",
"description": "{{ video.description }}",
"thumbnailUrl": "{{ video.assets.thumbnail_url }}",
"uploadDate": "{{ video.created_at }}",
"duration": "{{ video.duration | iso8601 }}",
"contentUrl": "{{ video.assets.hls_url }}",
"embedUrl": "{{ video.assets.hls_player_url }}"
}
</script>For contentUrl, Google lists M3U8 among its supported file types, so the hls_url playlist works. Keep each name, description and thumbnail unique per video, because Google asks for unique values on every video on your site.
The docs' example response returns duration as seconds, 283.492426, while Google expects an ISO 8601 duration such as PT4M43S. If you pass the raw number through, nothing throws an error and the markup quietly fails validation, so convert it with a small helper:
function toIsoDuration(seconds) {
const s = Math.round(seconds);
const h = Math.floor(s / 3600);
const m = Math.floor((s % 3600) / 60);
return `PT${h ? h + 'H' : ''}${m ? m + 'M' : ''}${s % 60}S`;
}
console.log(toIsoDuration(283.492426)); // PT4M43S
console.log(toIsoDuration(3725.2)); // PT1H2M5SWe ran this with Node.js 22 on Nov 4, 2026, and the comments show the real output. The sitemap tag works the other way: <video:duration> takes whole seconds from 1 to 28,800, so you only round the AIOZ value there.
A video sitemap tells Google where your watch pages and their videos are. You can add video tags to your existing sitemap or keep a separate one, and Google's video sitemap reference covers every tag. The required ones are <video:thumbnail_loc>, <video:title>, <video:description> (up to 2,048 characters), and either <video:content_loc> or <video:player_loc>.
Every URL you list must be reachable by Googlebot without a login, robots.txt block or firewall, and you shouldn't list a video that's only a small addendum to the page. One entry built from AIOZ Stream fields looks like this:
<url>
<loc>https://example.com/watch/product-tour</loc>
<video:video>
<video:thumbnail_loc>{{ video.assets.thumbnail_url }}</video:thumbnail_loc>
<video:title>{{ video.title }}</video:title>
<video:description>{{ video.description }}</video:description>
<video:player_loc>{{ video.assets.hls_player_url }}</video:player_loc>
<video:duration>{{ video.duration | round }}</video:duration>
</video:video>
</url>Sitemaps and structured data do different jobs, and you'll want both. The sitemap helps Google find the watch page, and the markup on the page tells Google what the video is once it gets there.
Key moments let searchers jump to a segment of your video, like chapters in a book. Google tries to detect segments on its own, but it gives priority to the ones you set, and for a video embedded on your own site you have two options.
Clip markup lists each segment yourself, with a name, a startOffset in seconds and a url that points to that moment on the same page path. SeekToAction instead tells Google how your timestamp URLs work, and Google picks the moments. Clip works in every language Google Search supports, while SeekToAction covers 12 languages, including English, Spanish and Japanese.
If you already store chapters on AIOZ Stream, you have the data. The chapter API keeps one WebVTT file per language, and each cue's start time and text become one Clip:
"hasPart": [
{
"@type": "Clip",
"name": "Upload and encoding",
"startOffset": 0,
"endOffset": 95,
"url": "https://example.com/watch/product-tour?t=0"
},
{
"@type": "Clip",
"name": "Playback and analytics",
"startOffset": 95,
"endOffset": 283,
"url": "https://example.com/watch/product-tour?t=95"
}
]Your watch page then has to read the t parameter and start playback at that second. Our comparison of the media chapter and media transcript APIs shows how the chapter files are structured.
Every video needs a valid thumbnail to get video features, and Google accepts BMP, GIF, JPEG, PNG, WebP, SVG and AVIF, at a minimum of 60x30 pixels, and larger is better. The file must be reachable by Googlebot and Googlebot Images at a stable URL.
If you set the thumbnail in several places, such as the poster attribute, the sitemap and thumbnailUrl, use the same URL in all of them. On AIOZ Stream, assets.thumbnail_url is built from the video ID, and you can replace the image with a PNG or JPG through the upload-thumbnail endpoint. Pick a frame that says what the video is about, because it's the first thing a searcher sees.
Search Console gives you three reports for this, and you should check them after your first deploy and after any template change:
Before you deploy, paste a watch page into Google's Rich Results Test to validate the markup. After you deploy, run URL Inspection on one page to confirm Google sees the player in the rendered HTML.
Everything you need comes from one call, and the request below is copied from the Video API reference; we didn't run it here, and you'd swap in your own video ID and token:
curl --location 'https://api.aiozstream.network/api/media/video_id' \
--header 'authorization: Bearer your_access_token_here'The response carries the fields from the template above, plus is_public, because only public videos belong in your sitemap and markup. Captions help search too, because they give crawlers real text from the video, and our captions and transcripts developer guide shows how to add them. To embed the player on your watch page, follow how to embed the AIOZ Stream player.
What the docs don't cover yet:
For the full list of video fields and endpoints, start with the AIOZ Stream Video API docs.
What are the required fields for VideoObject schema?
Google requires name, thumbnailUrl and uploadDate in ISO 8601 format, and it recommends contentUrl or embedUrl, description and duration for richer results.
Does AIOZ Stream automatically generate VideoObject schema markup?
No. Its docs don't mention structured data, JSON-LD, oEmbed or sitemap generation, so you build the markup from the API fields.
Which AIOZ Stream API fields map to VideoObject properties?
title to name, description to description, assets.thumbnail_url to thumbnailUrl, created_at to uploadDate, duration to duration after conversion, assets.hls_url to contentUrl, and assets.hls_player_url to embedUrl.
Why would my VideoObject markup fail validation with every field filled in?
The duration is the usual cause. AIOZ Stream returns seconds as a number such as 283.492426, and Google needs ISO 8601 such as PT4M43S.
Do I need both a video sitemap and structured data?
Yes, in most cases. The sitemap helps Google find the watch page, and the structured data tells Google what the video is once it's there.
How do I get key moments for my videos?
Add Clip markup with a name, start offset and timestamp URL for each segment, or SeekToAction markup that tells Google how your timestamp URLs work.
How do I know if Google indexed my video?
Open the Video indexing report in Search Console. It lists watch pages with an indexed video and the reason each other video wasn't indexed.

Video encoding compresses raw video with a codec, then a container packages it for playback. See how codecs, containers, bitrate and rate control fit together.

Video streaming protocols move video from encoder to viewer. See how RTMP, SRT, HLS, DASH and WebRTC differ in latency, transport and where each one fits.

Decentralized live streaming sends a real-time broadcast through independently run nodes instead of one company's servers. Here's how each stage of it works.

A video streaming glossary of 114 terms, from AAC and ABR to WebRTC and Widevine, each defined in plain English with sources and links to the deep posts.

Read the AIOZ Stream Statistics panel: bitrate in and out, FPS in and out and current viewers, plus the documented rules for each and what the docs leave out.

Embed an AIOZ Stream livestream with the Share button's iframe or send the Public Playback URL as a link. Covers API fields, public access limits and doc gaps.