Headless YouTube uploader for Windows. Authenticates via cookie export — no OAuth, no Google Cloud project, no quota approval. Drives the same Studio web upload pipeline a real browser would.
Upload videos to your own YouTube channel from the command line, without going through Google's OAuth flow or applying for a Cloud project. Two small CLIs:
nemo-yt-upload.exe— headless upload (title, description, tags, privacy, made-for-kids). Runs hidden, prints progress to console.nemo-yt-studio.exe— opens YouTube Studio in a real browser window. Used for first-time login (no cookie export needed) and for manual checks / metadata edits after automated uploads.
Both share a persistent WebView2 profile, so once you log in via
nemo-yt-studio (or seed cookies via the Chrome extension), subsequent
uploads run completely unattended.
If you only need to push your own videos to your own channel, the official YouTube Data API forces you to:
- Create a Google Cloud project
- Configure an OAuth consent screen
- Apply for upload-quota approval (rejected for most personal use cases)
- Re-grant OAuth on every session
nemo-youtube skips all four by using your existing browser session — the
same cookies your Chrome already sends. Drive Studio from Python, no API
keys required.
-
Download the latest
nemo-yt-portable_<version>.zipfrom Releases (see DOWNLOADS.md for version history + verification). -
Extract anywhere. You get two self-contained
.exefiles — no companion folders, drag-anywhere-and-run. -
Open a terminal in the extracted folder (or wherever you put the
.exes). -
Verify both binaries work:
nemo-yt-upload.exe --help nemo-yt-studio.exe --help
No Python install needed on the target machine. Windows 10/11 with WebView2 runtime (already shipped with modern Edge / Windows updates).
nemo-yt-studio.exe --profile-label default
A browser window opens at studio.youtube.com. Log in with your Google
account. The WebView2 profile keeps the cookies after you close the
window.
nemo-yt-upload.exe ^
--video "C:\path\to\video.mp4" ^
--title "My headless upload" ^
--description "First line\nSecond line" ^
--tags "music,cover,acoustic" ^
--privacy PRIVATE
--nmx is optional — by default the uploader uses the WebView2 profile
seeded by nemo-yt-studio above.
nemo-yt-upload.exe ^
--video "C:\path\to\video.mp4" ^
--title "My title" ^
--description "..." ^
--tags "tag1,tag2" ^
--privacy PRIVATE ^
--thumbnail "C:\path\to\cover.jpg" ^
--captions "C:\path\to\track.srt" --captions-lang vi ^
--playlist "Auto-dub batch"
--playlist accepts either a playlist ID (PL...) or a title — titles
are looked up and reused if they exist, else a new playlist is created.
Repeat --playlist for multiple. All optional flags can be mixed with
the minimal example above.
If you prefer not to log in via nemo-yt-studio, install Nemo Cookie
Anywhere from the Chrome Web Store, sign in to YouTube in Chrome, click
the extension icon, export. Pass the resulting file via --nmx:
→ https://chromewebstore.google.com/detail/nemo-cookie-anywhere/hnjephdikibgiajfdllmpjaahpjcgmei
- ✅ Headless upload (title, description, tags, privacy, made-for-kids)
- ✅ Multi-line descriptions via
--description-file - ✅ Custom thumbnail upload (
.jpg/.png, ≤ 2 MB) - ✅ Caption track from
.srt(UTF-8, any language code) - ✅ Playlist attach — by ID or by title (smart upsert: reuse if title exists, else create); repeatable for multiple playlists
- ✅ Cookie auth:
.nmxfile OR persistent WebView2 profile (login once vianemo-yt-studio, all subsequent uploads run unattended) - ✅ Hidden 1×1 minimized window for unattended batch jobs
- ✅ Visible Studio session for first-time login + manual edits
- ✅ DevTools mode for debugging (
nemo-yt-studio --debug) - ✅ Optional proxy support for wire inspection (Charles, mitmproxy)
nemo-youtube is CLI-first by design, so it slots cleanly into any
workflow that can shell out — Claude Code, Cursor / Aider, custom
Python scripts, n8n, GitHub Actions, Make, Zapier with a shell node,
etc. A few traits make it especially agent-friendly:
- No OAuth round-trip per run. The WebView2 profile keeps cookies
between invocations. An agent calls
nemo-yt-upload.exeand gets a videoId back — no consent-screen popup to click through. - No Cloud project, no quota approval. The official YouTube Data API requires manual quota review (often rejected for personal use). This tool works on day one.
- Headless by default. Uploads run in a 1×1 minimized window. No UI for the agent to interact with.
- Predictable stdout. Progress is line-buffered; the final block
prints
videoId: …,studio URL: …,metadata updated: …. Trivial to parse with a regex or string.split(). - Exit-code aware.
0= success,2= stale NMX cookies (agent should prompt for re-export), other non-zero = generic failure. - Stateless invocations. Each call uploads one video and exits. No session to keep alive between calls.
- Library API too.
from nemo_youtube import StudioUploaderif you prefer in-process integration over shelling out.
1. Shell-exec from any agent or scripting tool
import subprocess, re
result = subprocess.run([
"nemo-yt-upload.exe",
"--nmx", "C:/cookies.nmx",
"--video", "C:/out.mp4",
"--title", "AI-generated highlight #1",
"--privacy", "PRIVATE",
], capture_output=True, text=True, check=True)
video_id = re.search(r"videoId:\s*(\S+)", result.stdout).group(1)2. As a tool exposed to an LLM
Wrap the subprocess call in an upload_to_youtube(video_path, title, description, tags, privacy) function and register it as a tool /
function-call in your agent framework. The model picks a clip + writes
metadata; your wrapper handles auth, upload, and returns the videoId.
3. In-process Python library (batch jobs)
from nemo_youtube import StudioUploader
def workflow(up):
return [up.upload(v, title=t) for v, t in queue]
results = StudioUploader(nmx_path="C:/cookies.nmx").run(workflow)One WebView2 window opened, every video in queue uploaded inside
that one session — saves the per-call window-spawn overhead when
publishing 10+ clips at a time.
4. n8n / Make / Zapier — no-code workflow tools
Use the Execute Command node (n8n) or its equivalent. Example n8n
node config:
Command: nemo-yt-upload.exe
Arguments: --nmx "C:/cookies.nmx" --video "{{ $json.video_path }}"
--title "{{ $json.title }}" --privacy PRIVATE
Wire it after a "Watch Folder" / Webhook / Google Drive trigger to get
"video lands in folder → uploaded to YouTube" with zero custom code.
Parse videoId: from stdout in a follow-up "Set" node if you need to
chain (Slack notify, DB log, etc.).
- AI pipeline produces a video (text → script → TTS → render) → auto-
upload as
PRIVATEfor human review before publishing - Scheduled batch from a writeup queue (overnight cron, GitHub Actions on a runner)
- "Repurpose long-form into shorts" agent: input one long video, output N clipped shorts, all uploaded
- Multilingual dub pipeline: same source video → N language tracks → upload one variant per language
- Schedule publish (
publishAt) - Localized title/description
- macOS / Linux (Windows only — depends on WebView2)
See the project for roadmap details.
- Cookies expire. When the uploader fails preflight with
sessionInfo.token unavailable, re-export your.nmx(or runnemo-yt-studioto refresh the profile). - Not affiliated with or endorsed by YouTube/Google. This tool operates outside the official API. Cookie-based automation may violate YouTube's Terms of Service in certain contexts; use on your own channels only, at your own risk.
- Personal use intended. No bulk-upload spam, no operating other people's channels.
Built as the upload step of a Vietnamese video-dubbing pipeline. The helper machinery (in-page JS injector + WebSocket bridge) was developed through extensive reverse-engineering of YouTube Studio's bundle. See the DOWNLOADS page for version history.
If this tool saved you time, please consider supporting development: DONATE.
For inquiries beyond what's documented above:
- Telegram: @mobiphong (fastest)
- Email: phongtrannm@gmail.com
Common reasons people reach out:
- Upload to Facebook / TikTok / Instagram — same headless-browser approach can be ported to other platforms (no official API, no OAuth hoops). Currently YouTube-only; ping if you'd commission/sponsor another platform.
- Source-code access — the public repo only ships binaries; access to the underlying source is granted case-by-case
- Custom features / contract work — additions tailored to your workflow (scheduled releases, multi-channel batch, localized metadata, ...)
- Commercial-use license — if you intend to use this in a for-profit / company setting beyond personal use
- Partnership / collaboration — integrations with other tools, co-development, etc.
Bug reports and small feature suggestions are best filed via GitHub Issues on the public repo (faster than email and visible to other users). Private/sensitive topics → Telegram or email.
MIT — free to use, modify, redistribute. The "use on your own channels only" guidance under Limitations & disclaimer is about YouTube's Terms of Service, not about this tool's license.
