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:
# Beforeimport requests
# Afterimport niquests as requestsThat’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:
import niquests as requests
# Session usage works identicallysession = 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:
import niquests as requests
# Basic authresponse = requests.get( 'https://api.example.com/protected', auth=('username', 'password'))
# .netrc file support works automaticallysession = 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 Jar Compatibility
Cookie handling remained consistent across the migration:
import niquests as requests
session = requests.Session()
# First request sets cookiessession.get('https://example.com/login')
# Subsequent requests include cookies automaticallyresponse = 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
import niquests as requestsimport 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
import niquests as requestsimport responses
@responses.activatedef 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']) == 3Recording with betamax
import niquests as requestsfrom 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
import niquests as requestsimport requests_cache
# Enable cachingrequests_cache.install_cache( 'my_cache', backend='sqlite', expire_after=3600)
# First call hits the networkresponse1 = requests.get('https://api.example.com/expensive')
# Second call uses cacheresponse2 = 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:
# In your library, allow users to inject their HTTP clientdef 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:
- Updated my requirements.txt:
# requests>=2.28.0 # Removedniquests>=3.5.0 # Added- Searched for all import statements:
grep -r "import requests" --include="*.py" .grep -r "from requests" --include="*.py" .- Replaced the imports globally:
# Old imports# import requests# from requests.exceptions import HTTPError
# New importsimport niquests as requestsfrom niquests.exceptions import HTTPError- 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:
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/secondThe 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:
import niquests as requestsfrom 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:
import niquests as requests
# Connect and read timeouts work identicallyresponse = 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