👉 Get Vimeo Video Downloader: https://serp.ly/vimeo-video-downloader
-
-
Save devinschumacher/8024bc4693d79aef641b2c281e45d6cb to your computer and use it in GitHub Desktop.
Vimeo streams video in two possible ways:
- HLS → uses
.m3u8playlists and.ts/fragmented MP4 segments. - DASH → uses a
playlist.jsonmanifest and.m4ssegments.
- Open DevTools → Network.
- If you filter for
m3u8and see manifests → that video is using HLS. - If you only see
playlist.jsonand.m4ssegment requests → that video is using DASH.
👉 In my case:
- I didn’t see any
.m3u8in the Network tab. - I did see
playlist.jsonand lots of.m4ssegments. - That means the embed is DASH-only.
- ffmpeg can’t parse Vimeo’s JSON directly, but yt-dlp can (it knows how to read the DASH JSON and reassemble the streams).
-
Filter for
configin DevTools. -
You’ll find a request like:
https://player.vimeo.com/video/519981982/config?... -
The video ID here is
519981982. -
Strip the
/config?...part → the stable player URL is:https://player.vimeo.com/video/519981982
This URL doesn’t expire, unlike the signed segment URLs.
Run yt-dlp against the player page with a referer and concurrency:
yt-dlp --referer "https://player.vimeo.com/video/519981982" \
-N 15 -S "codec:avc,res,ext" \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
"https://player.vimeo.com/video/519981982"--referer→ Vimeo requires this header.-N 15→ download 15 fragments in parallel (much faster for long videos).-S "codec:avc,res,ext"→ prefer AVC (MP4) over VP9/WebM.--merge-output-format mp4→ final file will always be MP4.--remux-video mp4→ repackage without re-encoding.--postprocessor-args "ffmpeg:-movflags +faststart"→ optimize MP4 for instant playback.
-
If it fails: signed URLs (
exp=...) expired → reload and grab a fresh/video/<ID>/config. -
Private videos: use your browser cookies:
yt-dlp --cookies-from-browser chrome "https://player.vimeo.com/video/<ID>" -
Maximum speed: install aria2c and run with:
yt-dlp --downloader aria2c \ --downloader-args "aria2c:-x 16 -s 16 -k 1M" \ "https://player.vimeo.com/video/<ID>"
✅ Summary:
- Filter for
configin DevTools to get the video ID. - Build the stable
/video/<ID>URL. - Since no
.m3u8appears, this is DASH (playlist.json+.m4s). - Use yt-dlp with concurrency to fetch and merge into MP4.
brew install aria2That gives you the aria2c binary, which yt-dlp can use as an external downloader.
Then you can run your Vimeo command with aria2c for maximum speed:
yt-dlp --referer "https://player.vimeo.com/video/519981982" \
--downloader aria2c \
--downloader-args "aria2c:-x 16 -s 16 -k 1M" \
-S "codec:avc,res,ext" \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
"https://player.vimeo.com/video/519981982"-x 16→ up to 16 connections per download-s 16→ split into 16 segments-k 1M→ segment size (1 MB)
⚡ This will usually max out your bandwidth on long Vimeo videos.
vs.
👉 Try the Vimeo Video Downloader
- Example: https://vimeo.com/1100807276
- visit page & enter the password
- find your chrome profile number
- construct the correct command
Go to the Vimeo URL & enter the password.
- visit
chrome://profile-internals/ - expand your profile & grab the number
You will need:
https://vimeo.com/{VIDEO_ID}- chrome profile number
- password
Your command syntax:
yt-dlp \
'https://vimeo.com/{VIDEO_ID}' \
--cookies-from-browser "chrome:Profile {NUMBER}" \
--video-password '{PASSWORD}' \
-N 20 \
-S 'codec:avc,res,ext' \
--merge-output-format mp4 \
--remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart"et voila!
it hath downloaded
Vimeo can serve video in a few different ways:
- HLS (
.m3u8playlists +.ts/fragmented MP4 segments) - DASH (
playlist.json+.m4ssegments) - Inline config (
window.playerConfigJSON in the DOM)
Normally, you’d find the video ID by filtering for /config, .m3u8, or playlist.json in DevTools → Network. But:
/config, .m3u8, or playlist.json over the network. Instead, the player bootstraps with inline JSON in the DOM (often under window.playerConfig). In that case, you won’t see a manifest in the Network tab — but yt-dlp can still parse it cleanly if you point it at the /video/<ID> page.
Example player page:
https://player.vimeo.com/video/1097353467
This URL is stable and is what you should use with yt-dlp.
If the video is protected, yt-dlp will error with:
ERROR: This video is protected by a password, use the --video-password option
You must provide the password that unlocks the video on the embed page.
In zsh, watch out for special characters in passwords (like !). Wrap them in single quotes so the shell doesn’t interpret them.
yt-dlp \
--video-password 'XXXXXXXXXXXX' \
--referer 'https://shiatsuapos.com/55-convegno-nazionale-apos' \
-N 20 -S 'codec:avc,res,ext' \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
'https://player.vimeo.com/video/1097353467'--video-password→ unlocks password-protected videos.--referer→ required when the video is embedded on another site.-N 20→ downloads 20 fragments in parallel for speed.-S "codec:avc,res,ext"→ prefers AVC/MP4 over WebM/VP9.--merge-output-format mp4 --remux-video mp4→ ensures clean MP4 output.--postprocessor-args "ffmpeg:-movflags +faststart"→ optimizes MP4 for instant playback.
-
Password wrong → Vimeo won’t serve the manifest, and yt-dlp will stall or error.
-
No manifests in Network → that’s expected for inline JSON embeds. Use the
/video/<ID>URL with yt-dlp. -
Private or login-only videos → add cookies:
yt-dlp --cookies-from-browser chrome 'https://player.vimeo.com/video/<ID>' -
Slow downloads → increase
-Nor installaria2cfor external downloading:brew install aria2 yt-dlp --downloader aria2c --downloader-args "aria2c:-x 16 -s 16 -k 1M" ...
✅ Summary:
- Sometimes Vimeo videos hide manifests (
config,.m3u8,.json) and instead use inlineplayerConfigJSON in the DOM. - You won’t see streams in Network — but yt-dlp handles this automatically if you give it the player page URL.
- For password-protected videos, add
--video-password 'PASSWORD'. - Use concurrency (
-Nor aria2c) for faster downloads.
Here’s a write-up focused on your last question — how to add speed to your yt-dlp command when downloading a password-protected Vimeo video:
By default, yt-dlp downloads HLS/DASH video one fragment at a time. For long Vimeo videos this can feel very slow. You can dramatically accelerate downloads using concurrency or an external downloader.
yt-dlp \
--video-password 'CNVG_55_APOS2025!' \
--referer 'https://shiatsuapos.com/55-convegno-nazionale-apos' \
'https://player.vimeo.com/video/1097413677'That works, but it’s single-threaded.
Use the -N flag (number of parallel fragment fetches):
yt-dlp \
--video-password 'CNVG_55_APOS2025!' \
--referer 'https://shiatsuapos.com/55-convegno-nazionale-apos' \
-N 20 \
'https://player.vimeo.com/video/1097413677'-N 20→ up to 20 fragments at once (safe sweet spot: 8–32).- More concurrency = faster downloads, but too high can cause throttling or errors.
For even better performance, let yt-dlp hand fragments to aria2c, a high-speed segmented downloader.
brew install aria2yt-dlp \
--video-password 'CNVG_55_APOS2025!' \
--referer 'https://shiatsuapos.com/55-convegno-nazionale-apos' \
--downloader aria2c \
--downloader-args "aria2c:-x 16 -s 16 -k 1M" \
'https://player.vimeo.com/video/1097413677'-x 16→ max 16 connections per file.-s 16→ split into 16 segments.-k 1M→ piece size (1 MB chunks).
This usually maxes out your available bandwidth.
If you want to ensure smooth playback (seekable file):
yt-dlp \
--video-password 'CNVG_55_APOS2025!' \
--referer 'https://shiatsuapos.com/55-convegno-nazionale-apos' \
-N 20 \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
'https://player.vimeo.com/video/1097413677'--merge-output-format mp4 --remux-video mp4→ ensures final MP4.--postprocessor-args "ffmpeg:-movflags +faststart"→ moves metadata to front for instant playback.
tags: ['streamlink', 'ffmpeg', 'vimeo']
Vimeo embeds on Skool are streaming via HLS.
There are 2 methods for downloading these.
Streamlinkat the player.vimeo.com URL (w/ffmpeg)yt-dlpat the m3u8 stream
Streamlink already knows that plugin and can grab the master manifest https://player.vimeo.com/video/<id>.
In this situation, the Vimeo URL is either in the DOM or maybe can be constructed just from the video ID.
And u use streamlink
👉 Or just get the Vimeo Video Downloader: https://serp.ly/vimeo-video-downloader
- Get the player.vimeo.com URL from the DOM
- Use
streamlinkto download the media - Use ffmpeg to remux the mp4 (optional)
- BONUS: Chain the commands together
❯ streamlink https://player.vimeo.com/video/1056875977 best -o ~/Desktop/vimeo_video.mp4
[cli][info] Found matching plugin vimeo for URL https://player.vimeo.com/video/1056875977
[stream.hls][warning] Unrecognized language for media playlist: language='en-x-autogen' name='English (auto-generated)'
[cli][info] Available streams: 240p (worst), 360p, 540p, 720p, 1080p (best)
[cli][info] Opening stream: 1080p (hls-multi)
[cli][info] Writing output to
/Users/devin/Desktop/vimeo_video.mp4
[utils.named_pipe][info] Creating pipe streamlinkpipe-5915-1-3021
[utils.named_pipe][info] Creating pipe streamlinkpipe-5915-2-7551
[cli][info] Stream ended
[cli][info] Closing currently open stream...
[download] Written 22.24 MiB to /Users/devin/Desktop/vimeo_video.mp4 (5s @ 4.26 MiB/s)
→ yields a transport stream (MPEG-TS) file.
And at least on a Mac that gives me an MP4 that WORKS but has no "preview" image on the .mp4 file like most videos.
What’s happening is that Streamlink is just writing the transport stream as-is into an MP4 container, but it doesn’t fully rebuild the metadata/index (the “moov atom”) that players rely on to show a thumbnail/preview and allow proper seeking.
It needs to be re-encoded or "remux'd. This can be done with with ffmpeg, which rewrites the container properly, which is why you get the preview.
❯ ffmpeg -i vimeo_video.mp4 output.mp4
ffmpeg version 7.1.1 Copyright (c) 2000-2025 the FFmpeg developers
built with Apple clang version 17.0.0 (clang-1700.0.13.3)
configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/7.1.1_3 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags='-Wl,-ld_classic' --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libharfbuzz --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoolbox --enable-neon
libavutil 59. 39.100 / 59. 39.100
libavcodec 61. 19.101 / 61. 19.101
libavformat 61. 7.100 / 61. 7.100
libavdevice 61. 3.100 / 61. 3.100
libavfilter 10. 4.100 / 10. 4.100
libswscale 8. 3.100 / 8. 3.100
libswresample 5. 3.100 / 5. 3.100
libpostproc 58. 3.100 / 58. 3.100
Input #0, mpegts, from 'vimeo_video.mp4':
Duration: 00:00:56.45, start: 1.525000, bitrate: 3306 kb/s
Program 1
Metadata:
service_name : Service01
service_provider: FFmpeg
Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(tv, bt709, progressive), 1920x1080, 24 fps, 24 tbr, 90k tbn
Stream #0:1[0x101]: Audio: aac (LC) ([15][0][0][0] / 0x000F), 48000 Hz, mono, fltp, 192 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (aac (native) -> aac (native))
Press [q] to stop, [?] for help
[libx264 @ 0x132005100] using cpu capabilities: ARMv8 NEON
[libx264 @ 0x132005100] profile High, level 4.0, 4:2:0, 8-bit
[libx264 @ 0x132005100] 264 - core 164 r3108 31e19f9 - H.264/MPEG-4 AVC codec - Copyleft 2003-2023 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=24 lookahead_threads=4 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=24 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to 'output.mp4':
Metadata:
encoder : Lavf61.7.100
Stream #0:0: Video: h264 (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080, q=2-31, 24 fps, 12288 tbn
Metadata:
encoder : Lavc61.19.101 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
Stream #0:1: Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, mono, fltp, 69 kb/s
Metadata:
encoder : Lavc61.19.101 aac
[out#0/mp4 @ 0x6000034d8000] video:13954KiB audio:486KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.268337%
frame= 1351 fps=269 q=-1.0 Lsize= 14479KiB time=00:00:56.20 bitrate=2110.2kbits/s speed=11.2x
[libx264 @ 0x132005100] frame I:6 Avg QP:15.36 size: 83066
[libx264 @ 0x132005100] frame P:420 Avg QP:17.98 size: 22378
[libx264 @ 0x132005100] frame B:925 Avg QP:24.00 size: 4747
[libx264 @ 0x132005100] consecutive B-frames: 4.3% 6.5% 20.2% 69.0%
[libx264 @ 0x132005100] mb I I16..4: 25.3% 57.5% 17.2%
[libx264 @ 0x132005100] mb P I16..4: 5.0% 11.5% 0.5% P16..4: 17.6% 7.1% 3.8% 0.0% 0.0% skip:54.5%
[libx264 @ 0x132005100] mb B I16..4: 0.2% 0.4% 0.0% B16..8: 21.5% 2.2% 0.5% direct: 0.5% skip:74.7% L0:50.1% L1:45.7% BI: 4.2%
[libx264 @ 0x132005100] 8x8 transform intra:66.9% inter:67.7%
[libx264 @ 0x132005100] coded y,uvDC,uvAC intra: 28.1% 20.1% 3.2% inter: 4.0% 3.2% 0.0%
[libx264 @ 0x132005100] i16 v,h,dc,p: 47% 20% 22% 11%
[libx264 @ 0x132005100] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 36% 11% 43% 1% 2% 2% 2% 1% 1%
[libx264 @ 0x132005100] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 31% 18% 21% 4% 6% 6% 5% 4% 5%
[libx264 @ 0x132005100] i8c dc,h,v,p: 75% 11% 13% 1%
[libx264 @ 0x132005100] Weighted P-Frames: Y:0.0% UV:0.0%
[libx264 @ 0x132005100] ref P L0: 74.5% 9.0% 12.0% 4.5%
[libx264 @ 0x132005100] ref B L0: 91.8% 7.0% 1.1%
[libx264 @ 0x132005100] ref B L1: 97.3% 2.7%
[libx264 @ 0x132005100] kb/s:2021.68
[aac @ 0x132190030] Qavg: 251.876
→ remux/transcode into a playable MP4.
and it works!
This particular video is public (or playable without cookies); streamlink’s Vimeo plugin can fetch the manifest via Vimeo’s API without your session.
streamlink itself handles the necessary requests under the hood, so you didn’t need to supply headers for this case.
For private or cookie-dependent videos, streamlink does need the headers/cookies (via --http-header or --http-cookie). So while the bare command happened to succeed here,
We’ll still want our automation to capture and forward headers for the locked-down cases.
👉 Get Vimeo Video Downloader: https://serp.ly/vimeo-video-downloader
streamlink -O "URL" best --stream-segment-threads 5 | ffmpeg -i pipe:0 -c copy -movflags +faststart ~/Desktop/vimeo.mp4👉 Try the Vimeo Video Downloader
- Open the page with the Vimeo embed (or
player.vimeo.com/video/...). - Open Chrome DevTools → Network tab.
- Click Media filter (optional but helpful).
- Play the video so the actual streaming requests fire.
-
In the filter box, type
m3u8.-
You’ll see requests like:
.../playlist/av/primary/prot/.../playlist.m3u8?...sf=fmp4
-
-
Optional: search
vttif you want subtitles. -
Optional: search
jsonif you want the DASH manifests (butm3u8is usually simpler).
- Right-click the
.m3u8request → Copy → Copy link address. - If you need headers (for referer/cookies), use Copy → Copy as cURL (bash).
Paste the .m3u8 URL into this command:
yt-dlp --referer "https://player.vimeo.com/" \
-f "bv*+ba/best" \
--merge-output-format mp4 \
--remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
"PASTE_M3U8_URL_HERE"- This grabs the best AVC video + audio, merges them, and saves as MP4.
- Swap in
-f "bv*[height=1080][vcodec*=avc1]+ba/best"if you want a specific quality.
👉 Try the Vimeo Video Downloader
yt-dlp -N 16 \
--cookies-from-browser "chrome:Profile 197" \
--referer "https://vimeo.com/1118190054/173a644b6d" \
-S "codec:avc,res,ext" \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
"https://vimeo.com/1118190054/173a644b6d"Why: the video plays on Vimeo only when logged in, so yt-dlp needed your logged-in cookies and the exact page referer.
- Cause: Unquoted URL with
?in zsh → shell tried to glob. - Fix: Always quote URLs with
?(single or double quotes).
- Cause: Streamlink hit
player.vimeo.com/video/<id>without the right referer/cookies (and the video wasn’t public to unauthenticated clients). - Fix: Streamlink can work if you supply the embedding page referer and cookies, but Vimeo auth flows are more reliable with yt-dlp.
https://player.vimeo.com/video/1118190054/config?...
- Cause:
/configis a JSON bootstrap (player settings), not a media URL. The generic extractor downloaded a tiny JSON and failed to remux it →ERROR: Postprocessing: Invalid data found when processing input. - Fix: Don’t feed
/configto downloaders. Use the public Vimeo page (https://vimeo.com/<id>/<hash>) orplayer.vimeo.com/video/<id>(only if embed-only and you have the embedding page referer).
- Meaning: yt-dlp detected settings that look like embed-only and demanded the embedding page.
- In your case: the clip does play on Vimeo.com, so it isn’t strictly embed-only; the message appeared because the input/headers didn’t match a valid public Vimeo flow.
- Fix: Use the public vimeo.com page URL with the correct referer/cookies (see final command).
-
Meaning: The Vimeo page is viewable only when you’re signed in. yt-dlp, running headless, has no session by default.
-
Fix: Provide your browser session cookies:
--cookies-from-browser "chrome:Profile 197"(orDefault,Profile 1, etc.; checkchrome://profile-internals).
The extractor is attempting impersonation, but no impersonate target is available...
-
Meaning: yt-dlp optionally uses a “browser-like” HTTP stack (impersonation) for some sites. Your install lacked the optional deps.
-
Usually you don’t need it once cookies + referer are correct. If you ever do:
python3 -m pip install -U "yt-dlp[default]" curl_cffi brotli certifi- Then you can add
--impersonate chrome124if extraction fails without it.
-
Decide the right URL to pass to yt-dlp
- If it plays on vimeo.com while you’re logged in → use the public page URL
https://vimeo.com/<id>/<hash>. - If it doesn’t play on vimeo.com but plays embedded on some site → you must use the embedding page URL and set it as both the URL and
--referer.
- If it plays on vimeo.com while you’re logged in → use the public page URL
-
Send the exact referer
--referer "https://vimeo.com/<id>/<hash>"(or the real embedding page URL).
-
Provide auth if required
- Login-required →
--cookies-from-browser "chrome:Profile X". - Password-protected → add
--video-password '...'(you’ll still want the cookies if the page requires login too).
- Login-required →
-
Quality & output
-S "codec:avc,res,ext"biases toward MP4/AVC (good for compatibility).--remux-video mp4 --merge-output-format mp4 --postprocessor-args "ffmpeg:-movflags +faststart"→ single, seekable MP4 without re-encoding.-N 16→ parallel fragments for speed.
-
(Optional) Inspect formats first
yt-dlp --cookies-from-browser "chrome:Profile 197" \ --referer "https://vimeo.com/<id>/<hash>" \ -F "https://vimeo.com/<id>/<hash>"
- ✅ Public on vimeo.com → use the vimeo.com URL + (if needed) cookies.
- 🔑 Password-protected → add
--video-password. - 🔒 Login-required → add
--cookies-from-browser "chrome:Profile X". - 🌐 Embed-only → you must use the actual embedding page URL as the main URL and referer (cookies if gated).
- ⛔ Don’t feed
/config?...to downloaders.
Public / Login-required on vimeo.com
yt-dlp -N 16 \
--cookies-from-browser "chrome:Profile 197" \
--referer "https://vimeo.com/<ID>/<HASH>" \
-S "codec:avc,res,ext" \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
"https://vimeo.com/<ID>/<HASH>"Password-protected (add password)
yt-dlp -N 16 \
--cookies-from-browser "chrome:Profile 197" \
--video-password 'PASSWORD' \
--referer "https://vimeo.com/<ID>/<HASH>" \
-S "codec:avc,res,ext" \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
"https://vimeo.com/<ID>/<HASH>"Embed-only (needs the actual site that embeds it)
EMBED_URL='https://allowed-site.example/video-page'
yt-dlp -N 16 \
--cookies-from-browser "chrome:Profile 197" \
--referer "$EMBED_URL" \
--add-header "Origin: https://allowed-site.example" \
-S "codec:avc,res,ext" \
--merge-output-format mp4 --remux-video mp4 \
--postprocessor-args "ffmpeg:-movflags +faststart" \
"$EMBED_URL"- You hopped across three different URL types (
player,/config,vimeo.com/<id>/<hash>), each with different rules. - The video required login on Vimeo, so cookies were mandatory.
- The tools’ messages were accurate but easy to misread out of order (embed-only vs. login-required).
- Quoting/headers matter: zsh needed quotes; Vimeo needed referer (and sometimes origin) to pass checks.
Once you give yt-dlp the right page URL, plus your browser cookies and the exact referer, Vimeo behaves.
-
Public (unlisted or listed)
- You can hit the
vimeo.com/<id>/<hash>page and play without an account. - But internally, Vimeo still calls their “web client” API (which often requires a session cookie, even for public videos).
- If yt-dlp only uses that API, it sees “login required” unless cookies are present.
- You can hit the
-
Config/Embed API
- The
player.vimeo.com/video/<id>/configJSON contains stream manifests. - This works for public videos without login.
- yt-dlp normally knows how to grab this, but sometimes it defaults to the web client path first.
- The
Even though you weren’t logged in, by giving yt-dlp your browser cookies, you also gave it:
- Session info showing you had accepted Vimeo’s cookie banner.
- Region/consent headers Vimeo expects from a real browser.
- Any tracking/session IDs Vimeo needs to let the “web client” flow succeed.
So yt-dlp stopped complaining, because with cookies it could complete the API call.
- You didn’t need a Vimeo account login.
- yt-dlp just needed some browser cookies to satisfy Vimeo’s API (consent/session stuff).
That’s why it was weird: the video looked public, but Vimeo’s backend still enforced “web client requires cookies” logic.
Alright, here’s the refined Vimeo write-up that clears up the “required login” confusion and explains the distinction 👇
Vimeo videos can behave differently depending on the uploader’s privacy settings. Sometimes a video is public (anyone can watch it on vimeo.com), but yt-dlp still errors out with messages like:
The web client only works when logged-in. Use --cookies...
Here’s why that happens, and how to handle each case.
-
✅ Plays directly on
https://vimeo.com/<id>/<hash>with no account. -
⚠️ yt-dlp may still need browser cookies because Vimeo’s “web client” API refuses to serve streams without a session cookie (even for public videos). -
Fix:
yt-dlp -N 16 \ --cookies-from-browser "chrome:Profile 197" \ --referer "https://vimeo.com/<id>/<hash>" \ -S "codec:avc,res,ext" \ --merge-output-format mp4 --remux-video mp4 \ --postprocessor-args "ffmpeg:-movflags +faststart" \ "https://vimeo.com/<id>/<hash>"
-
🔒 Plays only after you type a password.
-
Fix: add
--video-password 'PASSWORD'(cookies still help).yt-dlp -N 16 \ --cookies-from-browser "chrome:Profile 197" \ --video-password 'SECRET' \ --referer "https://vimeo.com/<id>/<hash>" \ "https://vimeo.com/<id>/<hash>"
-
🔑 Plays only when signed in with a Vimeo account that has access.
-
Fix: you must supply logged-in cookies from your browser profile.
yt-dlp --cookies-from-browser "chrome:Profile 197" "https://vimeo.com/<id>/<hash>"
-
🌐 Doesn’t play on vimeo.com at all. Only plays embedded on specific whitelisted domains.
-
Fix: you need the embedding site’s URL and use it as both the URL and referer:
EMBED_URL="https://example.com/page-with-vimeo-embed" yt-dlp --referer "$EMBED_URL" "$EMBED_URL"
- Vimeo’s API often refuses to serve streams without basic browser state (consent banner, session tokens, geo headers).
- Even for public videos, yt-dlp without cookies = blocked API.
- With
--cookies-from-browser, yt-dlp looks like your browser and passes those small session values, so it works. - 👉 You weren’t required to log in; you just had to look like a real viewer.
- Always start with the public page URL (
https://vimeo.com/<id>/<hash>). - Add
--refererpointing to that same page. - Use
--cookies-from-browser "chrome:Profile X"if you get login/cookie errors — even if you’re not logged in. - Add
--video-passwordif it prompts for one. - For embed-only, use the real embedding site’s URL.
✅ That’s why your case felt “weird”: the video was publicly viewable, but yt-dlp still needed cookies from your browser to satisfy Vimeo’s API. Once you passed those, it downloaded fine.
Start
↓
Can you play the video on vimeo.com without logging in?
├─ Yes → Public (but may need cookies)
│ Command:
│ yt-dlp -N 16 \
│ --cookies-from-browser "chrome:Profile 197" \
│ --referer "https://vimeo.com/<id>/<hash>" \
│ -S "codec:avc,res,ext" \
│ --merge-output-format mp4 --remux-video mp4 \
│ --postprocessor-args "ffmpeg:-movflags +faststart" \
│ "https://vimeo.com/<id>/<hash>"
│
└─ No → Continue
↓
Does Vimeo ask for a password?
├─ Yes → Password-protected
│ Command:
│ yt-dlp -N 16 \
│ --cookies-from-browser "chrome:Profile 197" \
│ --video-password 'PASSWORD' \
│ --referer "https://vimeo.com/<id>/<hash>" \
│ "https://vimeo.com/<id>/<hash>"
│
└─ No → Continue
↓
Does Vimeo play only when logged into your Vimeo account?
├─ Yes → Login-required
│ Command:
│ yt-dlp -N 16 \
│ --cookies-from-browser "chrome:Profile 197" \
│ --referer "https://vimeo.com/<id>/<hash>" \
│ "https://vimeo.com/<id>/<hash>"
│
└─ No → Continue
↓
Does the video not play on Vimeo at all but plays embedded on another site?
├─ Yes → Embed-only
│ Command:
│ EMBED_URL="https://example.com/page-with-vimeo-embed"
│ yt-dlp -N 16 \
│ --cookies-from-browser "chrome:Profile 197" \
│ --referer "$EMBED_URL" \
│ --add-header "Origin: https://example.com" \
│ "$EMBED_URL"
│
└─ End
Related
- https://github.com/serpapps/vimeo-video-downloader
- https://gist.github.com/devinschumacher/a189434fc9f374965888ca2dc793953e
- https://gist.github.com/devinschumacher/8024bc4693d79aef641b2c281e45d6cb
- How to Download Password Protected Vimeo Videos
- https://gist.github.com/devinschumacher/8095f410a01494bc04ebf6c6440ce25d



👉 Get Vimeo Video Downloader: https://serp.ly/vimeo-video-downloader