Why is Python Requests Library Frozen and What's the Best Alternative?
Problem
I’ve been using Python’s requests library for years. It’s practically the default choice for HTTP calls in Python - over 40 million downloads per month. But recently I discovered something that changed my perspective: Requests is officially in “perpetual feature freeze.”
What does that mean? No new features. Ever. The maintainers will only fix bugs and address security issues. While HTTP/2 and HTTP/3 are becoming standard across the web, Requests will never support them.
This matters because modern APIs increasingly use HTTP/2 for multiplexing and HTTP/3 for better performance over unreliable networks. If you’re building a new project today, you’re locking yourself into HTTP/1.1 forever by choosing Requests.
What “Perpetual Feature Freeze” Actually Means
The Requests maintainers made this decision intentionally. The library is stable, widely adopted, and does exactly what it promises. But the web has moved forward:
2015: HTTP/2 standardized (multiplexing, header compression)2022: HTTP/3 standardized (QUIC protocol, no head-of-line blocking)2026: Requests still on HTTP/1.1 (and will stay there forever)This isn’t a criticism of Requests - it’s a mature, well-maintained library. But if you need modern HTTP features, you need to look elsewhere.
Enter Niquests: The Drop-In Replacement
Niquests is a fork of Requests that adds HTTP/2 and HTTP/3 support while maintaining backward compatibility. The migration is literally one line of code:
# Beforeimport requests
# Afterimport niquests as requests
# Everything else stays the sameresponse = requests.get("https://httpbin.org/get")print(response.json())I tested this on several projects, and in most cases, the migration was seamless. Niquests aims for 100% API compatibility with Requests.
Why HTTP/2 and HTTP/3 Matter
HTTP/2 Benefits
HTTP/2 brings several improvements over HTTP/1.1:
HTTP/1.1 (Requests): Request 1 ────────────────────► Response 1 (wait) Request 2 ────────────────────► Response 2 (wait) Request 3 ────────────────────► Response 3 Total: Sequential, head-of-line blocking
HTTP/2 (Niquests): Request 1 ───► Request 2 ───► Responses arrive as ready Request 3 ───► Total: Multiplexed, single connectionMultiplexing means you can send multiple requests over a single TCP connection simultaneously. No more queuing requests behind each other.
Header compression (HPACK) reduces overhead. If you’re making many API calls to the same host, headers like User-Agent, Accept, and Authorization get compressed instead of being sent in full every time.
HTTP/3 Benefits
HTTP/3 uses QUIC instead of TCP, which solves a fundamental problem:
TCP (HTTP/1.1, HTTP/2): Packet 1 ──► ✗ Lost Packet 2 ──► Arrives but must wait Packet 3 ──► Arrives but must wait Result: All subsequent packets blocked (head-of-line blocking)
QUIC (HTTP/3): Stream A Packet 1 ──► ✗ Lost (only blocks Stream A) Stream B Packet 1 ──► Arrives, processed immediately Stream C Packet 1 ──► Arrives, processed immediately Result: Independent streams, no head-of-line blockingThis matters on mobile networks, congested Wi-Fi, or any unreliable connection. One lost packet doesn’t block everything else.
Performance Comparison
I ran some benchmarks comparing Requests and Niquests:
import timeimport requests as old_requestsimport niquests as requests
def benchmark(client, url, n=100): start = time.time() for _ in range(n): response = client.get(url) return time.time() - start
# HTTP/1.1 clientold_time = benchmark(old_requests, "https://httpbin.org/get")print(f"Requests (HTTP/1.1): {old_time:.2f}s")
# HTTP/2+ clientnew_time = benchmark(requests, "https://httpbin.org/get")print(f"Niquests (HTTP/2+): {new_time:.2f}s")Results on a typical run:
Requests (HTTP/1.1): 45.32s for 100 requestsNiquests (HTTP/2): 32.18s for 100 requestsImprovement: 29% fasterYour mileage will vary based on network conditions, but multiplexing provides consistent benefits when making multiple requests to the same host.
Feature Comparison
| Feature | Requests | Niquests | httpx |
|---|---|---|---|
| HTTP/1.1 | Yes | Yes | Yes |
| HTTP/2 | No | Yes | Yes |
| HTTP/3 | No | Yes | No |
| Drop-in replacement | N/A | Yes | No (different API) |
| Sync API | Yes | Yes | Yes |
| Async API | No | No | Yes |
| Connection pooling | Yes | Yes | Yes |
| Session management | Yes | Yes | Yes |
| Mocking support | responses, betamax | responses, betamax | pytest-httpx |
| Active development | Maintenance only | Active | Active |
Why Not httpx?
httpx is another excellent alternative, but it’s not a drop-in replacement. You’d need to rewrite your code:
# Requests/Niquests styleresponse = requests.get(url, timeout=30)
# httpx styleimport httpxresponse = httpx.get(url, timeout=30) # Similar but not identical
# For async (httpx's strength)async with httpx.AsyncClient() as client: response = await client.get(url)If you need async, httpx is the better choice. But for synchronous codebases, Niquests offers the path of least resistance.
Migration Guide
Step 1: Install Niquests
pip install niquestsNiquests has minimal dependencies and doesn’t conflict with Requests, so you can install both side-by-side during migration.
Step 2: Update Imports
# Old codeimport requestsfrom requests.adapters import HTTPAdapter# New codeimport niquests as requestsfrom niquests.adapters import HTTPAdapterStep 3: Test
Run your test suite. Most tests should pass without modification.
Step 4: Handle Edge Cases
Some edge cases might need attention:
# Timeout behavior is slightly different# Niquests uses a more aggressive default
# Explicitly set timeoutsresponse = requests.get(url, timeout=(3.05, 27))
# For streaming, ensure proper resource cleanupwith requests.get(url, stream=True) as response: for chunk in response.iter_content(chunk_size=8192): process(chunk)Step 5: Remove Requests
Once everything works:
pip uninstall requests# Or update requirements.txt to replace requests with niquestsCompatibility with Existing Tooling
One concern I had was mocking. We use the responses library for mocking HTTP calls in tests:
import responsesimport niquests as requests
@responses.activatedef test_api_call(): responses.add( responses.GET, "https://api.example.com/data", json={"status": "ok"}, status=200 )
response = requests.get("https://api.example.com/data") assert response.json() == {"status": "ok"}This works without modification. Niquests is designed to be compatible with the existing Requests ecosystem.
When to Stick with Requests
Requests isn’t dead. It’s stable. Consider staying with Requests if:
- Your project is in maintenance mode - No need to change what works
- You’re on a restricted network - HTTP/2 might not be supported by your proxy
- You need maximum stability - Requests has been battle-tested for over a decade
- You’re using exotic features - Some edge cases might differ
When to Switch to Niquests
Switch to Niquests if:
- Starting a new project - Get modern HTTP from day one
- Making many parallel requests - Multiplexing helps significantly
- Targeting mobile users - HTTP/3 handles packet loss better
- Reducing latency - Every millisecond counts in your use case
Summary
The Python Requests library is stable, mature, and widely trusted. But it’s frozen in time, forever stuck on HTTP/1.1. For new projects or performance-sensitive applications, Niquests offers a compelling upgrade path:
- One-line migration:
import niquests as requests - HTTP/2 and HTTP/3 support: Modern protocols by default
- Performance gains: Multiplexing and better connection handling
- Full ecosystem compatibility: Works with your existing mocks and tools
I’m not saying Requests is bad - it’s served the Python community exceptionally well. But the web has evolved, and our tools should evolve with it. For my new projects, I’m using Niquests.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Niquests GitHub Repository
- 👨💻 Niquests Documentation
- 👨💻 Requests: HTTP for Humans
- 👨💻 HTTP/2 RFC 7540
- 👨💻 QUIC Protocol
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments