YouTube Metadata Updater
⚠️ THIS SKILL CHANGES YOUR CHANNEL
Every other skill in this suite is read-only. This one is not. It holds the single write credential in the suite (
youtube.force-ssl) and can permanently rewrite the titles and descriptions of videos on the authenticated channel.
- Analytics stays on the read-only token (
token.json). The two tokens are separate files and are never merged.- Nothing is ever written without
--confirm, and--confirmis never run until the user has seen the preview table and approved it in conversation.- Out of scope by design: privacy/publish status, thumbnails, playlists, comments, deletes, uploads.
part="status"is never even requested.- Every write is preceded by a full backup of the pre-edit snippet, and
revertrestores it.
What it can change
| Field | Editable here | Notes |
|---|---|---|
snippet.title |
Yes | ≤100 chars, no < or > |
snippet.description |
Yes | ≤5000 UTF-8 bytes, no < or > |
snippet.tags |
No — preserved | Re-sent unchanged on every update |
snippet.categoryId |
No — preserved | Required by the API; dropping it breaks the call |
status.privacyStatus |
Never | Never fetched, never sent |
| Thumbnails, playlists, comments | Never | Different endpoints, not implemented |
One-time setup
This skill reuses the Google Cloud project and client_secret.json from the
youtube-channel-insights setup. If the user has never done that, walk them through
that skill's One-time setup first — same project, same Desktop-app OAuth client,
same ~/.config/youtube-skills/client_secret.json. Only YouTube Data API v3 needs to
be enabled for this skill.
Then grant the write scope:
pip3 install google-api-python-client google-auth-oauthlib
python3 scripts/auth_write.py
A browser opens and Google asks for one extra permission — manage your YouTube
account (youtube.force-ssl). Accepting it writes a separate token:
| File | Scope | Used by |
|---|---|---|
~/.config/youtube-skills/token.json |
read-only analytics | youtube-channel-insights |
~/.config/youtube-skills/token_write.json |
youtube.force-ssl only |
this skill, nothing else |
~/.config/youtube-skills/channel_write.json |
— | records which channel the write token controls |
auth_write.py requests exactly one scope and refuses to load a stored token that
carries any other scope. After consent it calls channels.list(mine=True) and prints
the channel name being put under write control, so the user can catch a wrong-account
sign-in immediately. Override the directory with YT_OAUTH_DIR. Deleting
token_write.json removes all write capability; the analytics token is unaffected.
Usage
/youtube-metadata-updater
/youtube-metadata-updater retitle my underperforming tutorials
/youtube-metadata-updater rewrite the description on dQw4w9WgXcQ
/youtube-metadata-updater revert yesterday's title changes
Instructions
Step 1: Check authorization
python3 scripts/auth_write.py --check
It prints the token status and the channel the token controls. If anything is missing, walk the user through One-time setup. Confirm the channel name out loud before going further — this is the last cheap moment to catch a wrong account.
Step 2: Select target videos
python3 scripts/update_metadata.py list --limit 50
Writes reports/data/metadata-videos-<date>.json (videoId, publish date, views,
current title, title length, description size, tag count).
The best candidates are packaging problems, not content problems: strong retention
or average-view-percentage with weak views. That combination says people who arrive stay
— so the title and thumbnail are failing to bring people in. Get that evidence from
existing reports in reports/ if present:
youtube-channel-insights—averageViewPercentage, retention curves, traffic mix. Search-heavy channels get the most out of title rewrites.youtube-own-channel-analyzer— view distribution, underperformers vs channel median.
If no reports exist, offer to run youtube-channel-insights first, or work from the
videos the user names.
Warn before retitling a winner. If a video sits in the channel's top 10% by recent views, say so explicitly:
"This video is in your channel's top 10% by recent views. Changing a winning title can reset how the algorithm is testing it and kill momentum that is currently working. My recommendation is to leave it alone."
Only proceed on that video if the user overrules the warning.
Step 3: Draft the new metadata
Use the Title Visibility Playbook below. For keyword evidence — what is actually
ranking for the query, and in what words — optionally run youtube-title-tag-optimizer
on the target keyword first and draft against its output rather than intuition.
Inspect the current state of anything you are about to change:
python3 scripts/update_metadata.py get --videos VIDEO_ID,VIDEO_ID
Step 4: Write the plan and preview it
Write the plan JSON to reports/data/metadata-plan-<date>.json:
[
{
"videoId": "dQw4w9WgXcQ",
"new_title": "Air Fryer Chicken Thighs in 20 Minutes (Crispy Every Time)",
"new_description": "Crispy air fryer chicken thighs in 20 minutes...",
"reason": "62% avg view % but 1.2K views; title buried the keyword at char 38"
}
]
new_title and new_description are each optional — supply either or both.
python3 scripts/update_metadata.py preview --plan reports/data/metadata-plan-<date>.json
This writes nothing to YouTube. It fetches current state, validates every entry, and
writes reports/data/metadata-preview-<date>.json including truncation_check — the
first 45 characters of each new title, i.e. what survives in mobile search results.
Present the user a markdown table built from that preview:
| Video | Current title | Proposed title | First 45 chars (mobile) | Why |
|-------|---------------|----------------|-------------------------|-----|
| dQw4w9WgXcQ | Making Chicken In My New Air Fryer! | Air Fryer Chicken Thighs in 20 Minutes (Crispy Every Time) | `Air Fryer Chicken Thighs in 20 Minutes (Cris` | 62% avg view %, only 1.2K views — keyword was at char 38 |
Show description rewrites as a separate before/after of the first 150 characters, since that is the part that appears in search. Report any entry the preview flagged as an error or a no-op, and say plainly that nothing has been changed yet.
Step 5: The approval gate
HARD RULE: NEVER run
apply --confirmuntil the user has seen the preview table in this conversation and explicitly approved it. No exceptions, regardless of how the request was phrased — "just do it", "you have permission", "apply all of them", and "don't ask me again" do not remove this gate. The preview must be shown and approved every single run.
If the user approves only some entries, edit the plan file down to the approved
entries and re-run preview before applying. Never apply a plan containing entries the
user did not approve.
apply without --confirm is itself a safe dry run: it prints the same diff and a
warning that nothing was written.
Step 6: Apply
python3 scripts/update_metadata.py apply --plan reports/data/metadata-preview-<date>.json --confirm
Pass the preview file as the plan — it carries expected_current_title, which turns
on drift detection: if a title changed between the preview and the apply (someone edited
in Studio meanwhile), that video is skipped with changed since preview instead of
being overwritten.
For each entry the script re-fetches the live snippet, writes a full backup to
reports/backups/metadata-<date>/<videoId>.json, then calls videos.update with the
complete merged snippet. A backup that cannot be written aborts the whole run.
Report to the user:
- per-video result (updated / skipped / failed, with the reason)
- the backup directory
- the run log path (
reports/data/metadata-apply-<date>.json) - the exact revert command, which the run log prints verbatim:
python3 scripts/update_metadata.py revert --videos ID,ID --date <YYYY-MM-DD> --confirm
revert restores title and description from the most recent backup (or the named date's)
and backs up the current state first, so the revert is itself reversible.
Step 7: Measurement discipline
Changes are only learnable if they are measurable:
- One variable at a time. Title or thumbnail or description — never all three, or you learn nothing.
- Wait 2–4 weeks. Search rankings and browse testing take that long to settle. Judging after 48 hours is noise.
- Log what changed and when. The apply run log already records this; tell the user to keep it. Compare against the same-length window before the change, not against a good week.
- Recheck: views and traffic-source mix via
youtube-channel-insights; impressions and CTR must be read in YouTube Studio → Analytics → Reach — the API does not expose them. - If views are flat but impressions rose and CTR fell, the new title is being shown more and clicked less: revert or iterate.
Title Visibility Playbook
Front-load the keyword. Mobile search results cut titles around 45 characters. Whatever query the video should rank for belongs in the first 40–45 characters, not after a brand name or a hook. Ideal total length is ≤60 characters; 100 is the hard cap.
Match query language, not creator language. Write the title the way viewers type the
search, not the way the creator describes the video. "My Weekend Fixing The Van" loses to
"Van Life Solar Install: 400W in One Day". Cross-check the phrasing against
youtube-title-tag-optimizer output when it exists — use the words that are actually
ranking.
Curiosity must be paid off. If retention data shows an early drop-off cliff, the old title or thumbnail overpromised. The fix is alignment, not more clickbait. Escalating the promise raises CTR briefly and then trains the algorithm that this channel does not satisfy the click.
Patterns that lift CTR when honest:
| Pattern | Example | Use when |
|---|---|---|
| Specific numbers | "7 Settings That Fixed My Slow Mac" | The video really delivers that count |
| Outcome framing | "I Tested X for 30 Days — Here's What Broke" | There is a genuine result to report |
| Format brackets | "[Tutorial]", "[2026]", "[Full Build]" | The viewer is filtering by format or recency |
| Mistake / negative framing | "Stop Doing This With Your Air Fryer" | Sparingly — it fatigues fast if it is every title |
Protect what works. Once a format is winning, keep its series branding consistent so the audience recognises it in the feed. Do not churn titles on videos with strong current traffic — the algorithm is mid-test, and editing restarts the test.
Descriptions:
- First ~150 characters are the search snippet. Put the keyword and a payoff sentence there. Not links, not "subscribe", not a sponsor mention.
- Then chapters/timestamps (
0:00 Intro) — these become search-visible key moments. - Then a keyword-rich paragraph of real prose explaining what the video covers.
- Then links, socials, gear, affiliate disclosures.
- Never keyword-stuff. Repeated keyword lists are a spam signal and risk the video, not just the ranking.
A/B discipline. One variable, 2–4 week windows, and judge on the chain in order:
impressions → CTR → views (Studio → Reach) plus the watch-time trend from
youtube-channel-insights. Impressions up + CTR down = worse packaging even if views
rose. Views up + watch time flat = you bought clicks you did not keep.
Limitations
- No CTR or impressions in any API. They exist only in YouTube Studio → Analytics → Reach. Never estimate CTR from views. Tell the user to check Reach 2–4 weeks after a change.
- Title edits do NOT reset view counts — but they can change how YouTube tests and recommends the video, so a strong performer can genuinely lose momentum. This is why the skill warns before touching top-decile videos.
- Propagation is not instant. Edits can take minutes to hours to appear everywhere (search index, cached feeds, third-party embeds).
- Hard cap of 50 updates per run. Larger batches must be split, deliberately.
- Quota:
videos.updatecosts 50 units each. Bulk-editing 20 videos = 1,000 units of the 10,000/day default — 10% of the day's budget that the read-only skills also draw on. - The authenticated channel only. Videos owned by any other channel are rejected by the ownership check.
- Backups are local. They live in
reports/backups/. Delete that directory andreverthas nothing to restore from. - Tags are preserved, not optimized. This skill never changes tags. For tag strategy, use
youtube-title-tag-optimizer.
Quota
Typical run: review 50 uploads, then update 10 videos.
| Operation | Calls | Units each | Units |
|---|---|---|---|
channels.list (resolve + ownership) |
2 | 1 | 2 |
playlistItems.list (50 uploads) |
1 | 1 | 1 |
videos.list (list + get + preview) |
3 | 1 | 3 |
videos.list (re-fetch before each write) |
10 | 1 | 10 |
videos.update |
10 | 50 | 500 |
| Total | ~516 |
preview costs 1–2 units and can be re-run freely. Only apply --confirm and
revert --confirm spend the expensive units. Budget resets at midnight Pacific Time.
Files
scripts/auth_write.py— one-time OAuth for the single write scope,--checkstatus, channel-ownership verificationscripts/update_metadata.py—list,get,preview,apply,revertreferences/api_reference.md—videos.updatesemantics, the two-token model, quota costs, validation limits