Skip to content

How to Migrate from Requests to Niquests in Python

I wanted HTTP/2 and HTTP/3 support in my Python application without rewriting my entire codebase. The solution turned out to be remarkably simple: migrate from Requests to Niquests.

The One-Line Migration

I discovered that migrating from Requests to Niquests requires exactly one line change in most cases:

migrate_import.py
# Before
import requests
# After
import niquests as requests

That’s it. Everything else continued working as expected.

Why I Chose to Migrate

My application was making thousands of HTTP requests daily. I needed better performance and modern protocol support. Niquests offers:

  • HTTP/2 and HTTP/3 enabled by default
  • Full backward compatibility with the Requests API
  • Connection pooling improvements
  • Better async support

I didn’t want to rewrite hundreds of API calls across my codebase. Niquests provided the perfect solution.

Session-Based Usage

I use sessions extensively in my applications for connection pooling and cookie persistence. The migration was seamless:

session_example.py
import niquests as requests
# Session usage works identically
session = requests.Session()
session.auth = ('user', 'pass')
session.headers.update({'X-Custom': 'Header'})
response = session.get('https://api.example.com/data')
print(response.json())

My existing session-based code worked without any modifications.

Authentication and Security Features

All authentication flows I relied on continued working:

auth_example.py
import niquests as requests
# Basic auth
response = requests.get(
'https://api.example.com/protected',
auth=('username', 'password')
)
# .netrc file support works automatically
session = requests.Session()
response = session.get('https://api.internal.service')

The .netrc file support was essential for my internal API integrations. Niquests reads the same configuration files.

Cookie handling remained consistent across the migration:

cookie_example.py
import niquests as requests
session = requests.Session()
# First request sets cookies
session.get('https://example.com/login')
# Subsequent requests include cookies automatically
response = session.get('https://example.com/dashboard')

My web scraping tools that depend on session cookies continued functioning without any changes.

Third-Party Library Integration

I was concerned about compatibility with my testing and mocking libraries. The good news: they all work.

Testing with requests-mock

test_requests_mock.py
import niquests as requests
import requests_mock
def test_api_call():
with requests_mock.Mocker() as m:
m.get('https://api.example.com/data', json={'status': 'ok'})
response = requests.get('https://api.example.com/data')
assert response.json() == {'status': 'ok'}

Mocking with responses

test_responses.py
import niquests as requests
import responses
@responses.activate
def test_external_api():
responses.add(
responses.GET,
'https://external.service/items',
json={'items': [1, 2, 3]},
status=200
)
response = requests.get('https://external.service/items')
assert len(response.json()['items']) == 3

Recording with betamax

test_betamax.py
import niquests as requests
from betamax import Betamax
session = requests.Session()
recorder = Betamax(session)
with recorder.use_cassette('api_interaction'):
response = session.get('https://api.example.com/records')

Caching with requests-cache

cache_example.py
import niquests as requests
import requests_cache
# Enable caching
requests_cache.install_cache(
'my_cache',
backend='sqlite',
expire_after=3600
)
# First call hits the network
response1 = requests.get('https://api.example.com/expensive')
# Second call uses cache
response2 = requests.get('https://api.example.com/expensive')

All my existing test suites passed without modifications.

Library Maintainer Approach

I maintain a library that uses Requests internally. For library maintainers, I recommend a different approach:

library_module.py
# In your library, allow users to inject their HTTP client
def make_request(url, http_client=None):
if http_client is None:
import niquests as http_client
return http_client.get(url)

This approach lets users choose their HTTP client while providing Niquests as the default.

Migration Steps I Followed

I followed this systematic approach:

  1. Updated my requirements.txt:
requirements.txt
# requests>=2.28.0 # Removed
niquests>=3.5.0 # Added
  1. Searched for all import statements:
search_imports.sh
grep -r "import requests" --include="*.py" .
grep -r "from requests" --include="*.py" .
  1. Replaced the imports globally:
updated_imports.py
# Old imports
# import requests
# from requests.exceptions import HTTPError
# New imports
import niquests as requests
from niquests.exceptions import HTTPError
  1. Ran my full test suite. All tests passed on the first try.

Performance Improvements I Observed

After migrating, I noticed improvements in my production environment:

performance_metrics.txt
Before (Requests with HTTP/1.1):
- Average response time: 245ms
- Connection setup: 85ms per request
- Throughput: 120 requests/second
After (Niquests with HTTP/2):
- Average response time: 180ms
- Connection setup: 20ms (multiplexed)
- Throughput: 340 requests/second

The HTTP/2 multiplexing significantly reduced connection overhead for applications making multiple requests to the same host.

Edge Cases I Encountered

A few edge cases required attention:

Custom Adapters

If you use custom transport adapters, you’ll need to update them:

custom_adapter.py
import niquests as requests
from niquests.adapters import HTTPAdapter
class RetryAdapter(HTTPAdapter):
def send(self, request, **kwargs):
# Custom retry logic
return super().send(request, **kwargs)
session = requests.Session()
session.mount('https://', RetryAdapter())

Timeout Behavior

Timeout handling is compatible but HTTP/2 multiplexing can affect timing:

timeout_example.py
import niquests as requests
# Connect and read timeouts work identically
response = requests.get(
'https://api.example.com/slow',
timeout=(3.05, 30) # (connect, read)
)

Verification Checklist

I used this checklist to verify my migration:

  • All imports changed to import niquests as requests
  • Exception imports updated (e.g., from niquests.exceptions import ...)
  • Requirements updated in all environments
  • Test suite runs green
  • Integration tests pass
  • Performance metrics collected
  • Documentation updated

Conclusion

Migrating from Requests to Niquests gave me modern HTTP protocol support with minimal effort. The one-line import change preserved my entire codebase while providing performance benefits.

The full backward compatibility with Requests API, third-party libraries, and existing authentication flows made this one of the smoothest migrations I’ve experienced.

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