urllib3

raw JSON →
2.6.3 verified Tue May 12 auth: no python install: verified quickstart: verified

urllib3 is a powerful, user-friendly HTTP client library for Python providing thread-safe connection pooling, client-side TLS/SSL verification, multipart file uploads, retry helpers, redirect handling, and support for gzip, deflate, brotli, and zstd content encoding. Current stable version is 2.6.3 (released 2025). The project follows an active release cadence with security patches, minor feature releases, and occasional major versions; the 2.x line requires Python >=3.9 and OpenSSL >=1.1.1.

pip install urllib3
error Max retries exceeded with url
cause The request failed multiple times (due to connection errors, timeouts, or server issues) and exhausted the configured number of retries.
fix
Increase the number of retries or adjust the timeout settings for your PoolManager, and ensure the target server is accessible and responsive.
import urllib3

http = urllib3.PoolManager(
    retries=urllib3.Retry(total=5, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]),
    timeout=urllib3.Timeout(connect=2.0, read=5.0)
)
try:
    r = http.request('GET', 'http://example.com/resource', retries=3)
    print(r.status)
except urllib3.exceptions.MaxRetryError as e:
    print(f"Request failed after max retries: {e}")
error CERTIFICATE_VERIFY_FAILED
cause urllib3 failed to verify the SSL certificate of the remote host, often due to missing or outdated CA certificates, an untrusted self-signed certificate, or a hostname mismatch.
fix
Ensure certifi is installed and up-to-date (pip install --upgrade certifi), and your PoolManager is configured to use it, or provide a custom CA bundle if necessary.
import urllib3
import certifi

http = urllib3.PoolManager(
    cert_reqs='CERT_REQUIRED',
    ca_certs=certifi.where() # Ensures the latest CA certificates are used
)
try:
    r = http.request('GET', 'https://secure.example.com')
    print(r.status)
except urllib3.exceptions.SSLError as e:
    print(f"SSL Certificate Error: {e}")
error Failed to establish a new connection
cause urllib3 was unable to establish a new TCP connection to the specified host and port, often due to the server being down, incorrect address, or network/firewall issues.
fix
Verify that the target host and port are correct, the server is running and accessible, and there are no network or firewall restrictions blocking the connection.
import urllib3
import socket

http = urllib3.PoolManager()
try:
    r = http.request('GET', 'http://invalid-host-or-port.com:9999') # Correct this URL
    print(r.status)
except (urllib3.exceptions.NewConnectionError, socket.gaierror) as e:
    print(f"Connection establishment failed: {e}")
    print("Please check the hostname, port, server status, and network connectivity.")
