The yt-dlp flags worth knowing on a Mac
Not another install tutorial. The twenty or so flags a real user converges on after a year, what each is actually for, and the config file that means never typing them again.
yt-dlp has several hundred options. You will use about twenty. This is those twenty, grouped by the problem they solve, written to be bookmarked rather than read once.
Everything here was checked against yt-dlp 2026.07.04 on macOS.
Install and keep it current
brew install yt-dlp ffmpeg
FFmpeg is not optional in practice: above 360p, YouTube stores video and audio separately and something has to combine them. Without it you get two files, one silent.
brew upgrade yt-dlp
Do this before debugging anything. A large share of “yt-dlp is broken” is a
build from before YouTube’s last change. yt-dlp -U updates a standalone
binary but will refuse to touch a Homebrew install.
Choosing a format
The single most useful flag, and the one most people never touch:
yt-dlp -F "URL"
It lists every stream, with codec, resolution and size. Read it before guessing.
-f "bestvideo[vcodec^=avc1]+bestaudio[ext=m4a]" # H.264 — opens everywhere
-f "bestvideo+bestaudio/best" # best available, any codec
-f "best[ext=mp4]" # single file, capped at 360p
-f bestaudio[ext=m4a] # audio only, no re-encoding
-f "bv[height<=1080]+ba" # cap the resolution
bv and ba are shorthand for bestvideo and bestaudio. The / is a
fallback: A/B means try A, then B.
The one worth understanding is vcodec^=avc1, which is H.264. YouTube serves
nothing above 1080p in H.264 — above that it is VP9 and AV1, which QuickTime
does not play. That is an article of its
own and the reason this
selector exists.
--merge-output-format mkv
When mixing codecs that MP4 will not hold. MKV accepts anything without converting.
Naming files
-o "%(title)s.%(ext)s" # the default, roughly
-o "%(uploader)s - %(title)s.%(ext)s"
-o "%(upload_date)s - %(title)s [%(id)s].%(ext)s" # sorts by date
-o "%(playlist_index)03d - %(title)s.%(ext)s" # sorts by position
-o "%(upload_date>%Y)s/%(title)s.%(ext)s" # into per-year folders
Three details that save trouble later:
%(playlist_index)03d— the03dzero-pads, so002sorts before010. Without it Finder puts10before2.[%(id)s]— keeps the eleven-character video ID in the name. It is how you find the original after a title changes.%(upload_date>%Y)s— anything after>is astrftimeformat.
--restrict-filenames
ASCII only, no spaces. Worth it if the files are headed for a server, a script, or a drive shared with Windows.
Subtitles
--list-subs # see what exists
--write-subs --sub-langs en --convert-subs srt # creator subtitles
--write-auto-subs --sub-format srv1 # automatic captions, clean
--embed-subs # as a track inside the video
--skip-download # subtitles only, no video
--sub-format srv1 is the one to remember. The default download of automatic
captions arrives with every line duplicated two or three
times; srv1 does not have that
problem at all.
Playlists and channels
--yes-playlist / --no-playlist # resolve the video-or-list ambiguity
--playlist-items 1-25 # a range
--playlist-items 3,7,11-14 # a selection
--flat-playlist --print "%(title)s" # list without opening each video
--download-archive done.txt # skip what previous runs completed
--match-filter "duration > 120" # skip trailers and shorts
--download-archive is the one that matters. It records completed IDs, so a
re-run skips them — which is what makes a long job survivable and a scheduled
sync cheap. See playlists and
channel archives.
Only part of a video
--download-sections "*3:40-7:15" # a time range
--download-sections "*-30-0" # the last 30 seconds
--download-sections "Introduction" # a chapter, by name (no *)
--split-chapters # every chapter as its own file
--force-keyframes-at-cuts # exact cuts, at the cost of re-encoding
--download-sections fetches only the bytes it needs — ten seconds out of a
long 4K video came out at 93 KB in about two seconds. The full
piece, including why it returns 403 if
you pair it with the wrong format.
Not getting blocked
--sleep-requests 1 # between metadata requests
--min-sleep-interval 5 # between downloads
--max-sleep-interval 15
--limit-rate 5M # cap bandwidth
--retries 5
--fragment-retries 20
--continue # resume a partial file
--sleep-requests is the one that prevents HTTP Error 429 on playlists.
--fragment-retries matters more than it looks: big downloads arrive in
fragments and one failed fragment should not restart the file. Details in
403, 429 and downloads that crawl.
Metadata and tidying up
--embed-thumbnail # cover art in the file
--embed-metadata # title, artist, date into the tags
--embed-chapters # chapter markers players can jump between
--write-info-json # everything YouTube knows, as JSON alongside
--write-thumbnail # the thumbnail as a separate image
For music, --embed-thumbnail --embed-metadata is the difference between a
usable library and a folder of untitled tracks. For an archive,
--write-info-json preserves descriptions and dates that disappear with the
channel.
Diagnosing
--simulate # do everything except download
--print "%(title)s" # print one field and stop
--print "%(chapters)j" # a field as JSON
-J # the whole metadata object
--verbose # what to paste into a bug report
--rm-cache-dir # clear cached signature data
--ignore-config # run without your config file
--ignore-config is the first thing to try when behaviour makes no sense: it
tells you in one command whether the problem is yt-dlp or something you wrote
in the config months ago and forgot.
Stop typing all this
Put the settings you always want in ~/.config/yt-dlp/config. One flag per
line, no quotes around the whole line:
# Format: H.264 so it opens in QuickTime, capped at 1080p
-f bestvideo[vcodec^=avc1][height<=1080]+bestaudio[ext=m4a]/best
# Where things go
-o ~/Downloads/%(title)s.%(ext)s
# Be polite; 429 costs hours
--sleep-requests 1
--min-sleep-interval 3
--max-sleep-interval 8
# Resilience
--continue
--retries 5
--fragment-retries 20
# Metadata worth keeping
--embed-metadata
--embed-chapters
Override any of it per run — command line beats config. And when something
behaves strangely, --ignore-config tells you whether this file is why.
The ones people use and probably should not
--cookies-from-browser chrome — genuinely necessary for private and
members-only videos. Everywhere else it attaches your identity to activity that
was previously anonymous, which raises what a mistake costs without fixing
anything. It is widely recommended as a cure for
“Sign in to confirm you’re not a bot”;
read that before reaching for it.
--concurrent-fragments 8 — faster on a fast line, and one of the surest
ways to get rate limited on a long job. Fine for one video, wrong for nine
hundred.
--retries infinite — turns a temporary block into a permanent one by
never stopping asking.
And if you would rather not
Everything above is one program with a large surface. Most people want three of these behaviours — H.264 so it plays, audio extraction that does not re-encode, and an engine that stays current — and would rather not maintain a config file to get them.
That is the app I build: the same yt-dlp underneath, with those defaults chosen
and kept up to date on its own. It does less than the command line, on purpose.
When you need --download-sections or a scheduled channel sync, come back to
this page — the commands here are the ones it runs anyway.