Python requests 타임아웃 설정
Why every Python HTTP request needs a timeout and how to set connect, read, and total timeouts correctly using the requests library.
Python requests 타임아웃 설정
Introduction
If you write Python code that makes HTTP requests without setting a timeout, your application is one slow server away from hanging forever. This is one of the most common mistakes in Python networking code, and it can cause cascading failures in production systems.
I learned this lesson while building a cryptocurrency price aggregator in Lisbon. One of the exchange APIs I was querying started responding slowly, and my entire application hung because none of the HTTP requests had timeouts configured. Every coroutine was blocked waiting for a response that might never come.
Environment
Python 3.12.3
requests 2.31.0Problem
The default behavior — no timeout at all:
import requests
# This can hang forever if the server is slow
response = requests.get("https://api.example.com/data")When the server is slow or unresponsive:
# This hangs indefinitely — no timeout set
response = requests.get("https://slow-server.example.com/data")
# Your application is now stuck
# Ctrl+C is the only way outRunning this produces:
$ python no_timeout.py
# (program hangs indefinitely, no output)
# Must be killed with Ctrl+C or kill commandAfter adding a basic timeout:
import requests
try:
response = requests.get("https://api.example.com/data", timeout=5)
except requests.exceptions.Timeout:
print("Request timed out after 5 seconds")But what does "5 seconds" mean? Is it connect timeout? Read timeout? Both?
Analysis
The requests library timeout parameter accepts different formats.
A single number (recommended for most cases):
response = requests.get(url, timeout=5)
# Applies 5 seconds to BOTH connect and readA tuple of (connect, read):
response = requests.get(url, timeout=(3, 10))
# 3 seconds to establish connection
# 10 seconds to read the responseWhy timeouts matter:
- A slow server can hold your connection open indefinitely
- Without timeouts, connection pools fill up and new requests fail
- DNS resolution can hang if the DNS server is unresponsive
- SSL handshake can take forever on problematic connections
The timeout parameter behavior:
timeout=5: 5s for connect AND 5s for read (total ~10s max)timeout=(3, 10): 3s connect, 10s read (total ~13s max)timeout=None: No timeout (default — dangerous!)
Solution
Fix 1: Always set a timeout
import requests
# Simple and effective for most use cases
response = requests.get("https://api.example.com/data", timeout=10)Fix 2: Set different connect and read timeouts
import requests
# Fast connect timeout, longer read timeout
response = requests.get(
"https://api.example.com/data",
timeout=(3.05, 30) # 3.05s connect, 30s read
)Fix 3: Use a session with default timeout
import requests
session = requests.Session()
session.timeout = (5, 30) # Default for all requests in this session
# All requests in this session use the default timeout
response1 = session.get("https://api1.example.com/data")
response2 = session.get("https://api2.example.com/data")
# Override for specific requests
response3 = session.get("https://api3.example.com/data", timeout=60)Fix 4: Retry with exponential backoff
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry_strategy = Retry(
total=3,
backoff_factor=1, # 1s, 2s, 4s
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
session.timeout = (5, 30)
try:
response = session.get("https://api.example.com/data")
response.raise_for_status()
except requests.exceptions.Timeout:
print("Request timed out after all retries")
except requests.exceptions.ConnectionError:
print("Connection failed")
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e}")Fix 5: Timeout for specific scenarios
import requests
# API calls — fast connect, reasonable read
api_response = requests.get(
"https://api.example.com/data",
timeout=(2, 10)
)
# File downloads — fast connect, long read
download_response = requests.get(
"https://example.com/large-file.zip",
timeout=(5, 300), # 5 minutes for download
stream=True
)
# Health checks — very fast
try:
health = requests.get("https://service.example.com/health", timeout=2)
is_healthy = health.status_code == 200
except requests.exceptions.RequestException:
is_healthy = FalseLessons Learned
- Never use
timeout=Nonein production code — it is equivalent to no timeout at all. - Set
timeouton every request — make it a habit, not an afterthought. - Use sessions with default timeouts to avoid repeating timeout configuration.
- Different operations need different timeouts — API calls need short timeouts, file downloads need longer ones.
- Combine timeouts with retries for robust HTTP communication.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.