Here are three ways to download a video from a URL with Python:
- Using the requests module
- Using urllib module
- Using FFMpeg via subprocess
Downloading videos serves a multitude of purposes, from ensuring offline access when you may have limited internet access to archiving important content for further analysis and processing.
Ensure that before downloading a video, you respect the legal technicalities of the specific site and its boundaries.
To demonstrate this tutorial, we will use free and open-source URLs that allow us to download videos to our local system. Those URLs are:
https://static.videezy.com/system/resources/previews/000/008/220/original/Triangles_01.mp4 https://www.shutterstock.com/shutterstock/videos/1111470671/preview/stock-footage-electric-light-bulb-bright-polygonal-connections-on-a-dark-blue-background-technology-concept.webm
Shutterstock is a renowned website where you can find free, licensed images and videos for educational purposes.
My intent here is to teach you how to do it and not do it illegally. Having said that, let’s explore three ways.
Method 1: Using the “requests” module
The requests module provides a requests.get() method to download video files with streaming enabled.
If any type of error occurs during the process, we will catch and print it. If the file is too large, then download it in chunks and then save it locally.
Decision Tree Diagram
The diagram above illustrates the process flow. This provides a basic overview of how operations will be conducted.
Install the “requests” library using the command below:
pip install requests
Code example
import requests
import os
# Custom function that accepts url and local save_path
def download_video(url, save_path):
try:
# Ensuring the directory exists
os.makedirs(os.path.dirname(save_path), exist_ok=True)
# Requesting the video file with streaming
response = requests.get(url, stream=True)
response.raise_for_status() # Check for HTTP errors
# Getting the total file size from the response headers (optional)
total_size = int(response.headers.get('content-length', 0))
# Download and save the video in chunks
with open(save_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk: # Filter out keep-alive new chunks
f.write(chunk)
print(f"Video downloaded successfully and saved to {save_path}")
# Catch and print the exceptions
except requests.exceptions.HTTPError as e:
print(f"HTTP error occurred: {e}")
except requests.exceptions.RequestException as e:
print(f"Request error occurred: {e}")
except Exception as e:
print(f"An error occurred: {e}")
# Calling the custom function with video_url and save path
video_url = 'https://static.videezy.com/system/resources/previews/000/008/220/original/Triangles_01.mp4'
save_path = './videos/sample.mp4'
download_video(video_url, save_path)
Output
Video downloaded successfully and saved to ./videos/sample.mp4
In my current project folder, there is a “videos” folder, and I saved the video sample.mp4 inside this folder.
The requests library approach is easy to implement, has clear syntax, and is the fastest approach to downloading a video.
However, if the download is interrupted, then it does not natively support resuming it. Moreover, if you are working with complex streaming protocols, then it might not work either.
This approach is ideal when you have a URL that does not require authentication or additional processing to fetch it. The time complexity for the process is O(n), where n is the video file size. If the file is big, it will take longer.
The space complexity is O(1) as it writes a file in chunks without loading it entirely into memory.
Method 2: Using the “urllib” module
When working with the “urllib” library, ensure that you create a secure connection using the ssl.create_default_context() function with the certifi library.
Python certifi is a third-party package that provides Mozilla’s CA Bundle.
Open the URL with SSL and start reading video data.
If you encounter no errors, read the video in chunks and save it to the specified file locally. While downloading, display the progress, and once the video is downloaded, print the success message.
Decision Tree Diagram

Install the certifi library using the command below:
pip install certifi
Also, install ssl module:
pip install ssl
Code example
import urllib.request
import os
import ssl
import certifi
# Custom function to download a video
def download_video(url, save_path):
try:
# Ensuring the directory exists
os.makedirs(os.path.dirname(save_path), exist_ok=True)
# Creating an SSL context using certifi's CA bundle
context = ssl.create_default_context(cafile=certifi.where())
# Opening the URL with the SSL context
with urllib.request.urlopen(url, context=context) as response:
total_size = int(response.getheader('Content-Length', 0))
block_size = 8192 # 8 Kibibytes
downloaded = 0
with open(save_path, 'wb') as out_file:
while True:
buffer = response.read(block_size)
if not buffer:
break
out_file.write(buffer)
downloaded += len(buffer)
if total_size > 0:
percent = downloaded / total_size * 100
print(f"\rDownloading: {percent:.2f}%", end='')
else:
print(f"\rDownloaded: {downloaded} bytes", end='')
print(f"\nVideo downloaded successfully and saved to {save_path}")
# Printing the error if occurs
except urllib.error.URLError as e:
print(f"URL Error: {e.reason}")
except urllib.error.HTTPError as e:
print(f"HTTP Error: {e.code} {e.reason}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
# Calling the custom function with input url and output directory
video_url = 'https://www.shutterstock.com/shutterstock/videos/1111470671/preview/stock-footage-electric-light-bulb-bright-polygonal-connections-on-a-dark-blue-background-technology-concept.webm'
save_path = './videos/urllib_video.mp4'
download_video(video_url, save_path)
Output
Downloading: 100.00% Video downloaded successfully and saved to ./videos/urllib_video.mp4
It provides limited flexibility compared to the “requests” module approach, and is harder to handle chunked downloads.
It is suitable for simpler downloads where authentication is not required and streaming is not needed.
The time complexity is O(n), where n depends on the size of the video.
The space complexity is O(1).
Method 3: Using ffmpeg via subprocess
It is a straightforward solution that involves creating an FFMpeg command and running it using the subprocess module.
When the command is executed, it initiates the process of downloading a file. If an error occurs, it will be printed to the console.
Decision Tree Diagram

This approach requires the “ffmpeg” library installed on your machine. I am using MacOS, so I can download it if I have not by using the command below:
brew install ffmpeg
If you are using Windows or Linux, visit its website and download the software.
Code example
import subprocess
import os
# Creating a custom function
def download_video_ffmpeg(url, save_path):
try:
# Ensuring the directory exists
os.makedirs(os.path.dirname(save_path), exist_ok=True)
# FFmpeg command to download video
command = [
'ffmpeg',
'-i', url,
'-c', 'copy',
save_path
]
# Running the FFmpeg command
subprocess.run(command, check=True)
print(f"Video downloaded successfully and saved to {save_path}")
# Catching and print the exceptions
except FileNotFoundError as e:
print(
f"Error: {e}. Ensure FFmpeg is installed and available in your PATH.")
except subprocess.CalledProcessError as e:
print(f"Error downloading video: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
# Calling the custom function
video_url = 'https://static.videezy.com/system/resources/previews/000/008/220/original/Triangles_01.mp4'
save_path = './videos/ffmpeg_video.mp4'
download_video_ffmpeg(video_url, save_path)
Output
Here is the video stored locally:
The ffmpeg approach via subprocess is a powerful method that can handle various streaming protocols, formats, and encoding options. It can process large multimedia files.
However, you need to install ffmpeg, and you must be familiar with command-line tools. It can be the slowest approach among the three.
The ffmpeg method is ideal for processing and downloading videos from streaming services.
If you want to stream and perform manipulative tasks, I highly recommend using this approach.
The time complexity is O(n), where n is the size of the video.
The space complexity is O(n) because here we are streaming it in real-time.
Time measurement to execute each method
I ran an experiment to execute all three approaches and record their time. Not surprisingly, the fastest approach is using the “requests” module, and the slowest approach is “ffmpeg via subprocess”.
Here is the bar chart supporting my findings:
As shown in the above bar chart, the “requests” took 0.16 seconds and FFmpeg took 0.47 seconds.
Here, I used the same URL for all methods to ensure accurate results.



