Requests vs HTTPX in 2026: Measured Differences and Migration Gotchas

Every timing on this page now comes from one local server run by the evidence packet on requests 2.34.2 and httpx 0.28.1. The earlier version's benchmark table, which quoted third-party posts and unsourced figures, is gone. New: connection counting, threads vs asyncio, HTTP/2 on the same server, what "retries" means in each library, the errors a migration hits, and what the httpx 1.0 pre-release does to 0.28 code.
requests and httpx make the same request with almost the same line of code. The differences that matter are elsewhere: whether a connection is reused, how you run 200 requests at once, which protocol you speak, what happens by default on a redirect or a slow server, and what "retry" means. This page measures each of those on one server, then lists what breaks when you move a requests codebase to httpx 0.28.
The short version, from the tables below (Apple Silicon laptop, loopback, Python 3.12.11; the ratios are the claim, not the seconds):
| Case | requests 2.34.2 | httpx 0.28.1 |
|---|---|---|
300 sequential GETs, one Session / Client (plain HTTP) | 0.150 s, 1 connection | 0.126 s, 1 connection |
300 sequential GETs, no Session / Client (plain HTTP) | 0.217 s, 300 connections | 3.090 s, 300 connections (an SSL context is built per call) |
| 200 GETs with a 50 ms server delay, 20 threads | 0.543 s (32 connections; 20 with pool_maxsize=20) | 0.545 s, 20 connections |
Same 200 GETs, AsyncClient + asyncio.gather, Semaphore(20) | not available | 0.558 s, 20 connections |
| Same 200 GETs over TLS, no semaphore (httpx caps in-flight work at 100), HTTP/1.1 vs HTTP/2 | HTTP/1.1 only | 0.476 s, 200 connections vs 0.171 s, 1 connection |
| 50 MiB download, streamed, peak RSS | 35 MiB | 44 MiB |
| Retries against a 503 | urllib3.Retry(status_forcelist=[503]): 4 requests sent | HTTPTransport(retries=3): 1 request (retries cover connection failures only) |
Pick requests when the code is synchronous, the targets speak HTTP/1.1, and you want the larger ecosystem (plugins, mocks, examples). Pick httpx when you are inside asyncio (or an async framework), when you want HTTP/2, or when you want timeouts on by default. On a single reused connection the two are within a third of each other, and the one case where requests came out ahead was the loopback streaming test; nothing here says one is "faster".








