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.

npx skills add Illyism/seo-skills --skill youtube-seo
content/youtube-seo/SKILL.md
# 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