Skip to content

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:

HTTP Protocol Evolution Timeline
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:

migration_example.py
# Before
import requests
# After
import niquests as requests
# Everything else stays the same
response = 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 vs HTTP/2 Request Handling
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 connection

Multiplexing 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 vs QUIC Packet Loss Handling
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 blocking

This 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:

benchmark.py
import time
import requests as old_requests
import 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 client
old_time = benchmark(old_requests, "https://httpbin.org/get")
print(f"Requests (HTTP/1.1): {old_time:.2f}s")
# HTTP/2+ client
new_time = benchmark(requests, "https://httpbin.org/get")
print(f"Niquests (HTTP/2+): {new_time:.2f}s")

Results on a typical run:

Benchmark Results
Requests (HTTP/1.1): 45.32s for 100 requests
Niquests (HTTP/2): 32.18s for 100 requests
Improvement: 29% faster

Your mileage will vary based on network conditions, but multiplexing provides consistent benefits when making multiple requests to the same host.

Feature Comparison

FeatureRequestsNiquestshttpx
HTTP/1.1YesYesYes
HTTP/2NoYesYes
HTTP/3NoYesNo
Drop-in replacementN/AYesNo (different API)
Sync APIYesYesYes
Async APINoNoYes
Connection poolingYesYesYes
Session managementYesYesYes
Mocking supportresponses, betamaxresponses, betamaxpytest-httpx
Active developmentMaintenance onlyActiveActive

Why Not httpx?

httpx is another excellent alternative, but it’s not a drop-in replacement. You’d need to rewrite your code:

httpx_migration.py
# Requests/Niquests style
response = requests.get(url, timeout=30)
# httpx style
import httpx
response = 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

Terminal window
pip install niquests

Niquests has minimal dependencies and doesn’t conflict with Requests, so you can install both side-by-side during migration.

Step 2: Update Imports

before_import.py
# Old code
import requests
from requests.adapters import HTTPAdapter
after_import.py
# New code
import niquests as requests
from niquests.adapters import HTTPAdapter

Step 3: Test

Run your test suite. Most tests should pass without modification.

Step 4: Handle Edge Cases

Some edge cases might need attention:

edge_cases.py
# Timeout behavior is slightly different
# Niquests uses a more aggressive default
# Explicitly set timeouts
response = requests.get(url, timeout=(3.05, 27))
# For streaming, ensure proper resource cleanup
with 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:

Terminal window
pip uninstall requests
# Or update requirements.txt to replace requests with niquests

Compatibility with Existing Tooling

One concern I had was mocking. We use the responses library for mocking HTTP calls in tests:

mocking_test.py
import responses
import niquests as requests
@responses.activate
def 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:

  1. Your project is in maintenance mode - No need to change what works
  2. You’re on a restricted network - HTTP/2 might not be supported by your proxy
  3. You need maximum stability - Requests has been battle-tested for over a decade
  4. You’re using exotic features - Some edge cases might differ

When to Switch to Niquests

Switch to Niquests if:

  1. Starting a new project - Get modern HTTP from day one
  2. Making many parallel requests - Multiplexing helps significantly
  3. Targeting mobile users - HTTP/3 handles packet loss better
  4. 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:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments