HTTPX → Niquests Guide¶
This guide shows how to take code written for HTTPX and migrate it to niquests API.
Wherever HTTPX differs, you’ll find the Niquests equivalent here.
Note
That document heavily inspire itself from the HTTPX guide “Requests to HTTPX”. We took it backward as Niquests is a drop-in replacement for Requests.
Redirects¶
HTTPX does not follow redirects by default and requires you to opt in:
# HTTPX: must explicitly follow
response = client.get(url, follow_redirects=True)
In Niquests, redirects are followed by default. To disable them (i.e., mimic HTTPX default), pass:
# Niquests: disable auto-redirects
response = niquests.get(url, allow_redirects=False)
Or disable on a session:
session = niquests.Session()
session.max_redirects = 0 # will raise if any redirect is received
Client/Session Instances¶
In HTTPX, you use:
client = httpx.Client(**kwargs)
To migrate, swap to Niquests’ Session:
session = niquests.Session(**kwargs)
Any keyword arguments that HTTPX supported on Client may not exist on Session; check the Niquests docs for Session.__init__.
Response URLs¶
HTTPX’s response.url is a URL object.
In Niquests, response.url is already a string, so you can use it directly:
# HTTPX → str(response.url)
# Niquests → response.url (no conversion needed)
print(response.url) # e.g. 'https://www.example.com/path?query=1'
Determining the Next Redirect Request¶
HTTPX exposes response.next_request. In Niquests the attribute is named response.next:
# HTTPX
client = httpx.Client()
req = client.build_request("GET", url)
while req is not None:
resp = client.send(req)
req = resp.next_request
# Niquests
session = niquests.Session()
prepared = niquests.Request("GET", url).prepare()
while prepared is not None:
resp = session.send(prepared, allow_redirects=False)
prepared = resp.next
Raw Request Content vs Form Data¶
HTTPX distinguishes content= for raw bytes/text from data= for form submissions:
# HTTPX
httpx.post(url, content=b"raw bytes")
httpx.post(url, data={"field": "value"})
In Niquests, use:
niquests.post(url, data=b"raw bytes") # raw bytes/text are passed via `data=`
niquests.post(url, data={"field": "value"}) # form-encoded by default
Note: Niquests has no separate content parameter.
File Uploads¶
HTTPX enforces binary-mode file handles. Niquests is more lenient but still requires binary for non-text uploads:
# HTTPX
with open('file.bin', 'rb') as f:
httpx.post(url, files={'file': f})
# Niquests
with open('file.bin', 'rb') as f:
niquests.post(url, files={'file': f})
Content Encoding¶
Both HTTPX and Niquests encodes str bodies as UTF-8 by default:
# HTTPX: content="ñ" → UTF-8
httpx.post(url, content="ñ")
# Niquests: content="ñ" → UTF-8
niquests.post(url, data="ñ")
Status Codes Constants¶
HTTPX provides codes.NOT_FOUND (upper-case) and codes.not_found (lower).
Niquests only provides lower-case:
# HTTPX → codes.NOT_FOUND or codes.not_found
# Niquests → codes.not_found
if response.status_code == niquests.codes.not_found:
...
Streaming Responses¶
HTTPX uses a .stream() context:
with httpx.stream("GET", url) as resp:
for chunk in resp.iter_bytes():
...
In Niquests, pass stream=True to any existing methods and iterate:
with niquests.get(url, stream=True) as resp:
for chunk in resp.iter_content(chunk_size=-1):
...
resp.iter_content()↔︎resp.iter_bytes()resp.iter_lines()exists in both
Timeouts¶
Both HTTPX and Niquests have sensible defaults.
In Niquests, read operation (GET, HEAD, OPTIONS) default to 30s timeout otherwise (POST, DELETE, PUT, …) 120s. Those are very conservative default that you should override any time it suit your needs.
Previously you wrote this to set a global timeout:
with httpx.Client(timeout=httpx.Timeout(connect=10, total=60)):
...
You may now achieve the same using:
with niquests.Session(timeout=niquests.TimeoutConfiguration(connect=10, total=60)) as s:
...
Note
Instead of a TimeoutConfiguration you may pass a simple integer or float instead.
Proxies / Mounts¶
HTTPX uses mounts={...} with full URL schemes:
httpx.Client(mounts={'http://': transport, 'https://': transport})
In Niquests, you use a proxies dict:
session = niquests.Session()
session.proxies.update({'http': 'http://proxy.example', 'https': 'https://proxy.example'})
# or per request:
niquests.get(url, proxies={'http': '...', 'https': '...'})
SSL Configuration¶
HTTPX requires SSL settings on the client:
client = httpx.Client(verify='/path/to/ca.pem')
Niquests allows SSL args per-request or on a Session:
# per request
niquests.get(url, verify='/path/to/ca.pem')
# or on Session
session = niquests.Session()
session.verify = '/path/to/ca.pem'
Request Bodies on “Body-less” Methods¶
HTTPX disallows content on methods like .get(), recommending .request():
# HTTPX: must use .request()
httpx.request("DELETE", url, content=b"data")
Niquests lets you pass a body directly:
# Niquests: delete with body
niquests.delete(url, data=b"data")
# or use .request()
niquests.request("DELETE", url, data=b"data")
Warning
Passing bodies through DELETE, GET or HEAD is not recommended.
Success Checks¶
HTTPX uses response.is_success; Niquests has response.ok:
# HTTPX
if response.is_success:
...
# Niquests
if response.ok:
...
Note: Niquests’ ok is equivalent to checking 200 <= status_code < 400.
Prepared Requests¶
HTTPX’s Client.build_request replaces Niquests’ Request.prepare():
# HTTPX
req = client.build_request("GET", url)
# Niquests
req = niquests.Request("GET", url)
prepared = session.prepare_request(req)
HTTP/2¶
HTTPX disable HTTP/2 by default and requires you to install an extra dependency to make it work. Whereas Niquests enable HTTP/2 AND HTTP/3 by default.
Note
HTTPX don’t support HTTP/3 by any official ways.
To mimic HTTPX default behavior:
client = httpx.Client(http2=False) # default value
Do:
session = niquests.Session(disable_http2=True, disable_http3=True)
With this, Niquests will ever only establish good old HTTP/1.1 requests.
Async¶
As HTTPX, Niquests does mirror its sync interfaces to async.
For example:
session = niquests.Session()
Becomes:
session = niquests.AsyncSession()
And:
resp = niquests.get(...)
Transforms to:
resp = await niquests.aget(...)
Mocking & Testing¶
HTTPX: RESPX (https://github.com/lundberg/respx)
Niquests: responses (https://github.com/getsentry/responses) or requests-mock (https://requests-mock.readthedocs.io)
Note
See the migration guide for responses or requests-mock in extensions.
Caching¶
HTTPX: Hishel (https://hishel.com)
Niquests: cachecontrol (https://github.com/psf/cachecontrol) or requests-cache (https://github.com/requests-cache/requests-cache)
Note
See the migration guide for cachecontrol or requests-cache in extensions.
Networking Layer¶
HTTPX: uses HTTPCore under the hood
Niquests: built atop urllib3-future (fork of known urllib3)
Query Parameters & Form Data¶
HTTPX requires explicit lists in dicts; it does not accept lists of tuples or omit None values. Niquests supports both:
# HTTPX: httpx.get(..., params={'a': ['1','2'], 'b': None})
# Niquests equivalent:
niquests.get(url, params=[('a','1'), ('a','2'), ('b', '')])
# or omit None:
niquests.get(url, params={'a':['1','2']})
Event Hooks¶
HTTPX event hooks can observe but not mutate
Niquests hooks can mutate both
RequestandResponse.
# Niquests example: log each request
def print_url(r, *args, **kwargs):
print("URL:", r.url)
session = niquests.Session()
session.hooks['response'] = [print_url]
Whenever you see an HTTPX-specific parameter or method, look for its closest Niquests counterpart as shown above. Happy migrating!
ASGI/WSGI Testing¶
New in version 3.17.0.
You can do ASGI or WSGI testing using Niquests.
In HTTPX you wrote something like:
from flask import Flask
import httpx
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
transport = httpx.WSGITransport(app=app)
with httpx.Client(transport=transport, base_url="http://testserver") as client:
r = client.get("/")
assert r.status_code == 200
assert r.text == "Hello World!"
Now you can easily achieve the same with Niquests:
from flask import Flask
import niquests
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
with niquests.Session(app=app) as s:
r = s.get("/")
assert r.status_code == 200
assert r.text == "Hello World!"
Note
The same goes for ASGI testing, but instead of using Session, you’ll use AsyncSession instead.
Warning
ASGI lifespan startup/shutdown is not handled by Niquests (neither does httpx). You’ll use something like asgi-lifespan (https://github.com/florimondmanca/asgi-lifespan#usage) to handle that part.
With FastAPI you could have been used to:
from fastapi import TestClient
client = TestClient(app)
response = client.get("/")
You can do so with Niquests also!
from niquests import Session
client = Session(app=app)
response = client.get("/")
Note
TestClient exposed in FastAPI is actually starlette.TestClient. Starlette is using httpx deep under the hood as an optional dependency.
Warning
Like starlette.TestClient we are defeating the purpose of true async with threading. It’s there for convenience only, we recommend you to leverage async/await as a best practice.
Warning
The synchronous Session handle lifespan startup/shutdown events opposed to the pure asynchronous implementation. Also, for convenience.