A safe, dry-run-first command-line tool for bulk-managing YouTube video metadata — titles, descriptions, comments, watermarks, and uploads.
Built for creators, channel managers, and agencies who maintain a lot of videos and are tired of editing them one by one in the Studio UI.
Bulk-editing a YouTube channel is risky: one wrong script and dozens of titles or descriptions are overwritten with no undo. ytmeta is designed around that risk.
- Dry-run by default. Every write command previews exactly what it would change and
changes nothing until you add
--execute. - Uploads are private by default, so nothing goes public by accident.
- Deletes require a typed confirmation phrase, even with
--execute. - Your secrets stay local. OAuth credentials and tokens are read from your working directory (or env vars) and are never bundled or committed.
npm install -g ytmetaThen run ytmeta --help to confirm it's available. Requires Node.js 18+.
git clone https://github.com/crazyathlete220-stack/ytmeta.git
cd ytmeta
npm install
npm link # optional: makes `ytmeta` available globally-
In the Google Cloud Console, create a project and enable the YouTube Data API v3.
-
Create an OAuth client ID of type Desktop app and download the JSON.
-
Save it as
client_secret.jsonin the folder you runytmetafrom (or pointYTMETA_CREDENTIALSat it). -
Authenticate:
ytmeta login
Your browser opens, you approve access, and a
token.jsonis saved locally.
client_secret.jsonandtoken.jsonare already in.gitignore. Never commit them.
Everything is a dry-run unless you pass --execute.
ytmeta titles examples/titles.json # preview
ytmeta titles examples/titles.json --execute # applytitles.json:
[
{ "id": "VIDEO_ID", "title": "New title" }
]Replace a description outright, or add a shared header/footer to many videos at once:
[
{ "id": "VIDEO_ID_1", "description": "Full replacement text" },
{ "id": "VIDEO_ID_2", "append": "\n—\nSubscribe: https://youtube.com/@you" },
{ "id": "VIDEO_ID_3", "prepend": "📣 New playlist: https://example.com\n" }
]ytmeta descriptions examples/descriptions.json --executeytmeta comments examples/comments.json --executeThe API can't pin comments, so ytmeta posts them and prints the video URLs for you to pin in Studio.
ytmeta watermark logo.png --channel UCxxxxxxxxxxxxxxxx --executeytmeta upload ~/Movies/clip.mp4 --title "My video" --tags "a,b,c" # dry-run
ytmeta upload ~/Movies/clip.mp4 --title "My video" --privacy unlisted --executeytmeta delete VIDEO_ID_1 VIDEO_ID_2 # dry-run
ytmeta delete VIDEO_ID_1 VIDEO_ID_2 --execute # prompts for a confirmation phrase| Command | What it does | Write API |
|---|---|---|
login |
OAuth authentication | — |
upload <file> |
Upload a video (private by default) | videos.insert |
titles <file> |
Bulk title update | videos.update |
descriptions <file> |
Bulk description update / append / prepend | videos.update |
comments <file> |
Post comments | commentThreads.insert |
watermark <image> |
Set channel watermark | watermarks.set |
delete <ids...> |
Delete videos | videos.delete |
| Env var | Default | Purpose |
|---|---|---|
YTMETA_CREDENTIALS |
./client_secret.json |
OAuth client secret path |
YTMETA_TOKEN |
./token.json |
saved token path |
YTMETA_PORT |
4796 |
local port for the login callback |
- Subject to the YouTube Data API quota. Bulk writes cost quota per call; ytmeta adds small delays between calls.
- Comment pinning is not available via the API — pin manually in Studio.
- You can only modify videos on channels your authenticated account owns.
MIT