
Youtube
- 285 installs
- 291 repo stars
- Updated April 10, 2026
- agricidaniel/claude-youtube
Plan YouTube videos, titles, descriptions, tags, thumbnails briefs, and publishing cadence to grow subscribers and drive traffic from search and suggested feeds.
About
YouTube skill from agricidaniel/claude-youtube guides Claude through creator growth workflows: ideation, scripting hooks, metadata SEO, thumbnail direction, and publishing cadence. It targets organic distribution—helping brands and founders turn long-form video into searchable, suggestible content that builds audience and referral traffic over time.
- Video topic and hook planning
- Title, description, and tag SEO
- Thumbnail and packaging briefs
- Upload and scheduling workflows
- Subscriber and traffic growth tactics
Youtube by the numbers
- 285 all-time installs (skills.sh)
- Ranked #867 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/agricidaniel/claude-youtube --skill youtubeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 285 |
|---|---|
| repo stars | ★ 291 |
| Last updated | April 10, 2026 |
| Repository | agricidaniel/claude-youtube ↗ |
What it does
Plan YouTube videos, titles, descriptions, tags, thumbnails briefs, and publishing cadence to grow subscribers and drive traffic from search and suggested feeds.
Files
Claude YouTube — YouTube Creator Skill
Orchestrator for 14 sub-skills covering every aspect of YouTube channel growth,
optimisation, and monetisation. You route, delegate, and quality-check — sub-skills
and execution scripts do the work.
Command Router
| Command | Sub-Skill | Trigger Phrases |
|---|---|---|
/youtube audit | sub-skills/audit.md | "audit my channel", "channel health", "what's wrong with my channel", "my channel isn't growing" |
/youtube seo | sub-skills/seo.md | "video SEO", "rank higher", "keyword research", "improve search ranking" |
/youtube script | sub-skills/script.md | "write a script", "script for my video", "help me script" |
/youtube hook | sub-skills/hook.md | "write a hook", "improve my intro", "first 30 seconds", "opening" |
/youtube thumbnail | sub-skills/thumbnail.md | "thumbnail brief", "improve CTR", "design thumbnail" |
/youtube strategy | sub-skills/strategy.md | "channel strategy", "content plan", "positioning", "niche" |
/youtube calendar | sub-skills/calendar.md | "content calendar", "upload schedule", "what should I post this month" |
/youtube shorts | sub-skills/shorts.md | "Shorts", "short video", "vertical video", "Shorts strategy" |
/youtube analyze | sub-skills/analyze.md | "analyze metrics", "why are views dropping", "interpret analytics" |
/youtube repurpose | sub-skills/repurpose.md | "repurpose video", "turn into Shorts", "cross-platform", "extract clips" |
/youtube monetize | sub-skills/monetize.md | "monetize", "make money", "revenue", "brand deals", "memberships" |
/youtube competitor | sub-skills/competitor.md | "competitor analysis", "spy on channel", "what is [channel] doing" |
/youtube metadata | sub-skills/metadata.md | "upload metadata", "title and description", "pre-publish checklist" |
/youtube ideate | sub-skills/ideate.md | "video ideas", "what should I make next", "brainstorm", "content ideas" |
If the user's request doesn't clearly match one command, ask a clarifying question. If the request spans multiple sub-skills (e.g., "help me plan and script my next video"), run the relevant sub-skills sequentially, passing output from each as input to the next.
Context-Gathering Protocol
Before invoking ANY sub-skill, you MUST collect these three inputs. If any are missing, ask:
1. Channel niche/topic — What is the channel about? Be specific (not "tech" — "budget Android phone reviews"). 2. Channel size tier:
- New: < 1K subscribers
- Growing: 1K–10K subscribers
- Established: 10K–100K subscribers
- Authority: 100K+ subscribers
3. Primary goal: Growth / Monetisation / Brand Authority / Audience Engagement
For audit, analyze, and competitor sub-skills, also collect the channel URL or handle.
Channel Type Detection
Based on niche and content description, classify into one of 9 types and load the matching template from templates/:
| Type | Template | Signals |
|---|---|---|
| Education | templates/education-channel.md | How-to, explainers, courses |
| Entertainment | templates/entertainment-channel.md | Comedy, challenges, reactions |
| Tutorial | templates/tutorial-channel.md | Step-by-step, walkthroughs, demos |
| Vlog | templates/vlog-channel.md | Day-in-life, personal updates |
| Review | templates/review-channel.md | Product reviews, comparisons, unboxing |
| Commentary | templates/commentary-channel.md | News commentary, opinion, essays |
| Niche Authority | templates/niche-authority-channel.md | Deep-dive single topic, expert positioning |
| Personal Brand | templates/personal-brand-channel.md | Creator-as-brand, multi-format |
| Shorts-First | templates/shorts-first-channel.md | Primarily vertical content |
Parallel Agent Rules
`audit` sub-skill — spawn 4 parallel agents:
- Agent A: Technical SEO audit (loads
references/seo-playbook.md) - Agent B: Performance audit (loads
references/analytics-guide.md) - Agent C: Content strategy audit (loads
references/algorithm-guide.md) - Agent D: Monetisation audit (loads
references/monetization-guide.md)
`competitor` sub-skill — spawn 4 parallel agents:
- Agent A: Top video analysis
- Agent B: Keyword gap analysis
- Agent C: Format gap analysis
- Agent D: Audience gap analysis (comment mining)
All other sub-skills run inline (single-threaded). If Agent tool is unavailable, fall back to sequential inline execution.
Reference Files
Load on-demand when a sub-skill requests them. Never pre-load all at once. Do not reload a reference file already in context for this session.
| File | Content |
|---|---|
references/algorithm-guide.md | 3-system architecture, testing cascade, CTR/AVD benchmarks, 2024-2025 changes |
references/seo-playbook.md | Title/description/tags/chapters/hashtags rules, VideoObject schema |
references/retention-scripting-guide.md | Hook frameworks, pattern interrupts, CTA placement, retention graphs |
references/thumbnail-ctr-guide.md | CTR by niche, face psychology, A/B testing, title formulas |
references/shorts-playbook.md | Shorts algorithm, format specs, monetisation, repurposing |
references/analytics-guide.md | Metrics hierarchy, funnel ratios, RPM/CPM by niche |
references/monetization-guide.md | YPP tiers, 7 revenue streams, brand deal rates |
references/repurposing-guide.md | Hub/Hero/Help model, cross-platform workflows, platform specs |
references/dataforseo-integration.md | DataForSEO MCP tool reference, YouTube SERP, keyword research, trends |
DataForSEO MCP Integration (Optional)
When the DataForSEO MCP server is available (configured in ~/.claude/settings.json), youtube skills use live data for keyword research, YouTube SERP analysis, trend intelligence, video metadata, and competitive research. Falls back to WebSearch + execution scripts when unavailable.
Reference: Load references/dataforseo-integration.md for full tool reference, parameters, and efficiency guidelines.
Detection: Attempt any DataForSEO tool call (e.g., serp_youtube_organic_live_advanced). If it fails, fall back to WebSearch + execution scripts. Never block a workflow because DataForSEO is unavailable.
Default parameters: location_code=2840 (US), language_code="en"
Key YouTube DataForSEO Tools
| Tool | Purpose |
|---|---|
serp_youtube_organic_live_advanced | YouTube search results for a keyword — videos, channels, playlists with view counts |
serp_youtube_video_info_live_advanced | Deep video analysis — views, likes, comments, tags, category, subtitles |
serp_youtube_video_comments_live_advanced | Video comments with engagement data |
serp_youtube_video_subtitles_live_advanced | Video transcript/subtitles extraction |
kw_data_google_ads_search_volume | Search volume, CPC, competition for keyword arrays |
dataforseo_labs_google_keyword_ideas | Keyword ideas from seed keywords |
dataforseo_labs_google_keyword_suggestions | Autocomplete-style keyword suggestions |
dataforseo_labs_bulk_keyword_difficulty | Keyword difficulty scores (0-100) |
dataforseo_labs_search_intent | Intent classification (informational/commercial/transactional) |
kw_data_google_trends_explore | Google Trends time series (supports YouTube-specific filtering) |
Sub-Skill → DataForSEO Module Mapping
| Sub-Skill | DataForSEO Tools Used |
|---|---|
| ideate | YouTube SERP + keyword ideas + trends + volume |
| seo | Volume + difficulty + intent + YouTube SERP competition |
| competitor | YouTube SERP × keywords + video info + comments |
| strategy | Trends + volume + YouTube SERP + keyword ideas |
| calendar | Google Trends for seasonal planning + volume for prioritisation |
| analyze | YouTube SERP position tracking + video info |
| audit | YouTube SERP + volume + video info + keyword difficulty |
| shorts | YouTube SERP (Shorts filter) + trends |
| hook | YouTube SERP top video titles for keyword |
| thumbnail | YouTube SERP competitor thumbnails |
| metadata | Volume + difficulty for tag optimisation |
API Credit Awareness
DataForSEO charges per API call. Typical workflow costs $0.002-$0.04. Rules:
- Batch keywords into single calls (volume, difficulty, intent tools accept arrays)
- Don't re-fetch data already retrieved in the same session
- Use
dataforseo_labs_google_keyword_overviewfor single-keyword lookups - Warn the user before running expensive operations
NanoBanana MCP — Thumbnail Generation (Optional)
When the NanoBanana MCP server is configured, the thumbnail sub-skill can generate actual thumbnail images using Gemini models instead of just producing text briefs.
Tool: generate_image
Recommended thumbnail settings:
aspect_ratio:"16:9"(YouTube standard)resolution:"4k"(1280×720 minimum for YouTube)model_tier:"nb2"(fast, high-quality production assets)
Detection: Attempt generate_image tool call. If unavailable, deliver text-based thumbnail briefs only — they are detailed enough for any designer to execute.
Workflow: Primary thumbnail + 3 A/B variants = 4 generate_image calls per brief. See sub-skills/thumbnail.md for prompt engineering guidelines.
Execution Scripts
Scripts require YouTube API credentials. Before calling any script: 1. Check if YOUTUBE_API_KEY environment variable exists 2. If missing, provide the user with setup instructions instead of failing silently 3. For Analytics API scripts, check OAuth token exists
If credentials are absent, fall back to asking the user to provide data manually (e.g., paste YouTube Studio screenshots or describe their metrics).
| Script | Purpose | Quota Cost |
|---|---|---|
execution/fetch_channel_data.py | Channel stats + last N videos via Data API v3 | ~16 units |
execution/fetch_video_analytics.py | Private analytics (own channel, OAuth) | ~5 units |
execution/search_competitor_videos.py | Search competitor videos (expensive) | 100 units/search |
execution/fetch_transcript.py | Video transcript extraction | 1-2 units |
execution/utils/quota_tracker.py | Tracks 10K unit/day quota | 0 units |
execution/utils/youtube_auth.py | API key + OAuth handler | 0 units |
Quality Gates
Every sub-skill output MUST pass these checks before delivery:
1. Specificity — Every recommendation must be actionable for THIS channel. No generic advice like "post consistently" without specifying cadence for their tier. 2. Data grounding — Every benchmark cited must come from a reference file. Never hallucinate statistics. If unsure, say "benchmark unavailable" and explain. 3. Completeness — All sections in the sub-skill's output template must be present. Missing sections = incomplete deliverable.
Self-Anneal Loop
If a sub-skill output fails a quality gate: 1. Identify which gate failed and why 2. Re-read the relevant reference file for missing data 3. Re-generate only the failing sections 4. Re-check all three gates 5. If still failing after 2 attempts, deliver with explicit caveats noting the limitation
Output Format
Default to markdown. For metadata sub-skill, produce copy-paste-ready plain text blocks. For calendar, produce a markdown table. For audit, produce a structured report with scores. Always end with a "Next Steps" section pointing the user to the logical next sub-skill (e.g., after audit → suggest strategy or the lowest-scoring dimension's sub-skill).
"""
Fetch public channel data via YouTube Data API v3.
Uses the uploads playlist path (channels.list → playlistItems.list → videos.list)
to avoid expensive search.list calls. Total cost: ~16 units for channel + 10 videos.
Input: channel ID, handle (@username), or channel URL
Output: JSON with channel stats and top N videos
Usage:
python execution/fetch_channel_data.py UCxxxxxxxxxxxxxxxx
python execution/fetch_channel_data.py @channelhandle
python execution/fetch_channel_data.py "https://youtube.com/@handle"
python execution/fetch_channel_data.py UCxxx --videos 20
"""
import argparse
import json
import re
import sys
from datetime import datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from utils.quota_tracker import check_quota, consume_quota
from utils.youtube_auth import get_data_api_service
TMP_DIR = Path.home() / ".claude" / ".tmp"
def parse_channel_input(raw):
"""Extract channel ID or handle from various input formats."""
raw = raw.strip()
# Direct channel ID
if raw.startswith("UC") and len(raw) == 24:
return {"type": "id", "value": raw}
# Handle format
if raw.startswith("@"):
return {"type": "handle", "value": raw}
# URL formats
url_patterns = [
(r"youtube\.com/channel/(UC[\w-]{22})", "id"),
(r"youtube\.com/@([\w.-]+)", "handle"),
(r"youtube\.com/c/([\w.-]+)", "custom_url"),
(r"youtube\.com/user/([\w.-]+)", "username"),
]
for pattern, ptype in url_patterns:
match = re.search(pattern, raw)
if match:
val = match.group(1)
if ptype == "handle":
val = f"@{val}"
return {"type": ptype, "value": val}
# Assume it's a handle without @
if not raw.startswith("http"):
return {"type": "handle", "value": f"@{raw}"}
return {"type": "unknown", "value": raw}
def resolve_channel_id(service, parsed):
"""Resolve various inputs to a channel ID."""
if parsed["type"] == "id":
return parsed["value"]
if parsed["type"] == "handle":
response = service.channels().list(
part="id", forHandle=parsed["value"].lstrip("@")
).execute()
consume_quota("channels.list")
items = response.get("items", [])
if items:
return items[0]["id"]
if parsed["type"] in ("custom_url", "username"):
response = service.search().list(
part="id", q=parsed["value"], type="channel", maxResults=1
).execute()
consume_quota("search.list")
items = response.get("items", [])
if items:
return items[0]["id"]["channelId"]
return None
def fetch_channel_info(service, channel_id):
"""Fetch channel snippet, statistics, and branding."""
response = service.channels().list(
part="snippet,statistics,contentDetails,brandingSettings",
id=channel_id,
).execute()
consume_quota("channels.list")
items = response.get("items", [])
if not items:
return None
ch = items[0]
snippet = ch.get("snippet", {})
stats = ch.get("statistics", {})
content = ch.get("contentDetails", {})
branding = ch.get("brandingSettings", {}).get("channel", {})
return {
"channel_id": channel_id,
"title": snippet.get("title"),
"description": snippet.get("description", "")[:500],
"custom_url": snippet.get("customUrl"),
"published_at": snippet.get("publishedAt"),
"country": snippet.get("country"),
"subscriber_count": int(stats.get("subscriberCount", 0)),
"total_views": int(stats.get("viewCount", 0)),
"video_count": int(stats.get("videoCount", 0)),
"uploads_playlist": content.get("relatedPlaylists", {}).get("uploads"),
"keywords": branding.get("keywords", ""),
}
def fetch_recent_videos(service, uploads_playlist_id, max_results=10):
"""Fetch recent videos via uploads playlist (cheaper than search.list)."""
# Step 1: Get video IDs from playlist
video_ids = []
next_page = None
remaining = max_results
while remaining > 0:
page_size = min(remaining, 50)
response = service.playlistItems().list(
part="contentDetails",
playlistId=uploads_playlist_id,
maxResults=page_size,
pageToken=next_page,
).execute()
consume_quota("playlistItems.list")
for item in response.get("items", []):
video_ids.append(item["contentDetails"]["videoId"])
remaining -= 1
if remaining <= 0:
break
next_page = response.get("nextPageToken")
if not next_page:
break
if not video_ids:
return []
# Step 2: Get full video details in batches of 50
videos = []
for i in range(0, len(video_ids), 50):
batch = video_ids[i:i + 50]
response = service.videos().list(
part="snippet,statistics,contentDetails",
id=",".join(batch),
).execute()
consume_quota("videos.list", len(batch))
for v in response.get("items", []):
snippet = v.get("snippet", {})
stats = v.get("statistics", {})
content = v.get("contentDetails", {})
videos.append({
"video_id": v["id"],
"title": snippet.get("title"),
"published_at": snippet.get("publishedAt"),
"description": snippet.get("description", "")[:200],
"duration": content.get("duration"),
"views": int(stats.get("viewCount", 0)),
"likes": int(stats.get("likeCount", 0)),
"comments": int(stats.get("commentCount", 0)),
"thumbnail": snippet.get("thumbnails", {}).get("high", {}).get("url"),
})
return videos
def main():
parser = argparse.ArgumentParser(description="Fetch YouTube channel data")
parser.add_argument("channel", help="Channel ID, @handle, or URL")
parser.add_argument("--videos", type=int, default=10,
help="Number of recent videos to fetch (default: 10)")
parser.add_argument("--no-cache", action="store_true",
help="Skip cache and fetch fresh data")
args = parser.parse_args()
# Check quota before starting
estimated_cost = 2 + (args.videos // 50 + 1) * 2 # channels + playlist + videos
quota_check = check_quota("videos.list", estimated_cost)
if not quota_check.get("can_execute", True):
print(json.dumps({"error": quota_check["error"]}), file=sys.stderr)
sys.exit(1)
# Check cache
parsed = parse_channel_input(args.channel)
cache_key = parsed["value"].replace("/", "_").replace("@", "")
today = datetime.now().strftime("%Y-%m-%d")
cache_path = TMP_DIR / f"channel_{cache_key}_{today}.json"
if cache_path.exists() and not args.no_cache:
with open(cache_path) as f:
cached = json.load(f)
cached["_cached"] = True
print(json.dumps(cached, indent=2))
return
# Build service
service, error = get_data_api_service()
if error:
print(json.dumps(error), file=sys.stderr)
sys.exit(1)
# Resolve channel ID
channel_id = resolve_channel_id(service, parsed)
if not channel_id:
print(json.dumps({"error": f"Could not resolve channel: {args.channel}"}),
file=sys.stderr)
sys.exit(1)
# Fetch data
channel_info = fetch_channel_info(service, channel_id)
if not channel_info:
print(json.dumps({"error": f"Channel not found: {channel_id}"}),
file=sys.stderr)
sys.exit(1)
videos = []
if channel_info.get("uploads_playlist"):
videos = fetch_recent_videos(
service, channel_info["uploads_playlist"], args.videos
)
result = {
"channel": channel_info,
"videos": videos,
"fetched_at": datetime.now().isoformat(),
"quota_consumed": estimated_cost,
}
# Cache result
TMP_DIR.mkdir(parents=True, exist_ok=True)
with open(cache_path, "w") as f:
json.dump(result, f, indent=2)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
"""
Fetch video transcript/captions via multiple fallback methods.
Cascade:
1. YouTube Data API v3 captions (requires OAuth for own videos)
2. yt-dlp auto-subtitle extraction (if installed)
3. Graceful failure with instructions
Input: video ID or URL
Output: JSON with transcript text, timestamps, language
Usage:
python execution/fetch_transcript.py dQw4w9WgXcQ
python execution/fetch_transcript.py "https://youtube.com/watch?v=dQw4w9WgXcQ"
python execution/fetch_transcript.py VIDEO_ID --format text
python execution/fetch_transcript.py VIDEO_ID --format segments
"""
import argparse
import json
import re
import shutil
import subprocess
import sys
import tempfile
from datetime import datetime
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from utils.quota_tracker import consume_quota
TMP_DIR = Path.home() / ".claude" / ".tmp"
def parse_video_id(raw):
"""Extract video ID from URL or direct ID input."""
raw = raw.strip()
# Direct video ID (11 characters)
if re.match(r"^[\w-]{11}$", raw):
return raw
# URL patterns
patterns = [
r"(?:youtube\.com/watch\?v=|youtu\.be/|youtube\.com/shorts/)([\w-]{11})",
]
for pattern in patterns:
match = re.search(pattern, raw)
if match:
return match.group(1)
return raw # Return as-is, let the API validate
def fetch_via_ytdlp(video_id, lang="en"):
"""Fetch transcript using yt-dlp (fallback method)."""
ytdlp = shutil.which("yt-dlp")
if not ytdlp:
return None, "yt-dlp not installed"
url = f"https://www.youtube.com/watch?v={video_id}"
with tempfile.TemporaryDirectory() as tmpdir:
output_template = str(Path(tmpdir) / "transcript")
cmd = [
ytdlp,
"--skip-download",
"--write-auto-sub",
"--sub-lang", lang,
"--sub-format", "vtt",
"--output", output_template,
url,
]
try:
result = subprocess.run(
cmd, capture_output=True, text=True, timeout=30
)
except subprocess.TimeoutExpired:
return None, "yt-dlp timed out after 30 seconds"
# Find the subtitle file
vtt_files = list(Path(tmpdir).glob("*.vtt"))
if not vtt_files:
# Try SRT format as fallback
cmd[cmd.index("vtt")] = "srt"
try:
subprocess.run(cmd, capture_output=True, text=True, timeout=30)
except subprocess.TimeoutExpired:
return None, "yt-dlp timed out"
srt_files = list(Path(tmpdir).glob("*.srt"))
if not srt_files:
return None, f"No subtitles found. yt-dlp stderr: {result.stderr[:200]}"
return parse_srt(srt_files[0].read_text()), None
return parse_vtt(vtt_files[0].read_text()), None
def parse_vtt(content):
"""Parse WebVTT subtitle content into segments."""
segments = []
lines = content.strip().split("\n")
current_text = []
current_start = None
for line in lines:
line = line.strip()
# Skip header and empty lines
if line.startswith("WEBVTT") or line.startswith("Kind:") or \
line.startswith("Language:") or not line:
if current_text and current_start:
text = " ".join(current_text).strip()
# Remove VTT tags
text = re.sub(r"<[^>]+>", "", text)
if text:
segments.append({"start": current_start, "text": text})
current_text = []
continue
# Timestamp line
time_match = re.match(
r"(\d{2}:\d{2}:\d{2}\.\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2}\.\d{3})", line
)
if time_match:
if current_text and current_start:
text = " ".join(current_text).strip()
text = re.sub(r"<[^>]+>", "", text)
if text:
segments.append({"start": current_start, "text": text})
current_text = []
current_start = time_match.group(1)
continue
# Skip numeric cue identifiers
if re.match(r"^\d+$", line):
continue
current_text.append(line)
# Last segment
if current_text and current_start:
text = " ".join(current_text).strip()
text = re.sub(r"<[^>]+>", "", text)
if text:
segments.append({"start": current_start, "text": text})
# Deduplicate consecutive identical segments
deduped = []
for seg in segments:
if not deduped or deduped[-1]["text"] != seg["text"]:
deduped.append(seg)
return deduped
def parse_srt(content):
"""Parse SRT subtitle content into segments."""
segments = []
blocks = re.split(r"\n\n+", content.strip())
for block in blocks:
lines = block.strip().split("\n")
if len(lines) < 3:
continue
time_match = re.match(
r"(\d{2}:\d{2}:\d{2},\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2},\d{3})",
lines[1],
)
if time_match:
start = time_match.group(1).replace(",", ".")
text = " ".join(lines[2:]).strip()
text = re.sub(r"<[^>]+>", "", text)
if text:
segments.append({"start": start, "text": text})
return segments
def segments_to_text(segments):
"""Convert segments to plain text."""
return " ".join(seg["text"] for seg in segments)
def main():
parser = argparse.ArgumentParser(description="Fetch YouTube video transcript")
parser.add_argument("video", help="Video ID or URL")
parser.add_argument("--lang", default="en", help="Language code (default: en)")
parser.add_argument("--format", choices=["text", "segments", "both"],
default="both", help="Output format (default: both)")
args = parser.parse_args()
video_id = parse_video_id(args.video)
# Check cache
TMP_DIR.mkdir(parents=True, exist_ok=True)
cache_path = TMP_DIR / f"transcript_{video_id}_{args.lang}.json"
if cache_path.exists():
with open(cache_path) as f:
cached = json.load(f)
cached["_cached"] = True
print(json.dumps(cached, indent=2))
return
# Try yt-dlp (most reliable for public videos, no API quota cost)
segments, error = fetch_via_ytdlp(video_id, args.lang)
if segments:
result = {
"video_id": video_id,
"language": args.lang,
"method": "yt-dlp",
"segment_count": len(segments),
"fetched_at": datetime.now().isoformat(),
}
if args.format in ("segments", "both"):
result["segments"] = segments
if args.format in ("text", "both"):
result["text"] = segments_to_text(segments)
# Cache
with open(cache_path, "w") as f:
json.dump(result, f, indent=2)
print(json.dumps(result, indent=2))
return
# All methods failed
print(json.dumps({
"error": "Could not fetch transcript",
"video_id": video_id,
"attempts": [
{"method": "yt-dlp", "error": error or "not attempted"},
],
"suggestions": [
"Install yt-dlp: pip install yt-dlp",
"Check if the video has captions enabled",
"Try a different language with --lang",
"For private videos, ensure OAuth is configured",
],
}, indent=2), file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
"""
Fetch private video analytics from YouTube Analytics API.
Requires OAuth 2.0 authentication. Can ONLY access analytics for channels
you own or manage. Competitor analytics are NOT accessible via any official API.
Input: video ID(s), optional date range and metric list
Output: JSON with views, watch time, AVD, CTR, impressions, subs gained, etc.
Usage:
python execution/fetch_video_analytics.py VIDEO_ID
python execution/fetch_video_analytics.py VIDEO_ID --days 90
python execution/fetch_video_analytics.py VIDEO_ID1 VIDEO_ID2 --metrics views,estimatedMinutesWatched
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from utils.youtube_auth import get_oauth_service
DEFAULT_METRICS = [
"views",
"estimatedMinutesWatched",
"averageViewDuration",
"impressions",
"impressionClickThroughRate",
"subscribersGained",
"subscribersLost",
"likes",
"shares",
"comments",
]
TRAFFIC_SOURCE_METRICS = [
"views",
"estimatedMinutesWatched",
]
def fetch_video_metrics(service, channel_id, video_ids, start_date, end_date, metrics):
"""Fetch standard metrics for specified videos."""
filters = f"video=={','.join(video_ids)}"
try:
response = service.reports().query(
ids=f"channel=={channel_id}",
startDate=start_date,
endDate=end_date,
metrics=",".join(metrics),
filters=filters,
dimensions="video",
maxResults=200,
).execute()
except Exception as e:
return {"error": str(e)}
rows = response.get("rows", [])
headers = [h["name"] for h in response.get("columnHeaders", [])]
results = []
for row in rows:
entry = dict(zip(headers, row))
results.append(entry)
return results
def fetch_traffic_sources(service, channel_id, video_ids, start_date, end_date):
"""Fetch traffic source breakdown for videos."""
filters = f"video=={','.join(video_ids)}"
try:
response = service.reports().query(
ids=f"channel=={channel_id}",
startDate=start_date,
endDate=end_date,
metrics=",".join(TRAFFIC_SOURCE_METRICS),
filters=filters,
dimensions="insightTrafficSourceType",
maxResults=200,
).execute()
except Exception as e:
return {"error": str(e)}
rows = response.get("rows", [])
headers = [h["name"] for h in response.get("columnHeaders", [])]
return [dict(zip(headers, row)) for row in rows]
def fetch_daily_metrics(service, channel_id, video_ids, start_date, end_date):
"""Fetch daily metric breakdown for trend analysis."""
filters = f"video=={','.join(video_ids)}"
daily_metrics = ["views", "estimatedMinutesWatched", "likes", "subscribersGained"]
try:
response = service.reports().query(
ids=f"channel=={channel_id}",
startDate=start_date,
endDate=end_date,
metrics=",".join(daily_metrics),
filters=filters,
dimensions="day",
sort="day",
maxResults=200,
).execute()
except Exception as e:
return {"error": str(e)}
rows = response.get("rows", [])
headers = [h["name"] for h in response.get("columnHeaders", [])]
return [dict(zip(headers, row)) for row in rows]
def get_channel_id(oauth_creds):
"""Get the authenticated user's channel ID."""
try:
from googleapiclient.discovery import build
yt_service = build("youtube", "v3", credentials=oauth_creds)
response = yt_service.channels().list(part="id", mine=True).execute()
items = response.get("items", [])
if items:
return items[0]["id"]
except Exception:
pass
return None
def main():
parser = argparse.ArgumentParser(description="Fetch YouTube video analytics")
parser.add_argument("video_ids", nargs="+", help="One or more video IDs")
parser.add_argument("--days", type=int, default=28,
help="Number of days to look back (default: 28)")
parser.add_argument("--metrics", type=str, default=None,
help="Comma-separated metrics (default: standard set)")
parser.add_argument("--traffic-sources", action="store_true",
help="Include traffic source breakdown")
parser.add_argument("--daily", action="store_true",
help="Include daily metric breakdown")
args = parser.parse_args()
end_date = datetime.now().strftime("%Y-%m-%d")
start_date = (datetime.now() - timedelta(days=args.days)).strftime("%Y-%m-%d")
metrics = args.metrics.split(",") if args.metrics else DEFAULT_METRICS
# Build OAuth service
service, error = get_oauth_service()
if error:
print(json.dumps(error), file=sys.stderr)
sys.exit(1)
# We need the YouTube Data API service to get channel ID
try:
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
token_path = Path.home() / ".claude" / ".tmp" / "youtube_oauth_token.json"
if token_path.exists():
creds = Credentials.from_authorized_user_file(str(token_path))
yt = build("youtube", "v3", credentials=creds)
ch_resp = yt.channels().list(part="id", mine=True).execute()
channel_id = ch_resp["items"][0]["id"]
else:
print(json.dumps({"error": "OAuth token not found. Run auth first."}),
file=sys.stderr)
sys.exit(1)
except Exception as e:
print(json.dumps({"error": f"Failed to get channel ID: {e}"}),
file=sys.stderr)
sys.exit(1)
# Fetch metrics
result = {
"video_ids": args.video_ids,
"date_range": {"start": start_date, "end": end_date},
"metrics": {},
"fetched_at": datetime.now().isoformat(),
}
video_metrics = fetch_video_metrics(
service, channel_id, args.video_ids, start_date, end_date, metrics
)
result["metrics"]["standard"] = video_metrics
if args.traffic_sources:
traffic = fetch_traffic_sources(
service, channel_id, args.video_ids, start_date, end_date
)
result["metrics"]["traffic_sources"] = traffic
if args.daily:
daily = fetch_daily_metrics(
service, channel_id, args.video_ids, start_date, end_date
)
result["metrics"]["daily"] = daily
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
"""
Search YouTube for videos by topic or from a competitor channel.
Uses YouTube Data API v3 search.list (100 units per call — use sparingly).
For competitor channel videos, prefer using the uploads playlist path via
fetch_channel_data.py which costs only ~16 units instead of 100.
Note: Private metrics (retention, CTR, revenue) are NOT accessible for
competitor channels via any official API. Only public data is returned.
Input: search query or channel ID filter
Output: JSON array of videos with public metrics
Usage:
python execution/search_competitor_videos.py "python tutorial 2025"
python execution/search_competitor_videos.py --channel-id UCxxxx --max-results 20
python execution/search_competitor_videos.py "cooking tips" --order viewCount
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from utils.quota_tracker import check_quota, consume_quota
from utils.youtube_auth import get_data_api_service
TMP_DIR = Path.home() / ".claude" / ".tmp"
def search_videos(service, query, channel_id=None, max_results=10,
order="relevance", published_after=None):
"""Search for videos with optional channel filter."""
search_params = {
"part": "id,snippet",
"q": query,
"type": "video",
"maxResults": min(max_results, 50),
"order": order,
}
if channel_id:
search_params["channelId"] = channel_id
if published_after:
search_params["publishedAfter"] = published_after
response = service.search().list(**search_params).execute()
consume_quota("search.list")
video_ids = []
snippets = {}
for item in response.get("items", []):
vid = item["id"]["videoId"]
video_ids.append(vid)
snippets[vid] = item["snippet"]
if not video_ids:
return []
# Fetch full statistics for found videos (1 unit per batch of 50)
stats_response = service.videos().list(
part="statistics,contentDetails",
id=",".join(video_ids),
).execute()
consume_quota("videos.list", len(video_ids))
stats_map = {}
for v in stats_response.get("items", []):
stats_map[v["id"]] = {
"statistics": v.get("statistics", {}),
"content_details": v.get("contentDetails", {}),
}
# Combine data
results = []
for vid in video_ids:
snippet = snippets.get(vid, {})
data = stats_map.get(vid, {})
stats = data.get("statistics", {})
content = data.get("content_details", {})
views = int(stats.get("viewCount", 0))
likes = int(stats.get("likeCount", 0))
comments = int(stats.get("commentCount", 0))
engagement_rate = 0
if views > 0:
engagement_rate = round((likes + comments) / views * 100, 2)
results.append({
"video_id": vid,
"title": snippet.get("title"),
"channel_title": snippet.get("channelTitle"),
"channel_id": snippet.get("channelId"),
"published_at": snippet.get("publishedAt"),
"description": snippet.get("description", "")[:200],
"duration": content.get("duration"),
"views": views,
"likes": likes,
"comments": comments,
"engagement_rate_pct": engagement_rate,
"thumbnail": snippet.get("thumbnails", {}).get("high", {}).get("url"),
})
return results
def flag_outliers(videos, multiplier=3.0):
"""Flag videos that significantly outperform the channel average."""
if not videos:
return videos
# Group by channel
channels = {}
for v in videos:
ch = v.get("channel_id", "unknown")
channels.setdefault(ch, []).append(v)
for ch_id, ch_videos in channels.items():
if len(ch_videos) < 3:
continue
avg_views = sum(v["views"] for v in ch_videos) / len(ch_videos)
for v in ch_videos:
if avg_views > 0:
ratio = v["views"] / avg_views
v["vs_channel_avg"] = round(ratio, 1)
v["is_outlier"] = ratio >= multiplier
else:
v["vs_channel_avg"] = 0
v["is_outlier"] = False
return videos
def main():
parser = argparse.ArgumentParser(description="Search YouTube competitor videos")
parser.add_argument("query", nargs="?", default="",
help="Search query")
parser.add_argument("--channel-id", type=str,
help="Filter to specific channel")
parser.add_argument("--max-results", type=int, default=10,
help="Max results (default: 10, max: 50)")
parser.add_argument("--order", choices=["relevance", "viewCount", "date", "rating"],
default="relevance", help="Sort order")
parser.add_argument("--days", type=int, default=None,
help="Only videos from last N days")
parser.add_argument("--flag-outliers", action="store_true",
help="Flag videos that outperform channel average by 3x+")
args = parser.parse_args()
if not args.query and not args.channel_id:
parser.error("Provide a search query or --channel-id")
# Quota check — search.list costs 100 units
quota_status = check_quota("search.list")
if not quota_status.get("can_execute", True):
print(json.dumps({
"error": quota_status["error"],
"suggestion": "Use fetch_channel_data.py instead (~16 units) "
"if you just need a channel's recent videos.",
}), file=sys.stderr)
sys.exit(1)
remaining = quota_status.get("remaining", 10000)
if remaining < 200:
print(json.dumps({
"warning": f"Only {remaining} quota units remaining. "
f"This search costs 100+ units. Proceed with caution.",
}), file=sys.stderr)
# Build service
service, error = get_data_api_service()
if error:
print(json.dumps(error), file=sys.stderr)
sys.exit(1)
# Date filter
published_after = None
if args.days:
dt = datetime.now() - timedelta(days=args.days)
published_after = dt.strftime("%Y-%m-%dT00:00:00Z")
# Search
videos = search_videos(
service,
query=args.query,
channel_id=args.channel_id,
max_results=args.max_results,
order=args.order,
published_after=published_after,
)
if args.flag_outliers:
videos = flag_outliers(videos)
# Sort by views descending
videos.sort(key=lambda v: v["views"], reverse=True)
result = {
"query": args.query,
"channel_filter": args.channel_id,
"result_count": len(videos),
"videos": videos,
"fetched_at": datetime.now().isoformat(),
"note": "Only public metrics shown. Retention, CTR, and revenue "
"data are private and not accessible for competitor channels.",
}
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
"""
YouTube Data API v3 quota tracker.
Tracks unit consumption per operation and warns before quota exhaustion.
Daily quota: 10,000 units, resets at midnight Pacific Time.
Quota costs:
search.list = 100 units
videos.list = 1 unit
channels.list = 1 unit
playlistItems.list = 1 unit
commentThreads.list = 1 unit
videos.insert = 100 units
Usage:
python execution/utils/quota_tracker.py --check
python execution/utils/quota_tracker.py --consume search.list
python execution/utils/quota_tracker.py --consume videos.list --count 10
python execution/utils/quota_tracker.py --reset
"""
import argparse
import json
import sys
from datetime import datetime, timezone, timedelta
from pathlib import Path
TMP_DIR = Path.home() / ".claude" / ".tmp"
DAILY_QUOTA = 10000
WARN_THRESHOLD = 0.80 # Warn at 80% consumed
OPERATION_COSTS = {
"search.list": 100,
"videos.list": 1,
"channels.list": 1,
"playlistItems.list": 1,
"commentThreads.list": 1,
"captions.list": 1,
"playlists.list": 1,
"videos.insert": 100,
}
PACIFIC_OFFSET = timedelta(hours=-8) # PST (approximate; doesn't handle DST)
def _pacific_date():
"""Return current date in Pacific Time."""
utc_now = datetime.now(timezone.utc)
pacific_now = utc_now + PACIFIC_OFFSET
return pacific_now.strftime("%Y-%m-%d")
def _quota_file():
"""Return path to today's quota file."""
TMP_DIR.mkdir(parents=True, exist_ok=True)
return TMP_DIR / f"youtube_quota_{_pacific_date()}.json"
def _load_quota():
"""Load today's quota state."""
path = _quota_file()
if path.exists():
with open(path) as f:
return json.load(f)
return {"date": _pacific_date(), "consumed": 0, "operations": {}}
def _save_quota(data):
"""Save quota state."""
path = _quota_file()
with open(path, "w") as f:
json.dump(data, f, indent=2)
def get_remaining():
"""Return remaining quota units for today."""
data = _load_quota()
return DAILY_QUOTA - data["consumed"]
def check_quota(operation=None, count=1):
"""Check if an operation can be performed within quota.
Returns dict with status, remaining units, and warning if applicable.
"""
data = _load_quota()
remaining = DAILY_QUOTA - data["consumed"]
used_pct = data["consumed"] / DAILY_QUOTA
result = {
"date": data["date"],
"consumed": data["consumed"],
"remaining": remaining,
"daily_quota": DAILY_QUOTA,
"used_percent": round(used_pct * 100, 1),
}
if used_pct >= WARN_THRESHOLD:
result["warning"] = (
f"Quota {result['used_percent']}% consumed. "
f"Only {remaining} units remaining. Use sparingly."
)
if operation:
cost = OPERATION_COSTS.get(operation, 0)
total_cost = cost * count
result["operation"] = operation
result["operation_cost"] = total_cost
result["can_execute"] = total_cost <= remaining
if not result["can_execute"]:
result["error"] = (
f"Operation {operation} (×{count}) costs {total_cost} units "
f"but only {remaining} remaining. Defer to tomorrow."
)
return result
def consume_quota(operation, count=1):
"""Record quota consumption for an operation.
Returns dict with updated quota state.
"""
cost = OPERATION_COSTS.get(operation, 0)
if cost == 0:
return {"warning": f"Unknown operation '{operation}'. No quota consumed."}
total_cost = cost * count
data = _load_quota()
remaining = DAILY_QUOTA - data["consumed"]
if total_cost > remaining:
return {
"error": f"Quota exceeded. Need {total_cost} units, only {remaining} available.",
"remaining": remaining,
"consumed": data["consumed"],
}
data["consumed"] += total_cost
op_key = operation
data["operations"][op_key] = data["operations"].get(op_key, 0) + count
_save_quota(data)
new_remaining = DAILY_QUOTA - data["consumed"]
result = {
"operation": operation,
"units_consumed": total_cost,
"remaining": new_remaining,
"used_percent": round(data["consumed"] / DAILY_QUOTA * 100, 1),
}
if data["consumed"] / DAILY_QUOTA >= WARN_THRESHOLD:
result["warning"] = (
f"Quota {result['used_percent']}% consumed. "
f"Only {new_remaining} units remaining."
)
return result
def reset_quota():
"""Force reset today's quota (for testing only)."""
data = {"date": _pacific_date(), "consumed": 0, "operations": {}}
_save_quota(data)
return {"status": "reset", "date": data["date"], "remaining": DAILY_QUOTA}
def main():
parser = argparse.ArgumentParser(description="YouTube API quota tracker")
parser.add_argument("--check", action="store_true",
help="Check current quota status")
parser.add_argument("--consume", type=str,
help="Record quota consumption for an operation")
parser.add_argument("--count", type=int, default=1,
help="Number of operations (default: 1)")
parser.add_argument("--reset", action="store_true",
help="Reset quota counter (testing only)")
parser.add_argument("--can-afford", type=str,
help="Check if operation is affordable")
args = parser.parse_args()
if args.reset:
result = reset_quota()
elif args.consume:
result = consume_quota(args.consume, args.count)
elif args.can_afford:
result = check_quota(args.can_afford, args.count)
elif args.check:
result = check_quota()
else:
parser.print_help()
return
print(json.dumps(result, indent=2))
if "error" in result:
sys.exit(1)
if __name__ == "__main__":
main()
"""
YouTube API authentication manager.
Handles API key auth for public Data API v3 and OAuth 2.0 for Analytics API.
Input: auth_type ('api_key' | 'oauth'), optional credentials path
Output: authenticated googleapiclient service object OR error dict
Usage:
python execution/utils/youtube_auth.py --check api_key
python execution/utils/youtube_auth.py --check oauth
"""
import argparse
import json
import os
import sys
from pathlib import Path
TMP_DIR = Path.home() / ".claude" / ".tmp"
TOKEN_PATH = TMP_DIR / "youtube_oauth_token.json"
CLIENT_SECRETS_PATH = Path.home() / ".claude" / "youtube_client_secrets.json"
def get_api_key():
"""Return YouTube Data API v3 key from environment or config file."""
key = os.environ.get("YOUTUBE_API_KEY")
if key:
return key
config_path = Path.home() / ".claude" / "youtube-credentials.json"
if config_path.exists():
with open(config_path) as f:
data = json.load(f)
key = data.get("api_key")
if key:
return key
return None
def get_data_api_service(api_key=None):
"""Build and return YouTube Data API v3 service object."""
try:
from googleapiclient.discovery import build
except ImportError:
return None, {
"error": "google-api-python-client not installed",
"fix": "pip install google-api-python-client google-auth-oauthlib",
}
if api_key is None:
api_key = get_api_key()
if not api_key:
return None, {
"error": "YouTube API key not found",
"fix": (
"Set YOUTUBE_API_KEY environment variable or add to "
"~/.claude/youtube-credentials.json as {\"api_key\": \"YOUR_KEY\"}. "
"Get a key at https://console.cloud.google.com/apis/credentials"
),
}
try:
service = build("youtube", "v3", developerKey=api_key)
return service, None
except Exception as e:
return None, {"error": f"Failed to build YouTube service: {e}"}
def get_oauth_service(scopes=None):
"""Build YouTube Analytics API service with OAuth 2.0."""
try:
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
except ImportError:
return None, {
"error": "OAuth dependencies not installed",
"fix": "pip install google-api-python-client google-auth-oauthlib",
}
if scopes is None:
scopes = [
"https://www.googleapis.com/auth/yt-analytics.readonly",
"https://www.googleapis.com/auth/youtube.readonly",
]
TMP_DIR.mkdir(parents=True, exist_ok=True)
creds = None
if TOKEN_PATH.exists():
try:
creds = Credentials.from_authorized_user_file(str(TOKEN_PATH), scopes)
except Exception:
creds = None
if creds and creds.expired and creds.refresh_token:
try:
creds.refresh(Request())
with open(TOKEN_PATH, "w") as f:
f.write(creds.to_json())
except Exception:
creds = None
if not creds or not creds.valid:
if not CLIENT_SECRETS_PATH.exists():
return None, {
"error": "OAuth client secrets not found",
"fix": (
f"Download OAuth 2.0 client secrets from Google Cloud Console "
f"and save to {CLIENT_SECRETS_PATH}. "
f"Guide: https://developers.google.com/youtube/analytics/registering_an_application"
),
}
try:
flow = InstalledAppFlow.from_client_secrets_file(
str(CLIENT_SECRETS_PATH), scopes
)
creds = flow.run_local_server(port=0)
with open(TOKEN_PATH, "w") as f:
f.write(creds.to_json())
except Exception as e:
return None, {"error": f"OAuth flow failed: {e}"}
try:
service = build("youtubeAnalytics", "v2", credentials=creds)
return service, None
except Exception as e:
return None, {"error": f"Failed to build Analytics service: {e}"}
def check_auth(auth_type):
"""Check if authentication is configured and return status."""
if auth_type == "api_key":
key = get_api_key()
if key:
masked = key[:4] + "..." + key[-4:]
return {"status": "ok", "type": "api_key", "key": masked}
return {"status": "missing", "type": "api_key",
"fix": "Set YOUTUBE_API_KEY env var or add to ~/.claude/youtube-credentials.json"}
elif auth_type == "oauth":
if TOKEN_PATH.exists():
return {"status": "ok", "type": "oauth", "token_path": str(TOKEN_PATH)}
if CLIENT_SECRETS_PATH.exists():
return {"status": "needs_auth", "type": "oauth",
"message": "Client secrets found but no token yet. Run OAuth flow."}
return {"status": "missing", "type": "oauth",
"fix": f"Download OAuth client secrets to {CLIENT_SECRETS_PATH}"}
return {"status": "error", "message": f"Unknown auth type: {auth_type}"}
def main():
parser = argparse.ArgumentParser(description="YouTube API auth manager")
parser.add_argument("--check", choices=["api_key", "oauth"],
help="Check auth status")
args = parser.parse_args()
if args.check:
result = check_auth(args.check)
print(json.dumps(result, indent=2))
sys.exit(0 if result["status"] == "ok" else 1)
else:
parser.print_help()
if __name__ == "__main__":
main()
YouTube Algorithm Reference Guide
Table of Contents
- Three-System Architecture
- Signal Hierarchy & Satisfaction Metrics
- CTR Benchmarks
- Average View Duration (AVD)
- Context-Adaptive Weighting
- Engagement Signals
- Upload Cadence & Growth
- New Channel Promotion
- Algorithm Timeline 2024-2025
- Key Constraints & Gotchas
---
Three-System Architecture
YouTube operates three distinct recommendation systems, each with its own signal hierarchy:
| System | Primary Surface | Core Signals |
|---|---|---|
| Browse | Home feed, Subscriptions | Personalization, satisfaction, freshness |
| Search | Search results | Query relevance, metadata, engagement |
| Shorts | Shorts feed | Completion rate, loops, freshness |
- Each system ranks and serves content independently
- A video can perform well in one system and poorly in another
[2025]Beaupre (January 2025): YouTube is a "per-viewer prediction engine", satisfaction-weighted
---
Signal Hierarchy & Satisfaction Metrics
4-Layer Testing Cascade
Videos are tested through progressively wider audiences:
1. Core audience -- Subscribers and frequent viewers 2. Expanded -- Similar interest profiles 3. Broader -- Wider demographic/topic match 4. High authority -- Trending/viral distribution
- Advancement to next layer depends on performance relative to predictions at each stage
Satisfaction Signals (Ranked)
| Signal | Weight Notes |
|---|---|
| Shares | Strongest per-action signal |
| Repeat viewing | Indicates high satisfaction |
| Session continuation | Viewer stays on platform |
| Saves | Bookmarking intent |
| Surveys | Direct satisfaction measurement |
| Likes | Standard positive signal |
| Comments | Weighted for time investment |
- Quality Click Ratio: Clicks that convert to satisfied viewing (not just any click)
---
CTR Benchmarks
CTR by Traffic Source [2025]
Source: Focus Digital (single-agency report), December 2025
| Traffic Source | Average CTR |
|---|---|
| Search | 12.5% |
| Suggested | 9.5% |
| Browse | 3.5% |
| External | 2.8% |
CTR Performance Tiers
| CTR Range | Assessment |
|---|---|
| Below 3% | Fix needed |
| 4-6% | Average |
| 7-10% | Good |
| 10%+ | Exceptional |
---
Average View Duration (AVD)
Source: Retention Rabbit, 150M+ minutes analyzed
- Average AVD: 23.7%
- Only 16.8% of videos surpass 50% AVD
- Below 40% AVD = deprioritized by the algorithm
---
Context-Adaptive Weighting
Watch time is weighted differently depending on device and content type:
| Context | Watch Time Weight |
|---|---|
| TV / CTV | Higher |
| Mobile | Lower (relative to TV) |
| Podcasts | Higher |
| Music | Lower (relative to podcasts) |
---
Engagement Signals
- 50+ comments replied within 2 hours = 15-20% higher reach
- Comment replies in the first 2 hours are critical for the testing cascade
---
Upload Cadence & Growth
Source: vidIQ, 5.08M channels analyzed
| Cadence | Growth Impact |
|---|---|
| 12+/month | 8x faster view growth, 3x subscriber growth |
| Consistency > frequency | Regular schedule beats sporadic bursts |
- Max 3 notifications per 24 hours -- uploads beyond this won't trigger subscriber notifications
---
New Channel Promotion
- Channels under 500 subscribers receive active algorithmic promotion
- Hype feature: 3 votes per week per user, weighted more heavily for smaller channels
- Beta results: 5M hypes across 50K channels
---
Algorithm Timeline 2024-2025
| Date | Change |
|---|---|
| Mid-2024 | Thumbnail A/B testing rollout |
| Oct 2024 | [2025] Shorts extended to 3 minutes |
| Jan 2025 | [2025] LLMs integrated into recommendations, satisfaction-weighted |
| Mar 2025 | [2025] Shorts view counting change (any playback = view, loops = additional views). Engaged Views metric introduced |
| Aug 2025 | [2025] Undocumented ~30% viewership drops reported; desktop-to-mobile shift observed |
| Oct 2025 | [2025] Pichai confirms Shorts earn more per watch hour than in-stream ads in US |
| Late 2025 | [2025] Home feed long-form reduced up to 80% (per MrBeast retention director). Shorts consuming feed real estate |
| Sep 2025 | [2025] Shorts freshness prioritization; Shorts older than ~28-30 days deprioritized |
---
Core Mindset: Algorithm = Audience
Source: MrBeast methodology (validated at 100M+ views/video scale)
Whenever you hear "algorithm," replace it with "audience." The algorithm is simply a measurement of human behavior. If people click and watch your content, YouTube will promote it. There is no secret hack -- only understanding what your specific audience wants and delivering it better than anyone else.
Practical implication: Stop optimizing for "the algorithm" and start studying human psychology. Why do people click? Why do they leave? Why do they share? Every decision (thumbnail, title, script, pacing) should answer these questions.
---
Key Constraints & Gotchas
- Browse CTR is naturally low (3.5%) -- do not compare it against Search CTR (12.5%); they are different systems
- AVD below 40% triggers deprioritization -- retention is non-negotiable
- Notification cap of 3/24h means uploading more than 3 times daily wastes notification slots
- Consistency beats frequency -- irregular 12/month is worse than regular 4/month
- Shorts older than ~28-30 days lose freshness boost
[2025]-- plan for front-loaded Shorts performance - Long-form Home feed visibility dropped up to 80%
[2025]-- diversify traffic sources beyond Browse - August 2025 viewership drops were undocumented -- monitor analytics for unexplained dips
- Quality Click Ratio matters more than raw CTR -- clickbait that doesn't convert hurts ranking
- TV/CTV watch time is weighted higher -- optimize for lean-back viewing if targeting living room audiences
- Comment replies are time-sensitive -- the 2-hour window is critical for the 15-20% reach boost
YouTube Analytics Guide
Table of Contents
1. Metrics Hierarchy 2. Vanity Metrics to Ignore 3. Impressions Funnel 4. Traffic Source Health 5. RPM vs CPM 6. CPM Ranges by Niche 7. RPM Ranges by Niche 8. Geographic & Seasonal Factors 9. Channel Health Indicators 10. YouTube Analytics API 11. YouTube Reporting API 12. Key Constraints & Gotchas
---
Metrics Hierarchy
| Priority | Metric | Impact |
|---|---|---|
| 1 | Watch time | Most influential overall signal |
| 2 | CTR | ~80% of initial distribution decisions |
| 3 | AVD (Average View Duration) | 50%+ = 3x more likely to be recommended |
| 4 | Traffic sources | Distribution health indicator |
| 5 | Subscribers gained per video | Long-term channel value signal |
Vanity Metrics to Ignore
- Subscriber count in isolation -- PewDiePie: 110M subs, ~2.2M typical views
- Total views without retention context
- Likes in isolation
Impressions Funnel
| Phase | CTR Range | Meaning |
|---|---|---|
| Launch (warm audience) | 12%+ | Normal for initial push |
| Expansion | Natural drop | Impressions scaling to cold audience |
| Sustained healthy | 4-8% | Stable performance |
Diagnostic Matrix
| Impressions | CTR | Diagnosis |
|---|---|---|
| High | Low | Thumbnail/title problem |
| High | High + Low AVD | Clickbait signal |
| Low | High | Limited reach, content too niche |
Traffic Source Health
| Pattern | Interpretation |
|---|---|
| Balanced Suggested + Search | Healthy channel |
| Suggested growing faster than Search | Strong retention signals |
| External dominant | Over-reliant on off-platform promotion |
| Search dominant + low Suggested | Good SEO but content not engaging enough |
RPM vs CPM
| Metric | Definition |
|---|---|
| CPM | Advertiser pays per 1K ad impressions (before YouTube cut) |
| RPM | Creator earns per 1K total views (after YouTube 45% cut, all revenue sources) |
- RPM is always lower than CPM
- Videos >8 min with mid-roll ads = RPM roughly doubles
CPM Ranges by Niche
[2025] Advertiser cost per 1K ad impressions:
| Niche | CPM Range |
|---|---|
| Finance / Investing | $12-$16 |
| Tech / SaaS | $8-$14 |
| Business / Education | $5-$12 |
| Health | $4-$8 |
| Gaming | $1-$4 |
RPM Ranges by Niche
[2025] Creator earnings per 1K total views (US audience):
Tier 1 (High Value)
| Niche | RPM |
|---|---|
| Personal Finance | $20-$40+ |
| Legal / Real Estate | $20-$35 |
| Business / Entrepreneurship | $15-$30 |
| Tech / Software | $15-$25 |
| Digital Marketing | $12-$25 |
Tier 2 (Mid Value)
| Niche | RPM |
|---|---|
| Health / Fitness | $8-$15 |
| Education | $8-$15 |
| DIY | $8-$14 |
| Food / Cooking | $6-$12 |
| Travel | $6-$12 |
Tier 3 (Lower Value)
| Niche | RPM |
|---|---|
| Beauty | $5-$8 |
| Gaming | $2-$5 (median US $3.50) |
| Entertainment | $2-$5 |
| Lifestyle | $3-$6 |
| Music | $1-$3 |
Geographic & Seasonal Factors
- US viewers 5-8x more valuable than SE Asian viewers
- Finance RPM: USA $15-$25 vs India $0.50-$1.50
- Q4 (Oct-Dec): CPMs 30-60% higher
- January: cheapest CPMs of the year
Channel Health Indicators
Healthy Channel
| Metric | Target |
|---|---|
| CTR | 4-8% |
| Retention | Above 40% (ahead of 83% of channels) |
| Sub growth | Steady increase |
| Traffic sources | Balanced mix |
| Viewer ratio | Good new:returning balance |
Struggling Channel
| Signal | Threshold |
|---|---|
| CTR | Below 3% |
| View duration | Dropped 40%+ |
| Source reliance | 80%+ from single source |
| Schedule | Inconsistent uploads |
- JI Digital 20-point audit: channels scoring 76+ grow subs 3.2x faster than those below 50
YouTube Analytics API
Queryable Metrics
views,engagedViews,estimatedMinutesWatched,averageViewDurationestimatedRevenue,estimatedAdRevenue,grossRevenue,CPMlikes,dislikes,comments,sharessubscribersGained,subscribersLostcardClickRate,playlistViews,playlistStarts
`engagedViews` [2025]: Official metric since April 2025. For long-form, equals views. For Shorts, captures the pre-March 2025 view counting (minimum watch time required). Use engagedViews for accurate Shorts performance comparison across the view counting change.
Available Dimensions
- Time:
day,month - Geography:
country,province,city - Demographics:
ageGroup,gender - Device:
deviceType,operatingSystem - Source:
insightTrafficSourceType - Content:
video,playlist,liveOrOnDemand,subscribedStatus
Limits
- Max 200 results per query
- Groups up to 500 videos
YouTube Reporting API
- Bulk CSV exports
- 24-hour granularity
- 60-day data availability window
channel_reach_basic_a1report: thumbnail impressions and CTR
---
Key Constraints & Gotchas
- CTR naturally drops as impressions expand -- a declining CTR is not always bad
- AVD at 50%+ is the threshold for 3x recommendation likelihood
- RPM is always lower than CPM; never confuse the two in calculations
- Videos under 8 min cannot have mid-roll ads, roughly halving RPM potential
- US viewers are 5-8x more valuable than SE Asian -- audience geography dominates revenue
- Q4 CPM spikes 30-60%; plan high-value content for Oct-Dec
- Analytics API caps at 200 results per query; paginate for large datasets
- Reporting API data only available for 60 days; export regularly
- Subscriber count is a vanity metric without per-video view context
- 80%+ traffic from a single source indicates dangerous channel fragility
DataForSEO Integration Reference
Live YouTube data via the DataForSEO MCP server. When available, youtube skills use these tools for real keyword metrics, YouTube SERP analysis, video intelligence, trend data, and competitive research. Falls back to WebSearch + execution scripts when DataForSEO is unavailable.
Table of Contents
- Detection & Fallback
- Default Parameters
- API Credit Efficiency
- YouTube-Specific Tools
- Keyword Research Tools
- Trends & Seasonality
- Competitor & Domain Analysis
- AI Visibility (GEO)
- Sub-Skill → DataForSEO Module Mapping
- Estimated Cost Per Workflow
- YouTube SERP Research Workflow
- Key Constraints & Gotchas
---
Detection & Fallback
Before using DataForSEO tools, check availability by calling any tool (e.g., serp_youtube_organic_live_advanced). If the call fails or the tool is not found, fall back to WebSearch and execution scripts for that step.
Pattern: 1. Attempt DataForSEO tool call 2. If successful → use live data 3. If unavailable → fall back to WebSearch + execution scripts 4. Never block a workflow because DataForSEO is unavailable
Default Parameters
All DataForSEO tools accept these defaults unless the user specifies otherwise:
location_code: 2840 (United States)language_code: "en"device: "desktop" (SERP tools)limit: 50 (list endpoints)depth: 100 (SERP depth)
API Credit Efficiency
DataForSEO charges per API call. Follow these rules:
- Batch keywords into single calls (volume/difficulty/intent tools accept arrays)
- Don't re-fetch data already retrieved in the same session
- Use `dataforseo_labs_google_keyword_overview` for single-keyword lookups
- Default to limit=50 for list endpoints unless more results are needed
- Warn the user before running expensive operations (large keyword lists, full crawls)
---
YouTube-Specific Tools
YouTube SERP (Search Results)
| Tool | Purpose | Key Parameters | ~Cost |
|---|---|---|---|
serp_youtube_organic_live_advanced | YouTube search results for a keyword | keyword, location_code, language_code, device, depth | ~0.002 |
Response fields per result:
youtube_video: title, url, video_id, thumbnail_url, channel_id, channel_name, channel_url, channel_logo, description, views_count, publication_date, duration_time, duration_time_seconds, is_live, is_shorts, badgesyoutube_channel: channel_id, name, url, logo, video_count, is_verified, descriptionyoutube_playlist: title, url, playlist_id, videos_count, preview_videos
Use cases:
- Keyword competition analysis (how many videos rank, their view counts)
- Content gap discovery (what topics have low competition)
- Competitor video identification for any keyword
- Shorts vs long-form ratio analysis in search results
YouTube Video Intelligence
| Tool | Purpose | Key Parameters | ~Cost |
|---|---|---|---|
serp_youtube_video_info_live_advanced | Full video metadata and metrics | video_id | ~0.002 |
serp_youtube_video_comments_live_advanced | Video comments with engagement | video_id | ~0.002 |
serp_youtube_video_subtitles_live_advanced | Video subtitles/transcript | video_id | ~0.002 |
Video info response fields:
- Core: video_id, title, url, thumbnail_url, description, duration_time, duration_time_seconds, publication_date
- Engagement: views_count, likes_count, comments_count
- Channel: channel_id, channel_name, channel_url, channel_subscribers_count
- Classification: keywords (tags), category, is_live, is_embeddable
- Technical: subtitles (languages available), streaming_quality (resolutions)
Comments response: Comment text, author, likes, reply count, timestamp Subtitles response: Full transcript text with language and auto-generation flags
---
Keyword Research Tools
| Tool | Purpose | Parameters | ~Cost |
|---|---|---|---|
kw_data_google_ads_search_volume | Monthly search volume, CPC, competition, monthly trends | keywords (array), location_code, language_code | ~0.001 |
dataforseo_labs_google_keyword_ideas | Generate keyword ideas from seed | keywords (array), location_code, language_code, limit | ~0.001 |
dataforseo_labs_google_keyword_suggestions | Autocomplete-style suggestions | keywords (array), location_code, language_code, limit | ~0.001 |
dataforseo_labs_google_related_keywords | Semantically related keywords | keywords (array), location_code, language_code, limit | ~0.001 |
dataforseo_labs_bulk_keyword_difficulty | Difficulty scores (0-100) | keywords (array), location_code, language_code | ~0.001 |
dataforseo_labs_search_intent | Intent classification (informational, commercial, transactional, navigational) | keywords (array), location_code, language_code | ~0.0005 |
dataforseo_labs_google_keyword_overview | Quick overview: volume + difficulty + intent | keywords (array), location_code, language_code | ~0.001 |
YouTube context: Google search volume strongly correlates with YouTube search volume. Keywords with high Google volume + informational intent are prime YouTube targets. Commercial/transactional keywords convert better for monetisation but have lower view potential.
Trends & Seasonality
| Tool | Purpose | Parameters | ~Cost |
|---|---|---|---|
kw_data_google_trends_explore | Google Trends time series data | keywords (array, max 5), location_code, date_from, date_to, type | ~0.001 |
content_analysis_phrase_trends | Phrase popularity over time | keyword, date_from, date_to | ~0.002 |
Critical for YouTube: Use kw_data_google_trends_explore to:
- Identify rising topics before they peak (ideal upload timing)
- Compare topic seasonality for calendar planning
- Validate ideation topics against real trend data
- Detect declining topics to avoid (wasted production effort)
Competitor & Domain Analysis
| Tool | Purpose | Parameters | ~Cost |
|---|---|---|---|
dataforseo_labs_google_competitors_domain | Competing domains by keyword overlap | target (domain), location_code, language_code | ~0.002 |
dataforseo_labs_google_ranked_keywords | Keywords a domain ranks for with positions | target (domain), location_code, language_code, limit | ~0.002 |
dataforseo_labs_google_domain_intersection | Shared keywords across 2-20 domains | targets (array), location_code, language_code | ~0.002 |
dataforseo_labs_bulk_traffic_estimation | Estimated organic traffic for domains | targets (array), location_code, language_code | ~0.001 |
content_analysis_search | Find content by topic with quality scores | keyword or url | ~0.002 |
YouTube context: Use these to analyze competitor channel websites and blog presence. Cross-reference with serp_youtube_organic_live_advanced to build a complete picture of competitor keyword coverage across both Google and YouTube.
AI Visibility (GEO)
| Tool | Purpose | Parameters | ~Cost |
|---|---|---|---|
ai_optimization_chat_gpt_scraper | ChatGPT web search results: cited sources | query, location_code, language_code | ~0.01 |
ai_opt_llm_ment_search | LLM mentions of a keyword/brand | keyword, location_code, language_code | ~0.01 |
ai_opt_llm_ment_top_domains | Top domains cited by LLMs | keyword, location_code, language_code | ~0.01 |
YouTube context: YouTube videos appear in AI citations. Use these to check if a creator's videos are being cited by AI platforms and identify gaps.
---
Sub-Skill → DataForSEO Module Mapping
| Sub-Skill | DataForSEO Modules | What It Gets |
|---|---|---|
| ideate | KEYWORDS_DATA, SERP (YouTube), ONPAGE | Keyword ideas, YouTube SERP competition, trends, search volume for idea validation |
| seo | KEYWORDS_DATA, DATAFORSEO_LABS, SERP (YouTube) | Volume, difficulty, intent for title/tag optimisation, YouTube SERP for competition check |
| competitor | SERP (YouTube), DATAFORSEO_LABS | YouTube SERP for keyword gaps, competitor domain analysis, ranked keywords |
| strategy | KEYWORDS_DATA, DATAFORSEO_LABS, SERP (YouTube), ONPAGE | Trends, volume, competition depth, niche viability, content gaps |
| calendar | KEYWORDS_DATA, ONPAGE | Google Trends for seasonal planning, volume for prioritisation |
| analyze | SERP (YouTube), DATAFORSEO_LABS | YouTube SERP position tracking, keyword overview for diagnosed videos |
| audit | SERP (YouTube), DATAFORSEO_LABS, KEYWORDS_DATA | Channel keyword coverage, YouTube SERP positions, competitor benchmarking |
| shorts | SERP (YouTube), KEYWORDS_DATA | Shorts-specific keyword trends, YouTube SERP Shorts ratio |
| hook | SERP (YouTube) | Top-performing video titles/hooks for the keyword from YouTube SERP |
| thumbnail | SERP (YouTube) | Competitor thumbnail analysis via YouTube SERP results |
| metadata | KEYWORDS_DATA, DATAFORSEO_LABS | Real volume/difficulty for tag optimisation, intent for description framing |
Estimated Cost Per Workflow
| Workflow | Typical API Calls | Est. Cost |
|---|---|---|
/youtube ideate <niche> | 5-8 (trends + volume + ideas + YouTube SERP) | ~$0.01 |
/youtube seo <video> | 3-5 (volume + difficulty + intent + YouTube SERP) | ~$0.008 |
/youtube competitor <channels> | 6-10 (YouTube SERP × keywords + domain analysis) | ~$0.02 |
/youtube strategy <niche> | 8-12 (trends + volume + YouTube SERP + competition) | ~$0.03 |
/youtube calendar | 3-5 (trends + volume for scheduling) | ~$0.008 |
/youtube audit <channel> | 8-15 (YouTube SERP + volume + video info × videos) | ~$0.04 |
/youtube analyze <video> | 2-4 (video info + YouTube SERP + keyword overview) | ~$0.008 |
/youtube metadata <video> | 2-3 (volume + difficulty for tags) | ~$0.004 |
YouTube SERP Research Workflow
Standard research pattern for any YouTube sub-skill:
1. Identify target keywords (from user input or ideation)
2. Run `serp_youtube_organic_live_advanced` for each keyword
3. Extract from results:
- Top video view counts → benchmark for niche
- Channel distribution → concentration vs fragmentation
- Shorts vs long-form ratio → format opportunity
- Publication dates → content freshness in niche
- Title patterns → what's working
4. Cross-reference with `kw_data_google_ads_search_volume`
- High Google volume + low YouTube competition = opportunity
5. Validate with `kw_data_google_trends_explore`
- Rising trend = act now, declining = avoid---
Key Constraints & Gotchas
- Google volume ≠ YouTube volume -- DataForSEO provides Google Ads search volume, not YouTube-specific volume. Google volume is a strong proxy but not identical; some keywords are disproportionately searched on YouTube (gaming, tutorials) vs Google
- Batch keywords to save credits -- volume, difficulty, and intent tools all accept arrays. Sending 20 keywords in one call costs the same as sending 1. Never loop single-keyword calls
- Never re-fetch in the same session -- if you already retrieved keyword data or YouTube SERP results for a keyword earlier in the conversation, reuse that data instead of making another API call
- YouTube SERP results may lag -- DataForSEO caches YouTube SERP data; results may be hours or days old. For trending topic analysis, supplement with
kw_data_google_trends_explorefor real-time signals - Video info requires video_id, not URL -- extract the video ID from URLs before calling
serp_youtube_video_info_live_advanced. Format: 11-character alphanumeric string (e.g.,dQw4w9WgXcQ) - Comments tool returns limited depth --
serp_youtube_video_comments_live_advancedreturns top-level comments sorted by relevance, not all comments. Sufficient for audience gap analysis but not exhaustive - Subtitles may be auto-generated -- check the auto-generation flag in
serp_youtube_video_subtitles_live_advancedresponses. Auto-generated transcripts contain errors that affect content analysis accuracy - CPC as RPM proxy is approximate -- higher Google Ads CPC generally correlates with higher YouTube RPM for a topic, but the relationship is not linear. Use as a directional signal, not a precise estimate
- Trends data compares relative interest, not absolute volume --
kw_data_google_trends_explorereturns index values (0-100), not search counts. Always pair with volume data for a complete picture - Cost awareness -- typical YouTube workflow costs $0.002-$0.04. Warn the user before operations exceeding ~$0.05 (large keyword lists, multiple YouTube SERP queries)
YouTube Monetization Guide
Table of Contents
1. YPP Requirements 2. AdSense RPM by Niche 3. Channel Memberships 4. Super Chat / Thanks / Stickers 5. YouTube Shopping Affiliate 6. Brand Deals 7. FTC Disclosure Requirements 8. YouTube Premium Revenue 9. External Revenue Streams 10. Key Constraints & Gotchas
---
YPP Requirements
Expanded Tier (Partial Monetization)
| Requirement | Threshold |
|---|---|
| Subscribers | 500 |
| Public uploads | 3 in last 90 days |
| Watch hours | 3K in 12 months OR |
| Shorts views | 3M in 90 days |
Unlocks: Memberships, Super Chat/Thanks/Stickers, Shopping
Full Tier (Ad Revenue)
| Requirement | Threshold |
|---|---|
| Subscribers | 1,000 |
| Watch hours | 4K in 12 months OR |
| Shorts views | 10M in 90 days |
Unlocks: Ad revenue + Premium revenue
- Review process: ~1 month
- Both tiers require: 2-step verification, no active strikes, linked AdSense
[2025] Policy Change (July 2025)
- "Repetitious content" renamed to "Inauthentic content" policy
- Mass-produced / templated content risks losing monetization
AdSense RPM by Niche
[2025] Creator earnings per 1K total views (US audience):
Tier 1 (High Value)
| Niche | RPM |
|---|---|
| Personal Finance | $20-$40+ |
| Legal / Real Estate | $20-$35 |
| Business / Entrepreneurship | $15-$30 |
| Tech / Software | $15-$25 |
| Digital Marketing | $12-$25 |
Tier 2 (Mid Value)
| Niche | RPM |
|---|---|
| Health / Fitness | $8-$15 |
| Education | $8-$15 |
| DIY | $8-$14 |
| Food / Cooking | $6-$12 |
| Travel | $6-$12 |
Tier 3 (Lower Value)
| Niche | RPM |
|---|---|
| Beauty | $5-$8 |
| Gaming | $2-$5 (median US $3.50) |
| Entertainment | $2-$5 |
| Lifestyle | $3-$6 |
| Music | $1-$3 |
- US viewers 5-8x more valuable than SE Asian
- Finance RPM: USA $15-$25 vs India $0.50-$1.50
Channel Memberships
| Parameter | Value |
|---|---|
| Price range | $0.99-$499.99/month |
| Max tiers | 25 |
| YouTube cut | 30% |
| Eligibility | 1K subs (500 at expanded tier) |
- Even 1% conversion rate = significant revenue
- Start with 2-3 tiers initially
- Case study: one creator earns $217,000/year from memberships alone
- Caution: paywalled content pushed to non-members frustrates viewers
Super Chat / Thanks / Stickers
| Parameter | Value |
|---|---|
| Revenue split | 70/30 (creator/YouTube) |
| Per-message range | $1-$500 |
| User weekly cap | $2K/user/week |
| Eligibility | Expanded tier (500+ subs) |
Earnings Benchmarks
| Channel Size | Typical Monthly |
|---|---|
| Mid-size channels | $50-$500/month |
| Case study (300K subs) | $12,000/month (30-45% of total revenue) |
[2025] New Features
- Jewels & Gifts for vertical live streams
- Super Thanks available on Shorts
YouTube Shopping Affiliate
| Parameter | Value |
|---|---|
| Eligibility | YPP + likely 10K+ subs (conflicting reports: 1K-15K range) |
| Countries | 12 |
| Commission range | 5-20% (median ~15%) |
| Products per video | Up to 60 |
| Attribution window | 30 days |
[2025] Shopping Product Stickers (June 2025)
- 40% more clicks than standard product links
Brand Deals
Rate Benchmarks
| Average Views/Video | Rate per Video |
|---|---|
| 5K-15K | $1K-$1.5K |
| 50K-250K | $1.25K-$6.25K |
- Floor is almost always $1K+
Rate by Niche (per view)
| Niche | Rate per View |
|---|---|
| Gaming | $0.037/view |
| Lifestyle | $0.023/view |
FTC Disclosure Requirements
| Method | Requirement |
|---|---|
| Verbal | First 15-30 seconds of video |
| Visual text | On screen for 10+ seconds |
| Written | First 2 lines of description |
| YouTube toggle | Paid promotion toggle alone is NOT sufficient |
- Penalty: $53,088 per violation
- Both brand AND creator are liable
- AI-generated content requires double disclosure
YouTube Premium Revenue
| Metric | Value |
|---|---|
| Global subscribers | 125M+ |
| Creator share | ~55% |
| Revenue contribution | 15-30% of total for Premium-leaning channels |
| Premium Lite | $7.99/month |
External Revenue Streams
Digital Products
- Courses: $99 x 500 students = $49,500
- Platforms: Teachable, Kajabi, Gumroad
Membership Platforms (External)
| Platform | Fee |
|---|---|
| Patreon | 10% + 2.9% Stripe |
| Ko-fi | Free |
| Substack | 10% |
Affiliate Programs
| Program | Commission |
|---|---|
| Amazon Associates | 1-10% |
| ClickBank / DigiStore24 | 20-75% |
Industry Context
- Creator economy: $127B+
- 28.6% of creators rank YouTube as top income platform
---
Key Constraints & Gotchas
- YPP review takes ~1 month; plan content pipeline around the wait
- July 2025 "Inauthentic content" policy targets mass-produced/templated videos
- YouTube takes 30% of memberships and 30% of Super Chat -- factor into pricing
- Shopping affiliate eligibility threshold is unclear (reported 1K-15K subs)
- FTC penalties are $53,088 per violation -- both creator and brand are liable
- YouTube paid promotion toggle alone does NOT satisfy FTC requirements
- Paywalling membership content and showing it to non-members damages viewer trust
- Shorts RPM is far lower than long-form ($0.01-$0.07 typical, up to $0.30 in finance vs $2-$40+)
- US audience is 5-8x more valuable than SE Asian for ad revenue
- Super Chat has a $2K/user/week hard cap
- Premium Lite pays creators less than full Premium -- watch revenue mix
- AI content requires double disclosure under FTC rules
YouTube Content Repurposing & Strategy Guide
Table of Contents
1. Hub/Hero/Help Model 2. Evergreen vs Trending Mix 3. Playlist Strategy 4. Series Strategy 5. Channel Positioning & Niche 6. Repurposing Workflows 7. Competitor Keyword Gap Analysis 8. Integration Architecture 9. Key Constraints & Gotchas
---
Hub/Hero/Help Model
[2025] Updated content pillar framework:
| Pillar | Frequency | Purpose | Examples |
|---|---|---|---|
| Hero | 1-2x/year | Mass awareness, tentpole | Collabs, launches, events |
| Hub | Regular/episodic | Consistent audience building | Series, podcasts, weekly shows, behind-scenes |
| Help/Hygiene | Always-on | SEO capture, search traffic | Tutorials, how-tos, FAQs |
- Recommended mix: primarily Hub + Help, reserve Hero for 2-3 major moments/year
[2025]Edelman: 59% of consumers want brands to teach them
Evergreen vs Trending Mix
| Content Type | Target Share | Key Characteristics |
|---|---|---|
| Evergreen | 70-80% | Stays Google top 10 for 2+ years |
| Trending | 20-30% | Spikes views, decays fast |
- Updating evergreen content = 106%+ traffic increase
- Educational how-tos = 42.1% retention (highest of any format)
- Shorts freshness: older Shorts drop after ~30 days
- Long-form evergreen is unaffected by freshness decay
Playlist Strategy
| Metric | Impact |
|---|---|
| Watch time increase | 40% vs unoptimized channels |
| Channel growth | 20-30% from playlist SEO |
| Ideal playlist size | 5-10 videos |
- Playlists rank independently in YouTube and Google Search
- Use Series Playlists feature (enforces watch order)
- Start each playlist with the highest-performing video
- Case study: reorganizing from "All My Content" to themed playlists = views per start from 1.2 to 3.8 within a month
Series Strategy
- Optimal cycle: 5-7 video series
[2025]Connected content chains valued by 2025 algorithms- Elements: consistent branding, numbering, cliffhangers, series playlists
Channel Positioning & Niche
- Niche-first, expand later -- broad topics dead in 2025
- Micro-niche example: "cosmetics for everyone" narrowed to "skincare for rosacea" = audience 5x smaller but revenue increased
Phased Expansion
| Phase | Timeline | Focus |
|---|---|---|
| Authority | Months 1-6 | Core niche dominance |
| Expansion | Months 7-12 | Adjacent topics |
| Diversification | Year 2+ | Broader audience |
High-CPM Niches
| Niche | CPM Range |
|---|---|
| Finance | $12+ |
| Business / SaaS | $8-$14 |
| Education | $5-$12 |
| Tech | $4-$8 |
Repurposing Workflows
YouTube Long-Form to Shorts
- Extract self-contained "aha" moments
- "Hook-value bomb-CTA" structure = 74% better than continuous clips
- One 20-30 min video = 10-20 Shorts
- AI clipping tools reduce time 70-90%
- No TikTok watermarks (algorithm suppresses them)
YouTube to Blog
- Embed video for Google SEO value
- Add Key Moments via schema markup
- SEO synergy between video and written content
YouTube to LinkedIn
- Professional angle reframe
- 150-word hook + 3 key takeaways format
YouTube to X (Twitter) Thread
- Hook tweet + 4-6 insight tweets + CTA tweet
YouTube to Email Newsletter
- Subject line A/B test
- Preview text optimization
- 200-word distillation + video link
YouTube to Podcast
- Works for interview/discussion format only
- Extract to standalone audio file
Community Post (Same-Day)
- Image + caption + CTA
- Publish same day as video for cross-promotion
Universal Rules
- AI tools reduce repurposing time by 70-90%
- No watermarks when cross-posting between platforms
- Each platform gets a native-format adaptation, not a copy-paste
Competitor Keyword Gap Analysis
Workflow
1. Identify 3-5 competitors in your niche 2. Export their top-performing videos 3. Identify outliers (3-10x their average views) 4. Reverse-engineer why outliers performed 5. Check comments for unaddressed questions 6. Use YouTube Analytics "What your audience watches" report
Tools
| Tool | Price | Use Case |
|---|---|---|
| vidIQ | Free-$49/mo | Keyword research, competitor tracking |
| OutlierKit | $9/mo | Outlier video detection |
| TubeBuddy | Free-$49/mo | Keyword explorer, A/B testing |
Integration Architecture
API Access Tiers
| Data Type | API | Auth | Notes |
|---|---|---|---|
| Public data | Data API v3 | API key | 10K units/day quota |
| Search queries | Data API v3 | API key | 100 units per search call |
| Own analytics | Analytics API | OAuth | Channel owner only |
| Bulk export | Reporting API | OAuth | Channel owner only |
| Competitor public data | Data API + Social Blade | API key | No private metrics |
| Keyword trends | DataForSEO Google Trends | API key | type: youtube parameter |
| SERP data | DataForSEO YouTube SERP API | API key | -- |
| Transcripts | MCP servers | Varies | Third-party tools |
Limitations
- No competitor private data accessible via any API
- TubeBuddy and vidIQ have zero public APIs
- Data API quota resets daily at Pacific midnight
---
Key Constraints & Gotchas
- Broad/general channels are dead in 2025 -- niche-first is mandatory
- Shorts older than ~30 days lose algorithmic push; long-form evergreen is unaffected
- Playlists rank independently -- treat them as SEO assets, not just organization
- "Hook-value bomb-CTA" structure for Shorts outperforms raw clips by 74%
- No TikTok watermarks on any cross-posted content
- TubeBuddy and vidIQ have no APIs -- cannot automate their data
- Data API quota is 10K units/day; search costs 100 units per call (max 100 searches/day)
- Competitor private analytics (retention, RPM, CTR) are never accessible
- Updating old evergreen content can yield 106%+ traffic increase -- do not just publish and forget
- Micro-niching can shrink audience 5x but increase revenue -- smaller is often better
- Series playlists enforce watch order; regular playlists do not
Retention & Scripting Guide
Table of Contents
- Hook Framework
- Hook Benchmarks
- Pattern Interrupts
- Energy Patterns
- Optimal Video Length by Format
- Retention Graph Diagnosis
- Algorithmic Promotion Thresholds
- CTA Placement & Conversion
- Key Constraints & Gotchas
---
Hook Framework
Viewer Loss Data
- 55% of viewers lost in the first 60 seconds
- 20% lost in the first 10 seconds
- Videos with a value proposition in the first 15 seconds = 18% higher retention at the 1-minute mark
Hook Structure (First 30 Seconds)
| Timestamp | Purpose | Action |
|---|---|---|
| 0:00-0:05 | Attention grab | Visual/verbal pattern break, bold claim, or unexpected moment |
| 0:05-0:15 | Clarify promise | State exactly what the viewer will get |
| 0:15-0:30 | Stakes/context | Why this matters, what they'll miss |
---
Hook Benchmarks
| Metric | Threshold | Assessment |
|---|---|---|
| Retention at 10-15s | Below 50% | Hook is failing |
| Retention at 30s | 70%+ | Solid |
| Retention at 30s | 80%+ | Exceptional |
- Never open with "Hey guys welcome back" -- causes instant, measurable drop-off
---
Pattern Interrupts
Impact Data
- Pattern interrupt in first 5 seconds = 23% higher retention
- Strategic breaks at drop-off points = 15-22% re-engagement (Wistia)
- Adobe tutorials using pattern interrupts = 43% higher completion
Recommended Frequency
| Format | Interrupt Frequency |
|---|---|
| Pre-recorded | Every 30 seconds |
| Live | Every 2-3 minutes |
| Shorts | Every 2-3 seconds |
Interrupt Types
- Camera angle change
- Sound effects / music shift
- Text pop-ups / lower thirds
- Unexpected facts or stats
- Format shifts (talking head to B-roll, screen share to whiteboard)
---
Energy Patterns
Source: AIR Media-Tech, 5 documented patterns
| Pattern | Description | Best For |
|---|---|---|
| Gradual Slowdown | High energy open, gradually decreasing | Short content, impact pieces |
| Calm-Burst Oscillation | 15-25s calm, then energy burst every 2-3 min | Educational, tutorials |
| Anchor Pattern | Return to core thesis every 2-3 min | Long-form essays, explainers |
| Strategic Pauses | Deliberate silence/slowdown before key points | Storytelling, dramatic content |
| Progressive Energy | High first 3 min, stabilize, mix variety after min 8 | Vlogs, entertainment |
---
Optimal Video Length by Format
| Format | Optimal Length | Notes |
|---|---|---|
| Tutorials | 7-15 min | Step-by-step pacing |
| Entertainment / Vlogs | 8-12 min | Energy management critical |
| Educational | 15-25 min | Anchor pattern recommended |
| Gaming (edited) | 10-20 min | Pattern interrupts essential |
| Product reviews | 8-15 min | Front-load verdict for retention |
| Podcasts | 30-90 min | Calm-Burst Oscillation works well |
| Shorts | 15-30s | Peak completion rate range |
Length vs Performance
- 5-10 min = peak retention at 31.5%
- Shorts account for 75% of views on the platform
- Videos 20+ min capture 57% of total watch time
- 8-minute threshold unlocks mid-roll ads = ~50% revenue increase
---
Retention Graph Diagnosis
| Pattern | Visual Shape | Diagnosis | Fix |
|---|---|---|---|
| Sharp cliff | 20%+ lost in first 15s | Hook failure | Rebuild 0:00-0:15 |
| Steady decline | Gradual downward slope | Normal/expected | Optimize pacing |
| Mid-video valley | Dip at 40-60% mark | Pacing issue | Add pattern interrupt or reorder content |
| Spikes/bumps | Upward blips | Rewatch moments | Create more of these intentionally |
| Suspension bridge | High retention through open loops | Excellent scripting | 68% higher completion |
| Sawtooth | Zigzag from pattern interrupts | Active re-engagement | 43% higher completion |
---
Algorithmic Promotion Thresholds
- Videos outperforming channel average retention by 15%+ receive 2.3x more algorithmic promotion
- A 10 percentage point retention improvement = 25%+ impression increase
- AI narration = 70% lower retention vs human-fronted content
---
CTA Placement & Conversion
Viewer Reach Data
- Only 16% of viewers reach the final 10% of a video
- At ~1 min mark: ~60% of viewers still watching
- At ~4 min mark: ~35% of viewers still watching
Best Practices
| Strategy | Impact |
|---|---|
| Place CTA after first value delivery (1-3 min) | Catches majority of viewers |
| Embedded CTAs (visual + verbal) | 380% conversion increase over verbal-only |
| With CTA: 1 sub per 33 views | 2.5x better than without CTA (1 per 83 views) |
| "Join the family" vs "subscribe" | 150% growth boost |
Dual CTA Strategy
- First CTA at ~1 min = reaches ~60% of viewers
- Second CTA at ~4 min = reaches ~35% of viewers
- Never save the only CTA for the end (only 16% see it)
---
MrBeast Principles (Validated at Scale)
Source: MrBeast interviews (100M+ views per video average)
The 100-Video Rule
Your first videos will be terrible. Commit to making 100 videos and improving one specific thing each time -- script, editing, camera presence, pacing. Micro-improvements compound exponentially. Do not expect results before video ~50.
Deliver on the Promise Immediately
The title and thumbnail set a promise. In the first 5-10 seconds, instantly assure the viewer you are delivering on that promise. No vlog intros, no "hey guys", no talking about your day. Deliver what they clicked for, then promise even more to exceed expectations.
Ruthless Pacing & Final Payoff
- Remove every dull moment. Have critical friends roast your video to find dead spots.
- Use different camera angles and fast cuts to maintain visual stimulation.
- Ensure a strong payoff at the end (reveal, winner, result) so viewers have a
compelling reason to stay until the last second -- this directly boosts AVD.
- The algorithm replaces "algorithm" with "audience" -- if people click and watch, YouTube promotes it.
Auto-Play Era (2025+)
Videos now auto-play on the YouTube homepage. The first 5 seconds serve as an extension of your thumbnail -- you must visually convince people to stay while the video is already playing. This makes the 0:00-0:05 hook more critical than ever.
Outlier Analysis Method
Don't rely on guesswork. Look for outlier videos from smaller channels that suddenly get 10x-1000x their normal views. Analyse what made that specific video work. Study A/B thumbnail tests from successful creators in your niche.
---
Key Constraints & Gotchas
- 55% of viewers leave in the first 60 seconds -- the hook is the single highest-leverage optimization
- "Hey guys welcome back" is a measurable retention killer -- avoid channel-first openings entirely
- AI narration drops retention by 70% -- always prefer human-fronted content unless the channel is explicitly AI-themed
- Only 16% reach the final 10% of a video -- any CTA placed only at the end is seen by a fraction of viewers
- 8-minute mark unlocks mid-roll ads but do not pad content to reach it -- retention loss from padding costs more than mid-roll revenue gains
- Pattern interrupt frequency differs by format -- every 30s for pre-recorded is aggressive but data-supported; live content needs longer intervals (2-3 min)
- 15%+ above channel average retention is the threshold for algorithmic boost -- optimize your best-performing content types, not underperformers
- Shorts optimal length is 15-30s for completion rate, even though 3-minute Shorts are now allowed
[2025] - The suspension bridge pattern (open loops) delivers the highest completion rate improvement (68%) -- prioritize open-loop scripting over other techniques
YouTube SEO Playbook
Table of Contents
- Title Optimization
- Description Structure
- Tags
- Chapters & Timestamps
- Hashtags
- Captions & Transcription
- Cards & End Screens
- VideoObject Schema Markup
- YouTube Search vs Google Search
- Keyword Research Tools
- Key Constraints & Gotchas
---
Title Optimization
| Property | Value |
|---|---|
| Hard character limit | 100 characters |
| Desktop truncation | ~60-70 characters |
| Mobile truncation | ~40-50 characters |
| Front-load keyword | First 40-50 characters |
Performance Data
- 70-100 character titles outperform shorter titles by 10-14% (10xCreator, 3M+ videos analyzed)
- Numbers in titles = +20-30% CTR
- Brackets add useful context (e.g.,
[2025 Update],[Step-by-Step])
Clickbait Penalty [2025]
- Official rollout: late 2024, India first, then global
- Gemini AI compares actual video content against title/thumbnail metadata
- Misleading titles are actively suppressed
---
Description Structure
| Property | Value |
|---|---|
| Max length | 5,000 characters |
| Visible before "Show more" | First 150-200 characters |
| Primary keyword placement | First 25 words |
Recommended Structure
| Section | Content |
|---|---|
| Lines 1-3 | Primary keyword + hook (visible in preview) |
| Next block | Timestamps/chapters |
| Body | 200-350 words, keyword appears 2-4x |
| Links | Relevant resources, social |
| CTA | Subscribe, comment prompt |
| Closing | 3 hashtags |
[2025]29.5% of Google AI Overviews cite YouTube (BrightEdge, mid-2025)
---
Tags
- Status: Vestigial -- minimal ranking impact
- Limit: 500 characters, spend max 30 seconds
[2025]Gemini analyzes actual video content now, not tags
Only Use Tags For
- Common misspellings of your topic
- Disambiguation (when your topic has multiple meanings)
- Brand-new channels with no content history
---
Chapters & Timestamps
| Requirement | Value |
|---|---|
| Must start at | 0:00 |
| Minimum chapters | 3 |
| Minimum chapter length | 10 seconds each |
Impact
- Enables Google Key Moments in search results
- 25%+ of Google search results include video snippets
- 4% higher AVD (Backlinko)
- Retention up to 50% higher with timestamps (HubSpot)
- Use keyword-rich chapter labels
---
Hashtags
| Context | Optimal Count | Max Before Ignored |
|---|---|---|
| Regular videos | 3-5 | 15 (exceeding = ALL ignored) |
| Shorts | 1-5 | 60 total characters |
- First 3 hashtags appear above the video title
- Exceeding 15 hashtags causes YouTube to ignore ALL of them
---
Captions & Transcription
- 12% longer watch time with captions enabled
- 80% of caption users are not deaf (accessibility + comprehension)
- Manual SRT uploads rank better for indexing than auto-captions
- Say target keywords naturally on camera -- YouTube transcribes audio as SEO signals
---
Cards & End Screens
- Cards and end screens do not directly affect ranking
- End screen CTR benchmarks:
- 2%+ = healthy
- 4%+ = strong
- Optimal placement: last 15-20 seconds of video
- End screens can double post-viewing engagement
---
VideoObject Schema Markup
Required Properties
| Property | Description |
|---|---|
name | Video title |
thumbnailUrl | Thumbnail image URL |
uploadDate | ISO 8601 date |
Performance
[2025]30% higher CTR with VideoObject schema (Backlinko 2025)- Use JSON-LD format
- Add Clip and SeekToAction markup for chapter-level rich results
- Since late 2023: video must be main content of the page for rich snippets (not sidebar embeds)
---
YouTube Search vs Google Search
| Factor | YouTube Search | Google Search |
|---|---|---|
| Primary ranking signal | Retention & satisfaction | Information delivery |
| Content priority | Watch time, engagement | Relevance, authority |
Video-Trigger Keywords (Appear in Google Video Results)
- "how to", "tutorial", "review", "vs", "explained"
Discovery Tool
- Use Google Trends with YouTube Search filter for keyword demand validation
---
Keyword Research Tools
| Tool | Best For | Price |
|---|---|---|
| vidIQ | Research quality, keyword scoring | $17.50/mo |
| TubeBuddy | A/B testing integration | $2.25/mo |
| YouTube Studio Research | Own channel analytics | Free |
| Google Trends (YouTube filter) | Trend validation | Free |
- Neither vidIQ nor TubeBuddy has a public API
[2025]Jan 2026 Search Filter Update for long-tail keywords (unconfirmed -- mentioned in community reports, not officially documented)
---
Key Constraints & Gotchas
- Mobile truncates titles at ~40-50 chars -- if the keyword or hook isn't in that range, mobile viewers miss it
- Exceeding 15 hashtags causes ALL hashtags to be ignored -- not just the extras
- Tags are effectively dead -- spending more than 30 seconds on them is wasted effort. Gemini reads video content directly
[2025] - Description keyword stuffing hurts -- 2-4x keyword density in 200-350 words is the ceiling
- First 150-200 chars of description are all viewers see before clicking "Show more" -- treat them as ad copy
- VideoObject schema only works when the video is the main page content (since late 2023)
- Clickbait penalty is AI-enforced
[2025]-- Gemini compares metadata to actual content. No manual review needed to trigger suppression - Auto-captions are inferior to manual SRT for SEO indexing
- Chapters require 0:00 start -- omitting it disables the entire chapter system
- Google AI Overviews increasingly cite YouTube (29.5%, BrightEdge mid-2025) -- optimize descriptions for Google, not just YouTube search
YouTube Shorts Playbook
Table of Contents
1. Algorithm: Explore & Exploit 2. Signal Hierarchy 3. Format & Specs 4. SEO & Discovery 5. Impact on Long-Form 6. Monetisation 7. View Counting (March 2025) 8. Repurposing to/from Shorts 9. Key Analytics 10. Key Constraints & Gotchas
---
Algorithm: Explore & Exploit
- Explore phase: seed audience test with small cohort
- Exploit phase: triggered if early engagement is strong (past first 5 seconds)
- Scales progressively once in exploit phase
- A Short can take off as late as day 4 after upload
- CTR plays almost no role -- swipe-away rate is the primary surface signal
Signal Hierarchy
| Priority | Signal | Benchmark |
|---|---|---|
| 1 | Completion / retention | 70%+ = aggressive promotion |
| 2 | Loop / replay rate | Heavily weighted by algorithm |
| 3 | Comments and shares | More weight than likes |
| 4 | Viewed vs Swiped Away | 75%+ good; below 50% = broken hook |
| 5 | Satisfaction signals | Surveys, long-press interest |
Format & Specs
| Parameter | Value |
|---|---|
| Max length | 3 min (Oct 2024) |
| Sweet spot | 15-60 seconds |
| Bimodal peaks | 13s or 60s (35B views study) |
| Aspect ratio | 9:16 |
| Resolution | 1080x1920 |
| Captions | Essential |
| Visual change cadence | Every 3 seconds |
| Hook window | First 1-3 seconds |
Length by Content Type
| Type | Ideal Length | Expected Completion |
|---|---|---|
| Quick tips | 15-20s | 85-95% completion |
| Tutorials | 25-40s | -- |
| Loopable content | Any (favored by algorithm) | >100% avg viewed = loops |
- Content ID music blocks 1-3 min Shorts worldwide
SEO & Discovery
- ~40 characters visible before title truncation
- Optimal title: 4-6 words, 20-40 characters
- Use 3-5 hashtags
- First 125 characters of description shown in previews
- Thumbnails irrelevant in feed (Shorts auto-play)
- Shorts appear in YouTube Search AND Google mobile results
Impact on Long-Form
| Metric | Value |
|---|---|
| Audience overlap with long-form | ~10% |
| "Related video" linking conversion | <1% |
| Growth boost (channels using both) | 40-60% faster |
- MacDannyGun case study: 670K subs through Shorts, resulting in 10x long-form views
[2025]Tubefilter/Digital i (Feb 2025): no cannibalization in US market- Caveat: home feed reducing long-form real estate
Monetisation
Revenue Flow
1. Revenue pooling across all Shorts 2. Music licensing allocation deducted 3. Distributed by engaged view share 4. Creator keeps 45%
Music Track Impact on Revenue
| Tracks Used | Creator Revenue Share |
|---|---|
| No music | 100% of pool allocation |
| 1 track | 50% |
| 2 tracks | 33% |
RPM Ranges
| Niche | RPM per 1K Views |
|---|---|
| Finance | $0.05-$0.30 |
| Comedy / Lifestyle | $0.01-$0.05 |
| Typical range | $0.01-$0.07 (finance: up to $0.30) |
- 1M views ~ $10-$70 (finance: up to $300)
[2025]vidIQ: 150% RPM increase in 2025[2025]Shorts = 22% of YouTube total ad revenue (up from 15% in 2024)
View Counting (March 2025)
[2025]Any playback = 1 view (changed March 2025)- Loops count as additional views
- Engaged Views = separate metric (retained attention)
- Total views inflated ~30% compared to pre-March counts
Repurposing to/from Shorts
- Extract self-contained "aha" moments from long-form
- "Hook-value bomb-CTA" structure performs 74% better than continuous clips
- One 20-30 min video = 10-20 Shorts
- AI clipping tools reduce production time 70-90%
- No TikTok watermarks (suppressed by algorithm)
- Use Shorts to test hooks, then turn best performers into long-form
Key Analytics
| Metric | What It Tells You |
|---|---|
| Viewed vs Swiped Away | Replaces CTR for Shorts |
| Average % Viewed | >100% = loops (viral quality) |
| Shown in Feed | True algorithmic reach |
The 60% Rule
- If Viewed vs Swiped Away is under 60%, test a new opening immediately
[2025] Freshness Prioritization (September 2025)
- Algorithm deprioritizes Shorts older than ~28-30 days
---
Key Constraints & Gotchas
- CTR is irrelevant for Shorts -- focus on swipe-away rate instead
- Content ID music blocks kill 1-3 min Shorts globally
- No TikTok watermarks -- algorithm suppresses them
- March 2025 view counting change inflates totals ~30%; use Engaged Views for accuracy
- September 2025 freshness decay: Shorts lose algorithmic push after ~28-30 days
- Revenue is pooled, not per-video -- music tracks directly reduce your share
- 10% audience overlap means Shorts and long-form serve mostly different audiences
- Hook must land in 1-3 seconds; visual changes every 3 seconds minimum
- Title truncation at ~40 chars -- front-load keywords
- Completion rate is king: 70%+ triggers aggressive promotion
Thumbnail & CTR Guide
Table of Contents
- CTR Benchmarks by Niche
- CTR Lifecycle
- Face Thumbnails
- Visual Design Rules
- Thumbnail Specifications
- A/B Testing
- Information Split Rule
- Mobile Optimization
- Suppression Triggers & Strikes
- High-CTR Title Formulas
- Key Constraints & Gotchas
---
CTR Benchmarks by Niche
Source: Focus Digital, December 2025 [2025]
| Niche | Average CTR |
|---|---|
| Gaming | 8.5% |
| Health & Fitness | 8.0% |
| Tech & Reviews | 7.5% |
| Beauty & Fashion | 6.5% |
| Entertainment | 6.0% |
| Finance & Business | 5.5% |
| Education | 4.5% |
---
CTR Lifecycle
- Highest CTR occurs in the first 24 hours (warm/subscribed audience)
- Sustained 4-8% CTR across expanding audience reach = healthy performance
- CTR naturally drops as the video reaches colder audiences -- this is expected behavior
---
Face Thumbnails
- Faces in thumbnails = +20-30% CTR (vidIQ)
- Face thumbnails average 921,000 more views per video (AmpiFire)
- Strong emotion required -- neutral expressions underperform
- Exaggerated but authentic expressions work best
---
Visual Design Rules
| Rule | Specification |
|---|---|
| Text overlay | 5 words max (ideally 3) |
| Color palette | 2-3 primary colors for brand consistency |
| Viewer decision time | Less than 1 second |
| Custom thumbnail usage | 90% of top-performing videos (YouTube Creator Academy) |
| Negative space | 30-40% of thumbnail area |
| Focal points | One clear focal point |
---
Thumbnail Specifications
| Property | Value |
|---|---|
| Minimum resolution | 1280x720 |
| Recommended resolution | 1920x1080 (optimized for TV) |
| Aspect ratio | 16:9 |
| File formats | JPG, PNG, GIF, BMP |
| Max file size | 2MB |
---
A/B Testing
Available through YouTube Studio (rolled out mid-2024)
| Feature | Detail |
|---|---|
| Max variants | Up to 3 thumbnails |
| Optimization metric | Watch time share (not just CTR) |
| Test duration | Up to 2 weeks |
| Result categories | Winner / Same / Inconclusive |
| Platform | Desktop-only setup |
| Restrictions | Not available for Shorts or kids content |
Impact Data
- CTR improvements of 37% to 110% in documented case studies
- A 0.5% CTR difference is significant when measured over millions of impressions
[2025]Title A/B testing added late 2024
---
Information Split Rule
- Thumbnail = visual and emotional hook
- Title = keyword + promise
- Never duplicate text between thumbnail and title
- Each element should contribute unique information that together create a compelling click proposition
---
Mobile Optimization
- 70%+ of views come from mobile devices
- Design thumbnails at 1280x720 minimum (1920x1080 for TV viewing)
- Always test at mobile feed size -- details invisible at small scale are wasted
- Front-load title hook in first 45 characters (mobile truncation)
---
Suppression Triggers & Strikes
Thumbnail Violations That Trigger Suppression
- Nudity / sexual content
- Violence / graphic imagery
- Misleading content (clickbait penalty)
- Hate speech
- Child safety violations
- Vulgar text
Strike System
| Event | Consequence |
|---|---|
| First violation | Warning (expires after 90 days) |
| Same policy violation within 90 days | Strike |
| 3 strikes | Channel termination |
| Pornographic thumbnails | Immediate termination (no warnings) |
[2025]2024-2025: Active crackdown on clickbait thumbnails for breaking news content
---
High-CTR Title Formulas
| Formula | Example |
|---|---|
| Dense Value + Time Compression | "30 Years of Investing in 30 Minutes" |
| System / Framework | "The Blueprint for Building a $1M Channel" |
| Searchable + Clickable Hybrid | "[Keyword]: [Benefit/Curiosity Hook]" |
| Contradiction + Promise | "Why Working Less Made Me More Productive" |
| How I + Specific Result + Timeframe | "How I Got 100K Subscribers in 6 Months" |
| Bracketed Context | "Complete Guide to YouTube SEO [2025 Update]" |
Title Strategy by Traffic Source
| Traffic Source | Title Style |
|---|---|
| Search traffic | Keyword-heavy, direct |
| Browse traffic | Curiosity-driven, emotional |
---
MrBeast Thumbnail Principles
Source: MrBeast methodology (validated at 100M+ views/video scale)
- Titles under 50 characters -- longer titles get cut off with "..." on mobile, killing curiosity
- Spark intense curiosity -- the thumbnail should be so interesting a viewer would "lie awake wondering what happened" if they didn't click
- Auto-play adaptation -- because videos auto-play on the homepage (2025+), the first 5 seconds of video are now an extension of the thumbnail. Design thumbnails that create a visual question the video's opening answers.
- Outlier-driven A/B testing -- study thumbnail A/B tests from successful creators in your niche to see exactly what visual layouts and concepts drive clicks. Data beats intuition.
---
Key Constraints & Gotchas
- Viewers decide in under 1 second -- if the thumbnail doesn't communicate instantly, it fails regardless of quality
- Never duplicate information between thumbnail text and title -- this wastes one of your two persuasion surfaces
- A/B testing optimizes for watch time share, not CTR -- a lower-CTR thumbnail can win if it attracts viewers who watch longer
- Desktop-only A/B setup -- you cannot A/B test thumbnails for Shorts or kids content
- 0.5% CTR difference matters at scale -- do not dismiss small A/B test margins on high-impression videos
- Exceeding 5 words on thumbnail text reduces readability at mobile sizes where 70%+ of views occur
- Pornographic thumbnails cause immediate channel termination -- no warning, no strike, no appeal
- Clickbait suppression is now AI-enforced
[2025]-- Gemini compares thumbnail/title claims against actual video content - Face thumbnails require strong emotion -- a neutral face performs worse than no face at all
- CTR drops as reach expands -- a declining CTR over days is normal; sustained 4-8% across broader audiences is the real benchmark
- Design for 1920x1080 even though 1280x720 is the minimum -- TV/CTV viewing is growing and low-res thumbnails look poor on large screens
- 30-40% negative space is essential -- cluttered thumbnails lose the focal point at small sizes
Analyze Sub-Skill
This sub-skill interprets raw YouTube channel analytics data — whether from screenshots, CSV exports, or verbally described metrics — and transforms it into a structured diagnosis of channel health with a prioritised action plan. It maps data points to known algorithm signals, identifies funnel leaks, classifies retention patterns, and produces maximum 5 high-impact actions the creator can execute immediately.
Inputs Required
- Channel analytics data (any format): YouTube Studio screenshots, CSV/spreadsheet data, or described metrics (views, CTR, watch time, retention %, traffic sources, subscriber changes, revenue)
- Time period (optional): date range for the data (defaults to last 28 days)
- Channel niche (optional): for benchmarking against niche averages
- Specific concerns (optional): areas the creator wants focused attention on
Reference Files to Load
references/analytics-guide.mdreferences/algorithm-guide.md
DataForSEO Research (When Available)
Enhance analytics interpretation with external data:
1. YouTube SERP check: For the channel's key videos, run serp_youtube_organic_live_advanced with their target keywords to check if they appear in search results. A video with low views but no SERP presence = SEO problem; a video in top 5 SERP but low views = keyword demand problem. 2. Video deep-dive: Use serp_youtube_video_info_live_advanced on specific videos the user wants analysed to get full metadata, engagement metrics, and tags. 3. Keyword validation: If the user provides video topics, run dataforseo_labs_google_keyword_overview to check if those keywords have real search demand. Low-demand keywords explain low search traffic.
Fallback: If DataForSEO is unavailable, rely on user-provided analytics data and WebSearch for contextual benchmarks.
Efficiency: Typical analysis costs ~$0.008 (2-4 API calls).
Parallel Agents
Not required. Execute sequentially.
Step-by-Step Execution
1. Load both reference files. Internalise metric benchmarks, algorithm signals, retention pattern classifications, and traffic source implications. 2. Extract all available metrics from the provided data. Normalise into a consistent format regardless of input type (screenshot, CSV, or verbal). 3. Build the funnel diagnosis: Impressions to CTR to Views to Watch Time. Calculate each conversion step and identify where the biggest drop-off occurs. Cross-reference CTR and watch time benchmarks from the analytics guide. 4. Classify retention graph patterns. If retention data is available, match each video's curve to known types: cliff (hook failure within first 30 seconds), valley (pacing issue at a specific point), bump (rewatch segment), suspension bridge (strong open and close, dip in middle), sawtooth (repeated engagement spikes), steady decline (normal healthy pattern). Provide specific timestamps where issues occur if visible. 5. Analyse traffic source distribution. Calculate percentage from each source (Browse, Search, Suggested, External, Shorts feed, etc.). Flag over-reliance on any single source (above 60%). Map traffic source health to algorithm signals — is the algorithm actively recommending this channel? If DataForSEO is available, cross-reference with serp_youtube_organic_live_advanced to check whether the channel's videos appear in YouTube search results for their target keywords — this explains search traffic share. 6. Identify content performance outliers. Find the best and worst performing videos by views, CTR, and watch time. Extract patterns: what topic, format, length, thumbnail style, title structure, and upload timing correlate with high or low performance. 7. If revenue data is available, perform RPM analysis. Compare channel RPM to niche benchmarks. Identify which video types or topics earn disproportionately higher RPM and why. 8. Synthesise findings into a priority action list of maximum 5 items, ranked by impact-to-effort ratio.
Output Template
## Channel Analytics Report
**Period**: [Date range]
**Data sources**: [Screenshots / CSV / Described metrics]
---
### 1. Funnel Diagnosis
| Stage | Value | Benchmark | Status |
|-------|-------|-----------|--------|
| Impressions | [N] | — | [Trend: up/down/flat] |
| CTR | [X%] | [Niche avg: Y%] | [Above/Below/At benchmark] |
| Views | [N] | — | [Trend] |
| Avg Watch Time | [M:SS] | — | [Trend] |
| Avg % Viewed | [X%] | [Niche avg: Y%] | [Above/Below] |
**Primary funnel leak**: [Stage where biggest drop-off occurs]
**Diagnosis**: [Why this leak is happening — specific, evidence-based]
---
### 2. Retention Patterns
| Video | Pattern Type | Key Moment | Interpretation |
|-------|-------------|------------|----------------|
| "[Title]" | [Cliff/Valley/Bump/etc.] | [Timestamp] | [What happened and why] |
| ... | ... | ... | ... |
**Recurring pattern**: [If multiple videos share the same retention issue, call it out]
**Fix**: [Specific action to address the dominant retention pattern]
---
### 3. Traffic Source Health
| Source | Share | Trend | Health Signal |
|--------|-------|-------|---------------|
| Browse Features | [X%] | [Up/Down/Flat] | [Algorithm actively recommending / Not recommending] |
| YouTube Search | [X%] | ... | ... |
| Suggested Videos | [X%] | ... | ... |
| External | [X%] | ... | ... |
| Shorts Feed | [X%] | ... | ... |
| Other | [X%] | ... | ... |
**Over-reliance warning**: [Flag if any source exceeds 60%]
**Algorithm health**: [Overall assessment — is YouTube pushing this channel?]
---
### 4. Content Performance Outliers
**Top performers**:
| Video | Views | CTR | Watch Time | Why It Worked |
|-------|-------|-----|------------|---------------|
| "[Title]" | [N] | [X%] | [M:SS] | [Specific factors] |
| ... | ... | ... | ... | ... |
**Underperformers**:
| Video | Views | CTR | Watch Time | Why It Underperformed |
|-------|-------|-----|------------|----------------------|
| "[Title]" | [N] | [X%] | [M:SS] | [Specific factors] |
| ... | ... | ... | ... | ... |
**Patterns**: [Common traits of winners vs losers — topic, format, length, thumbnail, timing]
---
### 5. Revenue Analysis
> *(included only if revenue data is provided)*
| Metric | Value | Niche Benchmark |
|--------|-------|-----------------|
| Channel RPM | $[X.XX] | $[Y.YY] |
| Monthly Revenue | $[N] | — |
| Top Earning Video | "[Title]" — $[N] | — |
**High-RPM content types**: [Which formats/topics earn disproportionately more]
**Revenue opportunity**: [Untapped revenue potential based on current traffic]
---
### 6. Priority Action List
| # | Action | Why | How to Measure | Timeline |
|---|--------|-----|----------------|----------|
| 1 | [Highest impact action] | [Evidence from data] | [Specific metric to track] | [This week / This month] |
| 2 | ... | ... | ... | ... |
| 3 | ... | ... | ... | ... |
| 4 | ... | ... | ... | ... |
| 5 | ... | ... | ... | ... |
**Impact-to-effort ranking**: Actions are ordered by expected improvement per hour of effort.Quality Criteria
- Every diagnosis must be tied to specific data points, never generic advice
- Funnel diagnosis identifies exactly one primary leak with evidence
- Retention patterns use the correct classification names from the analytics guide
- Traffic source percentages must sum to approximately 100%
- Over-reliance warnings trigger at 60%+ from a single source
- Content outlier analysis explains WHY, not just WHAT
- Action list is capped at 5 items — no padding with low-impact actions
- Each action includes a measurable outcome and timeline
- Revenue analysis uses realistic niche RPM benchmarks from the monetization guide
- If data is incomplete, explicitly state what is missing and how it limits the analysis