error ModuleNotFoundError: No module named 'urllib3'
cause The `urllib3` package is not installed in the Python environment currently being used or is not accessible in the `PYTHONPATH`.
fix
Install the urllib3 library using pip in your active Python environment.
pip install urllib3
breaking urllib3 v2.0 dropped Python 2.7 and 3.5–3.8 support and requires OpenSSL >=1.1.1. Environments compiled against older OpenSSL (e.g. AWS Lambda Python 3.9 runtime, Amazon Linux 2) will raise an ImportError or NotOpenSSLWarning on import.
fix Upgrade the Python runtime (Lambda: use Python 3.10+), or pin urllib3<2 if the runtime cannot be changed.
breaking VerifiedHTTPSConnection, DEFAULT_CIPHERS, and several other symbols moved or were deleted in v2.0. Importing them from their old v1.x locations (e.g. urllib3.connectionpool.VerifiedHTTPSConnection, urllib3.util.ssl_.DEFAULT_CIPHERS) raises AttributeError/ImportError.
fix Import VerifiedHTTPSConnection from urllib3.connection; remove all references to DEFAULT_CIPHERS (urllib3 now uses the system cipher list).
breaking The ssl_version parameter (used to pin a specific TLS protocol version) was deprecated in 2.0 and removed as of v2.6.0. Passing it now raises a TypeError.
fix Replace ssl_version=ssl.PROTOCOL_TLSv1_2 with ssl_minimum_version=ssl.TLSVersion.TLSv1_2 when constructing an SSLContext or calling create_urllib3_context().
breaking v2.0 dropped commonName certificate hostname verification; only subjectAltName is now accepted. Self-signed or legacy certs that only set CN (and not SAN) will fail TLS verification.
fix Reissue certificates with a proper subjectAltName extension, or set cert_reqs='CERT_NONE' only for internal/test environments (never production).
gotcha The module-level urllib3.request() function uses a hidden global PoolManager. In libraries or multi-threaded services, calling it shares cookies, connections, and retry state across all callers in the same process.
fix Instantiate an explicit urllib3.PoolManager() and keep it as a long-lived object; pass it around rather than relying on the global shortcut.
gotcha Responses with preload_content=True (the default) read the entire body into memory before returning. For large or streaming responses this can exhaust memory silently.
fix Pass preload_content=False to request(), then iterate with resp.stream(chunk_size) and always call resp.release_conn() when done.
gotcha CVE-2026-21441 (GHSA-38jv-5279-wg99, CVSS 8.9): decompression-bomb safeguards in the streaming API were bypassed when HTTP redirects were followed. Fixed in 2.6.3.
fix Upgrade to urllib3>=2.6.3 immediately.
pip install urllib3[brotli]
pip install urllib3[zstd]
pip install urllib3[socks]
python os / libc variant status wheel install import disk
3.10 alpine (musl) urllib3 - - 0.20s 18.7M
3.10 alpine (musl) brotli - - 0.21s 22.5M
3.10 alpine (musl) socks - - 0.20s 18.8M
3.10 alpine (musl) zstd - - 0.20s 20.4M
3.10 slim (glibc) urllib3 - - 0.14s 19M
3.10 slim (glibc) brotli - - 0.14s 24M
3.10 slim (glibc) socks - - 0.14s 19M
3.10 slim (glibc) zstd - - 0.14s 21M
3.11 alpine (musl) urllib3 - - 0.31s 20.7M
3.11 alpine (musl) brotli - - 0.32s 24.4M
3.11 alpine (musl) socks - - 0.31s 20.8M
3.11 alpine (musl) zstd - - 0.31s 22.5M
3.11 slim (glibc) urllib3 - - 0.26s 21M
3.11 slim (glibc) brotli - - 0.26s 26M
3.11 slim (glibc) socks - - 0.24s 21M
3.11 slim (glibc) zstd - - 0.25s 23M
3.12 alpine (musl) urllib3 - - 0.27s 12.5M
3.12 alpine (musl) brotli - - 0.27s 16.3M
3.12 alpine (musl) socks - - 0.27s 12.6M
3.12 alpine (musl) zstd - - 0.27s 14.3M
3.12 slim (glibc) urllib3 - - 0.26s 13M
3.12 slim (glibc) brotli - - 0.27s 18M
3.12 slim (glibc) socks - - 0.28s 13M
3.12 slim (glibc) zstd - - 0.27s 15M
3.13 alpine (musl) urllib3 - - 0.23s 12.2M
3.13 alpine (musl) brotli - - 0.23s 15.9M
3.13 alpine (musl) socks - - 0.23s 12.3M
3.13 alpine (musl) zstd - - 0.24s 13.9M
3.13 slim (glibc) urllib3 - - 0.24s 13M
3.13 slim (glibc) brotli - - 0.24s 18M
3.13 slim (glibc) socks - - 0.24s 13M
3.13 slim (glibc) zstd - - 0.25s 14M
3.9 alpine (musl) urllib3 - - 0.17s 18.2M
3.9 alpine (musl) brotli - - 0.17s 21.9M
3.9 alpine (musl) socks - - 0.17s 18.3M
3.9 alpine (musl) zstd - - 0.18s 19.8M
3.9 slim (glibc) urllib3 - - 0.15s 19M
3.9 slim (glibc) brotli - - 0.14s 24M
3.9 slim (glibc) socks - - 0.14s 19M
3.9 slim (glibc) zstd - - 0.15s 20M

Create an explicit PoolManager and make GET/POST requests with timeout and retry logic.

import urllib3
from urllib3.util.retry import Retry
from urllib3.util.timeout import Timeout

# Explicit PoolManager — preferred over the module-level urllib3.request() in
# library code to avoid shared global state.
http = urllib3.PoolManager(
    timeout=Timeout(connect=3.0, read=10.0),
    retries=Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]),
)

# Simple GET — response body is bytes; call .json() for JSON payloads
resp = http.request("GET", "https://httpbin.org/get")
print(resp.status)          # 200
print(resp.json())          # parsed JSON dict

# POST with form fields
resp = http.request("POST", "https://httpbin.org/post", fields={"key": "value"})
print(resp.status)

# POST with JSON body (sets Content-Type: application/json automatically)
resp = http.request("POST", "https://httpbin.org/post", json={"key": "value"})
print(resp.json()["json"])  # echoed back by httpbin

# Streaming a large response
resp = http.request("GET", "https://httpbin.org/stream-bytes/1024", preload_content=False)
for chunk in resp.stream(32):
    print(len(chunk), "bytes")
resp.release_conn()