Downloading YouTube videos programmatically requires a library that keeps pace with YouTube's constant changes. Pytubefix is an actively maintained fork of pytube that solves broken functionality and adds new features. In this guide, you'll learn how to use pytubefix to download videos, audio, playlists, and captions—plus advanced techniques like async downloads and proxy support.
What is Pytubefix?
Pytubefix is a lightweight Python library for downloading YouTube videos. It's a drop-in replacement for pytube that provides active maintenance and bug fixes whenever YouTube changes its internal API.
The library supports progressive and DASH streams, callback functions for download progress, caption extraction in SRT format, and has zero third-party dependencies. This makes it ideal for scripts, automation tools, and applications that need reliable YouTube downloading.
Installing Pytubefix
Getting pytubefix installed takes just one command. Open your terminal and run:
pip install pytubefix
Verify the installation by checking the version:
pip show pytubefix
You should see output showing version 10.3.6 or later. The library requires Python 3.7 or higher.
For development installations from source, you can clone the repository:
git clone https://github.com/JuanBindez/pytubefix.git
cd pytubefix
pip install -e .
Downloading Your First Video
The simplest way to download a YouTube video takes just a few lines of code. Start by importing the YouTube class and creating an instance with your video URL.
from pytubefix import YouTube
# Create a YouTube object
yt = YouTube('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
# Print video title
print(yt.title)
# Get highest resolution stream
stream = yt.streams.get_highest_resolution()
# Download the video
stream.download()
This code downloads the video in the highest available resolution to your current directory.
Let's break down what each line does:
YouTube()creates an object representing the videoyt.titleaccesses the video's metadatastreams.get_highest_resolution()finds the best quality streamdownload()saves the file to disk
Adding Progress Tracking
For larger videos, you'll want to see download progress. Pytubefix provides a built-in progress callback that displays a progress bar in your terminal.
from pytubefix import YouTube
from pytubefix.cli import on_progress
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
# Add the progress callback
yt = YouTube(url, on_progress_callback=on_progress)
print(f"Downloading: {yt.title}")
stream = yt.streams.get_highest_resolution()
stream.download()
print("Download complete!")
The on_progress callback displays percentage completion as chunks download. This visual feedback is essential when downloading long videos.
You can also create custom progress callbacks:
def my_progress_callback(stream, chunk, bytes_remaining):
total_size = stream.filesize
bytes_downloaded = total_size - bytes_remaining
percentage = (bytes_downloaded / total_size) * 100
print(f"\rDownloaded: {percentage:.1f}%", end="")
yt = YouTube(url, on_progress_callback=my_progress_callback)
This gives you full control over how progress appears in your application.
Downloading Audio Only
Extracting audio from YouTube videos is common for podcasts, music, and background listening. Pytubefix makes audio-only downloads straightforward.
from pytubefix import YouTube
from pytubefix.cli import on_progress
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
yt = YouTube(url, on_progress_callback=on_progress)
print(f"Extracting audio from: {yt.title}")
# Get audio-only stream
audio_stream = yt.streams.get_audio_only()
# Download as m4a
audio_stream.download()
The get_audio_only() method returns the highest quality audio stream without video. Files save in M4A format by default.
To download audio as MP3, you have two options. First, use the built-in mp3 parameter:
audio_stream = yt.streams.get_audio_only()
audio_stream.download(mp3=True)
Alternatively, convert using an external library like moviepy:
from moviepy.editor import VideoFileClip
import os
# Download audio
audio_path = audio_stream.download()
# Convert to MP3
video = VideoFileClip(audio_path)
video.audio.write_audiofile(audio_path.replace('.m4a', '.mp3'))
# Remove original m4a file
os.remove(audio_path)
Working with Streams and Quality Selection
YouTube provides multiple streams for each video at different resolutions and formats. Understanding stream types helps you download exactly what you need.
Listing Available Streams
from pytubefix import YouTube
yt = YouTube('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
# View all available streams
print(yt.streams)
This prints a list of Stream objects showing resolution, codec, and file type. You'll see progressive streams (video+audio combined) and adaptive streams (separate video and audio).
Filtering Streams
Filter streams by specific attributes:
# Get only MP4 streams
mp4_streams = yt.streams.filter(file_extension='mp4')
# Get only progressive streams (video + audio)
progressive = yt.streams.filter(progressive=True)
# Get only 1080p streams
hd_streams = yt.streams.filter(res='1080p')
# Get only audio streams
audio_only = yt.streams.filter(only_audio=True)
# Combine filters
result = yt.streams.filter(
progressive=True,
file_extension='mp4'
).order_by('resolution').desc()
Progressive vs DASH Streams
Progressive streams contain both video and audio in one file. They're limited to 720p maximum but download in a single request.
DASH (Dynamic Adaptive Streaming over HTTP) streams separate video and audio. They offer higher resolutions like 1080p, 4K, and 8K but require downloading two files and merging them.
# Get highest resolution progressive stream (max 720p)
stream = yt.streams.get_highest_resolution()
# Get adaptive streams for higher quality
video_stream = yt.streams.filter(
adaptive=True,
file_extension='mp4',
only_video=True
).order_by('resolution').desc().first()
audio_stream = yt.streams.filter(
adaptive=True,
only_audio=True
).first()
For 1080p+ downloads, you need to merge video and audio using ffmpeg:
ffmpeg -i video.mp4 -i audio.m4a -c:v copy -c:a aac output.mp4
Downloading Entire Playlists
Pytubefix handles YouTube playlists seamlessly. The Playlist class iterates through all videos in a playlist.
from pytubefix import Playlist
from pytubefix.cli import on_progress
playlist_url = "https://www.youtube.com/playlist?list=PLxxxxxx"
pl = Playlist(playlist_url)
print(f"Playlist: {pl.title}")
print(f"Total videos: {len(pl.video_urls)}")
# Download all videos
for video in pl.videos:
print(f"\nDownloading: {video.title}")
stream = video.streams.get_highest_resolution()
stream.download()
For audio-only playlist downloads:
for video in pl.videos:
audio = video.streams.get_audio_only()
audio.download(output_path="playlist_audio")
You can access playlist metadata:
print(f"Owner: {pl.owner}")
print(f"Views: {pl.views}")
print(f"Last updated: {pl.last_updated}")
Downloading Channel Videos
Download every video from a YouTube channel using the Channel class:
from pytubefix import Channel
channel_url = "https://www.youtube.com/@ProgrammingKnowledge"
c = Channel(channel_url)
print(f"Channel: {c.channel_name}")
print(f"Total videos: {len(c.video_urls)}")
# Download all channel videos
for video in c.videos:
print(f"Downloading: {video.title}")
stream = video.streams.get_highest_resolution()
stream.download(output_path="channel_videos")
This approach works for any public YouTube channel. Be mindful of storage space when downloading entire channels.
Extracting and Saving Captions
YouTube videos often include captions that pytubefix can extract and save:
from pytubefix import YouTube
yt = YouTube('https://www.youtube.com/watch?v=2lAe1cqCOXo')
# View available caption tracks
print(yt.captions)
The output shows language codes like a.en (auto-generated English) or en (manual English captions).
Generating SRT Subtitles
# Get English captions
caption = yt.captions['a.en']
# Generate SRT format
srt_content = caption.generate_srt_captions()
print(srt_content)
Saving Captions to File
# Save to text file
caption.save_captions("video_captions.txt")
Captions save in SRT format, compatible with most video players and subtitle editors.
Using OAuth for Age-Restricted Content
Some YouTube videos require authentication to access. Pytubefix supports OAuth to handle age-restricted and members-only content.
from pytubefix import YouTube
from pytubefix.cli import on_progress
url = "https://www.youtube.com/watch?v=restricted_video_id"
yt = YouTube(
url,
use_oauth=True,
allow_oauth_cache=True,
on_progress_callback=on_progress
)
stream = yt.streams.get_highest_resolution()
stream.download()
When you run this code the first time, it opens a browser window asking you to sign in to Google. After authentication, your credentials cache locally so future runs skip the login step.
The allow_oauth_cache=True parameter stores your token for subsequent downloads. This is essential for automated scripts.
Specifying Output Directory and Filename
Control where files save and what they're named:
from pytubefix import YouTube
yt = YouTube('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
stream = yt.streams.get_highest_resolution()
# Custom output directory
stream.download(output_path="/path/to/downloads")
# Custom filename
stream.download(filename="my_video.mp4")
# Both directory and filename
stream.download(
output_path="/path/to/downloads",
filename="custom_name.mp4"
)
If the output directory doesn't exist, pytubefix creates it automatically.
Searching YouTube Videos
Find videos without leaving your script using the Search class:
from pytubefix import Search
results = Search('Python tutorial')
for video in results.videos:
print(f"Title: {video.title}")
print(f"URL: {video.watch_url}")
print(f"Duration: {video.length} seconds")
print("---")
Using Search Filters
Narrow results using the Filter class:
from pytubefix.contrib.search import Search, Filter
filters = (
Filter.create()
.upload_date(Filter.UploadDate.TODAY)
.type(Filter.Type.VIDEO)
.duration(Filter.Duration.UNDER_4_MINUTES)
.feature([Filter.Features.CREATIVE_COMMONS, Filter.Features._4K])
.sort_by(Filter.SortBy.UPLOAD_DATE)
)
s = Search('programming', filters=filters)
for video in s.videos:
print(video.watch_url)
Available filter options include upload date, video type, duration, features, and sort order.
Async Downloads with AsyncYouTube
For applications requiring non-blocking downloads, pytubefix provides an async interface:
import asyncio
from pytubefix import AsyncYouTube
async def main():
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
yt = AsyncYouTube(url, use_oauth=True, allow_oauth_cache=True)
# Fetch streams asynchronously
streams = await yt.streams()
print("Available Streams:")
for stream in streams:
print(stream)
# Get video metadata asynchronously
title = await yt.title()
views = await yt.views()
print(f"Title: {title}")
print(f"Views: {views}")
if __name__ == '__main__':
asyncio.run(main())
Async Download with Progress Callbacks
import asyncio
from pytubefix import AsyncYouTube
async def main():
url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
def on_progress(stream, chunk, bytes_remaining):
total = stream.filesize
percent = (1 - bytes_remaining / total) * 100
print(f"\rProgress: {percent:.2f}%", end="")
def on_complete(stream, file_path):
print(f"\nDownload complete: {file_path}")
yt = AsyncYouTube(url, use_oauth=True, allow_oauth_cache=True)
yt.register_on_progress_callback(on_progress)
yt.register_on_complete_callback(on_complete)
# Get stream by itag (18 = 360p MP4)
stream = await yt.get_stream_by_itag(18)
print(f"Downloading: {await yt.title()}")
stream.download(filename="async_video.mp4")
if __name__ == '__main__':
asyncio.run(main())
The async interface is ideal for GUI applications or web servers where blocking operations cause poor user experience.
Using Proxies for Scale
When downloading many videos, YouTube may rate limit your IP address. Using proxies helps distribute requests across multiple IPs.
from pytubefix import YouTube
proxies = {
'http': 'http://proxy_ip:port',
'https': 'https://proxy_ip:port'
}
yt = YouTube(
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
proxies=proxies
)
stream = yt.streams.get_highest_resolution()
stream.download()
For residential proxies that appear more like regular users, services like Roundproxies.com offer rotating residential, datacenter, ISP, and mobile proxies. These reduce the chance of detection when running automated downloads.
Rotate through multiple proxies for high-volume operations:
import random
proxy_list = [
{'http': 'http://proxy1:port', 'https': 'https://proxy1:port'},
{'http': 'http://proxy2:port', 'https': 'https://proxy2:port'},
{'http': 'http://proxy3:port', 'https': 'https://proxy3:port'},
]
def download_with_rotation(url):
proxy = random.choice(proxy_list)
yt = YouTube(url, proxies=proxy)
return yt.streams.get_highest_resolution().download()
Accessing Video Chapters and Key Moments
YouTube videos with chapters display timestamps in the progress bar. Pytubefix can extract this data:
import asyncio
from pytubefix import AsyncYouTube
async def main():
url = "https://www.youtube.com/watch?v=video_with_chapters"
yt = AsyncYouTube(url)
chapters = await yt.chapters()
key_moments = await yt.key_moments()
print("Chapters:", chapters)
print("Key Moments:", key_moments)
asyncio.run(main())
Chapter data includes timestamps and titles, useful for splitting long videos into segments.
Error Handling Best Practices
Production code needs proper error handling for network issues, unavailable videos, and other failures:
from pytubefix import YouTube
from pytubefix.exceptions import (
VideoUnavailable,
RegexMatchError,
AgeRestrictedError
)
def safe_download(url, output_path="."):
try:
yt = YouTube(url)
stream = yt.streams.get_highest_resolution()
stream.download(output_path=output_path)
return True
except VideoUnavailable:
print(f"Video unavailable: {url}")
return False
except AgeRestrictedError:
print("Age restricted - try OAuth authentication")
return False
except RegexMatchError:
print("Failed to parse video data - YouTube may have changed")
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
Always wrap download operations in try-except blocks. YouTube frequently changes its internal APIs, which can break parsing temporarily.
Command Line Interface
Pytubefix includes a CLI for quick downloads without writing code:
# Download video
python -m pytubefix https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Download audio only
python -m pytubefix https://www.youtube.com/watch?v=dQw4w9WgXcQ --audio
# Specify output directory
python -m pytubefix https://www.youtube.com/watch?v=dQw4w9WgXcQ -o /downloads
The CLI is handy for one-off downloads or shell scripts.
Migrating from Pytube to Pytubefix
Pytube users can switch to pytubefix with minimal changes. The API is nearly identical:
Before (pytube):
from pytube import YouTube
yt = YouTube('url')
After (pytubefix):
from pytubefix import YouTube
yt = YouTube('url')
Just change the import statement. All method names and parameters remain the same.
Pytubefix has several advantages over pytube:
- Active maintenance with regular updates
- Faster bug fixes when YouTube changes
- Additional features like async support
- Better OAuth handling for restricted content
Common Issues and Solutions
"Video unavailable" errors: The video may be private, deleted, or region-locked. Try using a proxy from a different country.
Download stalls or fails: YouTube may be throttling. Add delay between downloads:
import time
for url in video_urls:
download_video(url)
time.sleep(2) # Wait 2 seconds between downloads
OAuth token expires: Delete the cached token file and re-authenticate:
rm ~/.cache/pytubefix/tokens.json
Cipher errors: Update pytubefix to the latest version:
pip install --upgrade pytubefix
Batch Downloading Multiple Videos
Download multiple videos efficiently by processing a list of URLs:
from pytubefix import YouTube
from pytubefix.cli import on_progress
import time
video_urls = [
"https://www.youtube.com/watch?v=video1",
"https://www.youtube.com/watch?v=video2",
"https://www.youtube.com/watch?v=video3",
]
def batch_download(urls, output_path="downloads"):
successful = 0
failed = []
for i, url in enumerate(urls):
try:
print(f"\n[{i+1}/{len(urls)}] Processing...")
yt = YouTube(url, on_progress_callback=on_progress)
print(f"Title: {yt.title}")
stream = yt.streams.get_highest_resolution()
stream.download(output_path=output_path)
successful += 1
time.sleep(1) # Rate limiting
except Exception as e:
print(f"Failed: {e}")
failed.append(url)
print(f"\nComplete: {successful} downloaded, {len(failed)} failed")
return failed
# Run batch download
failed_urls = batch_download(video_urls)
This pattern handles failures gracefully and provides progress feedback for each video.
Getting Video Metadata
Access rich metadata without downloading the video itself:
from pytubefix import YouTube
yt = YouTube('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
# Basic metadata
print(f"Title: {yt.title}")
print(f"Author: {yt.author}")
print(f"Length: {yt.length} seconds")
print(f"Views: {yt.views}")
print(f"Rating: {yt.rating}")
print(f"Description: {yt.description[:200]}...")
# Thumbnail URLs
print(f"Thumbnail: {yt.thumbnail_url}")
# Publish date
print(f"Published: {yt.publish_date}")
# Keywords/tags
print(f"Keywords: {yt.keywords}")
This metadata is useful for building video catalogs, checking content before downloading, or displaying information in applications.
Frequently Asked Questions
Is pytubefix legal to use?
Pytubefix is a legal Python library. However, downloading copyrighted content without permission may violate YouTube's Terms of Service and copyright laws. Use the library responsibly for content you have rights to download, like your own videos or Creative Commons licensed content.
What's the difference between pytubefix and pytube?
Pytubefix is a maintained fork of pytube. When pytube stopped receiving updates and broke due to YouTube changes, pytubefix continued development. It fixes bugs faster, adds new features like async support, and maintains compatibility with YouTube's evolving API.
Why does my download fail with cipher errors?
YouTube regularly changes how it protects video streams. When this happens, pytubefix needs an update to handle new encryption methods. Run pip install --upgrade pytubefix to get the latest version with current cipher fixes.
Can I download 4K videos?
Yes, but 4K streams are DASH adaptive streams. You need to download video and audio separately, then merge them using ffmpeg. Progressive streams max out at 720p.
How do I download private videos?
Private videos require OAuth authentication. Set use_oauth=True when creating the YouTube object and sign in with an account that has access to the video. The library caches your credentials for future downloads.
Does pytubefix work with YouTube Shorts?
Yes, YouTube Shorts work the same as regular videos. Use the Shorts URL directly with the YouTube class. The download process is identical.
Conclusion
Pytubefix provides a reliable way to download YouTube content programmatically. From simple single-video downloads to complex async operations with proxy rotation, the library handles it all.
Start with basic downloads, then add progress tracking and error handling. For production applications, implement OAuth authentication and proxy support to handle edge cases.
The library's active maintenance means bugs get fixed quickly when YouTube changes. Check the GitHub repository for updates and community discussions when you encounter issues.
Keep pytubefix updated regularly to ensure compatibility with YouTube's latest changes. The developer community actively monitors for breaking changes and releases fixes within days.