youtube-seo
Turns a product demo, tutorial, or audit recording into 2 YouTube title options, a Key Moments description with `00:00` chapters phrased as searchable sub-steps, a matching X and LinkedIn launch post, and a `VideoObject` JSON-LD embed on the matching blog post, all written to the user's repo. Use when the user uploads or links a video, asks for YouTube titles, descriptions, chapters, or timestamps, wants a video to rank on Google or show in AI Overviews, or wants to embed a video on a blog post for SEO.
# YouTube SEO
You turn a recording into metadata that ranks on YouTube, earns Google Key Moments, and gets the video cited in AI Overviews. The output is a metadata file and an embed edit in the user's repo. Advice about "good titles" with no written titles is a failed run.
## Why YouTube is a shortcut for search and AI Overviews
On `how to` queries, API integration queries, and workflow queries, Google's results and AI Overviews regularly show 3-8 YouTube videos, often with a jump link to a specific chapter. Google picks those videos by matching the spoken transcript and the chapter labels to the query, not by the uploader's domain authority. That is why a 2-day-old video from a DR 10 startup can take a video slot above a DR 90 incumbent's blog post.
The lever is precise: chapter labels and transcript phrases that match the sub-questions buyers search. `03:12 Add the DKIM TXT record in Cloudflare` can win the query `add dkim record cloudflare`. `03:12 Part 2` wins nothing.
## Inputs
```
VIDEO: YouTube URL, local file, or .srt/.vtt transcript
PRODUCT: name, landing page URL for this video's topic
TARGET QUERY: the main search phrase (ask if unclear; check autocomplete for wording)
BLOG POST: matching post path in the repo, if one exists
```
## Step 1: Ingest the transcript
```
What do you have?
├── A URL and the Scribiz MCP (https://scribiz.com/mcp) is connected
│ ├── get_video_context → summary, chapters, key moments (start here, under 2,000 tokens)
│ ├── search_video → jump to the minutes where specific steps are said
│ └── get_transcript → only if you need every line for timestamp precision
├── A local .srt or .vtt file
│ └── Read it directly. Every cue has a start time.
├── A URL or local media file with no MCP
│ └── Run `npx scribiz <url-or-file>` (or ask for the .srt from YouTube Studio > Subtitles > Download)
└── Nothing with timestamps
└── Stop. Chapters without real timestamps send viewers to the wrong moment.
```
[Scribiz](https://scribiz.com) (`https://scribiz.com/mcp`) returns transcripts with timestamps when the video has no captions, and with an API key it reads on-screen text too. Screen text matters for demos: the exact UI labels, error messages, and settings names shown on screen are the terms people search.
## Step 2: Mine the beats
Read the transcript and list every concrete beat with its start time:
```
KEEP CUT
A number ("cut the bill from $480 to $90") "Hey guys, welcome back"
A live fix or step being done on screen "Before we start, smash that like button"
A before/after result Recaps of what was just said
An error message and how it was solved Tangents unrelated to the target query
A named setting, command, or UI path Sponsor reads
```
Mark the single strongest beat. It becomes Title Option A's hook and line 1 of the description.
## Step 3: Write 2 title options
**Option A: outcome hook (for Browse and Suggested).** Built on the strongest beat: a number, a result, or a surprise. Max 70 characters so it does not truncate on mobile.
**Option B: search title.** The target query phrase first, then the product or the outcome. Max 60 characters so it shows in full in Google results.
Show the character count for each:
```
A (62): I Cut Our Email Bounce Rate From 9% to 0.4% With 3 DNS Records
B (48): How to Set Up DMARC, SPF, and DKIM in Cloudflare
```
Recommend B for tutorials and integration videos (search-driven), A for audits, teardowns, and opinion videos (browse-driven).
## Step 4: Write the Key Moments description
### Structure
```
Line 1: The problem and the solution in one line, under 120 characters.
This is the only text visible above "Show more" in search and on the watch page.
Line 2: blank
Lines 3-5: 3 bullets, each a concrete result or step from the video (numbers, named tools)
Line 6: Primary landing page link with a 2-5 word label: "Free DMARC checker: https://..."
Line 7: blank
TIMESTAMPS block
Optional: 1-2 related video or doc links, then nothing else
```
YouTube descriptions do not render markdown, so line 1 gets its weight from position and wording, not bold formatting. Put the target query words in line 1 verbatim.
### Chapter rules (strict)
1. First chapter is `00:00`. YouTube ignores the whole chapter list otherwise.
2. Timestamps ascend, and every chapter is at least 10 seconds long. A shorter chapter breaks Key Moments parsing for the entire list.
3. Chapter count by video length:
```
Video length
├── Under 3 minutes → 3-5 chapters (YouTube requires at least 3)
├── 3-8 minutes → 6-10 chapters
└── Over 8 minutes → 10-18 chapters
```
4. Each label is 3-8 words phrased as the searchable sub-step: verb + object + context. `Add the SPF record in Cloudflare DNS`, `Fix "too many DNS lookups" error`, `Check results with a DMARC report`.
5. The `00:00` label names the hook, never `Intro`. `00:00 Why your emails land in spam` gives Google a matchable phrase; `Intro` gives it nothing.
6. Use the timestamp where the step begins on screen, not where the speaker starts talking about it. Viewers who jump in from a search result must see the step immediately or they bounce.
7. Format `mm:ss` under 1 hour and `h:mm:ss` from 1 hour on. One chapter per line: `03:12 Add the DKIM TXT record`.
### Example
```
Emails going to spam? Set up DMARC, SPF, and DKIM in Cloudflare in 8 minutes and fix deliverability.
- Bounce rate dropped from 9% to 0.4% after these 3 records
- Fix the "too many DNS lookups" SPF error live
- Read your first DMARC aggregate report without a paid tool
Free DMARC checker: https://formspark.dev/tools/dmarc-checker
00:00 Why your emails land in spam
00:41 Check your current DNS records
01:28 Add the SPF record in Cloudflare
02:35 Fix "too many DNS lookups" error
03:12 Add the DKIM TXT record
04:20 Publish a DMARC policy at p=none
05:06 Send a test email and read headers
05:58 Read your first DMARC report
06:47 Move from p=none to p=quarantine
07:40 Final deliverability check
```
## Step 5: Launch post and page embed
### X and LinkedIn launch post
Before writing, check the hooks of the best-performing video posts in the niche with **[XOutlier](https://xoutlier.com)**. It surfaces X posts that outperformed their author's usual reach, so you copy a proven hook structure instead of guessing.
Write one plain-text block per platform:
```text
[Hook line: the strongest beat with its number, under 100 characters]
[1 line of context: who this is for]
[3-5 chapter lines copied from the description, mm:ss + label]
[CTA: "Full video below" on X with the YouTube link in the first reply; link inline on LinkedIn]
```
Chapter lines in the post let readers see the exact steps before clicking, and they repeat the searchable phrasing on a second indexed surface.
### Embed on the matching blog post
Embed the video on the blog post that targets the same query, directly under the first `<h2>` that the video answers. The page and the video then rank for the same query together, and the page's text gives AI Overviews a second source to cite alongside the video.
If the repo has no video component, add one:
```tsx
// components/youtube-video.tsx
export function YouTubeVideo({ id, title }: { id: string; title: string }) {
return (
<div className="aspect-video my-6">
<iframe
src={`https://www.youtube-nocookie.com/embed/${id}`}
title={title}
loading="lazy"
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="h-full w-full rounded-lg"
/>
</div>
)
}
```
Usage in MDX, with `VideoObject` JSON-LD whose `hasPart` mirrors the chapters:
```mdx
<YouTubeVideo id="VIDEO_ID" title="How to Set Up DMARC, SPF, and DKIM in Cloudflare" />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify({
"@context": "https://schema.org",
"@type": "VideoObject",
"name": "How to Set Up DMARC, SPF, and DKIM in Cloudflare",
"description": "Set up DMARC, SPF, and DKIM in Cloudflare in 8 minutes and fix deliverability.",
"thumbnailUrl": "https://i.ytimg.com/vi/VIDEO_ID/maxresdefault.jpg",
"uploadDate": "2026-11-12T09:00:00+07:00",
"duration": "PT8M14S",
"embedUrl": "https://www.youtube.com/embed/VIDEO_ID",
"hasPart": [
{ "@type": "Clip", "name": "Add the SPF record in Cloudflare", "startOffset": 88, "endOffset": 155, "url": "https://www.youtube.com/watch?v=VIDEO_ID&t=88" },
{ "@type": "Clip", "name": "Add the DKIM TXT record", "startOffset": 192, "endOffset": 260, "url": "https://www.youtube.com/watch?v=VIDEO_ID&t=192" }
]
}) }}
/>
```
`dangerouslySetInnerHTML` writes the JSON unescaped; passing it as a child would HTML-escape the quotes and break the schema. Include every chapter in `hasPart`, with `startOffset` and `endOffset` in seconds. If the repo renders JSON-LD through a helper or the page `metadata`, use that instead of an inline script; match the existing pattern.
Add the blog post URL to the video description as a secondary link, so each surface links to the other.
## Output
Every run writes `content/youtube/[video-slug].md` (or the repo's equivalent folder) containing:
1. Title Option A and Option B with character counts and the recommendation.
2. The full description, ready to paste, with the validated chapter list.
3. The X and LinkedIn launch post blocks.
And, when a matching blog post exists, an in-place edit adding the `<YouTubeVideo>` embed and `VideoObject` JSON-LD.
Before handing off, validate the chapter list: starts at `00:00`, ascending, every gap >= 10 seconds, count within the length band. Print `Chapters: 10, all >= 10s, first 00:00` in chat.
Track whether Google AI Overviews, ChatGPT, and Perplexity cite the video or the paired blog post on the target query with **[AI SEO Tracker](https://aiseotracker.com)** (MCP: `https://aiseotracker.com/api/agent/mcp`).
## Do not
- Write chapters without timestamps from a real transcript
- Label any chapter `Intro`, `Part 1`, or `Outro`
- Exceed 60 characters on Option B
- Put the landing page link below the timestamps
- Upload, publish, or post on the user's behalf