A requests-compatible HTTP library powered by Rust's ureq crate
Project description
pyureq
A requests-compatible Python HTTP library backed by Rust's
ureq crate. Drop-in replacement for
requests — same API, lower overhead, no external runtime dependency.
Installation
pip install pyureq
Pre-built wheels are available for Linux, macOS, and Windows (Python 3.9+). The wheel bundles its own OpenSSL, so no system dependency is required.
Quick start
import pyureq
# Simple GET
r = pyureq.get("https://httpbin.org/get", params={"q": "hello"})
print(r.status_code) # 200
print(r.json())
# POST JSON
r = pyureq.post("https://httpbin.org/post", json={"key": "value"})
print(r.json()["json"])
# POST form data
r = pyureq.post("https://httpbin.org/post", data={"field": "hello"})
print(r.json()["form"])
# With timeout and headers
r = pyureq.get(
"https://httpbin.org/get",
headers={"X-Custom": "header"},
timeout=5,
)
r.raise_for_status()
Alias as requests
Existing code that imports requests can be pointed at pyureq with a
one-line change:
import pyureq as requests # <- only change needed
r = requests.get("https://example.com")
print(r.text)
Sessions
pyureq.Session maps directly to requests.Session and shares the same
connection pool across requests:
import pyureq
with pyureq.Session() as s:
s.headers.update({"Authorization": "Bearer mytoken"})
s.timeout = 10
r = s.get("https://api.example.com/users")
r = s.post("https://api.example.com/items", json={"name": "widget"})
API reference
Top-level functions
| Function | Description |
|---|---|
pyureq.get(url, **kwargs) |
GET |
pyureq.post(url, data=None, json=None, **kwargs) |
POST |
pyureq.put(url, data=None, **kwargs) |
PUT |
pyureq.patch(url, data=None, **kwargs) |
PATCH |
pyureq.delete(url, **kwargs) |
DELETE |
pyureq.head(url, **kwargs) |
HEAD |
pyureq.options(url, **kwargs) |
OPTIONS |
pyureq.request(method, url, **kwargs) |
Any method |
pyureq.session() |
Create a Session |
Common keyword arguments
| Argument | Type | Description |
|---|---|---|
params |
dict or list[tuple] |
Query string parameters |
data |
dict, str, or bytes |
Form-encoded or raw request body |
json |
any JSON-serializable | JSON request body |
headers |
dict |
Extra HTTP headers |
cookies |
dict |
Cookies to send |
auth |
(user, password) |
HTTP Basic Auth |
timeout |
float |
Seconds before timing out |
allow_redirects |
bool |
Follow redirects (default True) |
verify |
bool |
Verify TLS certificate (default True) |
Response attributes and methods
| Name | Description |
|---|---|
.status_code |
Integer status (e.g. 200) |
.headers |
Case-insensitive response headers |
.content |
Response body as bytes |
.text |
Response body decoded to str |
.encoding |
Encoding used for .text (readable/writable) |
.url |
Final URL after redirects |
.ok |
True if status < 400 |
.elapsed |
datetime.timedelta of round-trip time |
.json(**kwargs) |
Parse body as JSON |
.raise_for_status() |
Raise HTTPError on 4xx/5xx |
Exceptions
All exceptions live under pyureq.exceptions and mirror the requests
exception hierarchy:
RequestException
├── ConnectionError
│ └── ProxyError
│ └── SSLError
├── HTTPError
├── URLRequired
├── TooManyRedirects
├── Timeout
│ ├── ConnectTimeout
│ └── ReadTimeout
└── ...
import pyureq
from pyureq.exceptions import Timeout, HTTPError
try:
r = pyureq.get("https://example.com", timeout=2)
r.raise_for_status()
except Timeout:
print("Request timed out")
except HTTPError as e:
print(f"HTTP error: {e.response.status_code}")
Building from source
pip install maturin
maturin build --release
pip install target/wheels/*.whl
For a development install (rebuild-on-change):
pip install maturin
maturin develop
Running tests
pip install pytest flask
pytest tests/ -v
Benchmarks
All benchmarks run against a local server to eliminate network jitter.
Single-thread latency
Mean latency (ms) per request — lower is better.
Scenario pyureq requests curl_cffi httpx†
──────────────────────────────────────────────────────────────
GET /get 1.58 2.59 2.46 2.39
GET /get (params) 1.45 2.68 2.36 2.37
POST JSON 1.56 2.74 2.16 2.39
POST form 1.68 3.33 2.17 2.17
GET /status/200 1.24 2.43 2.03 1.85
GET /headers 1.44 2.72 2.37 2.37
──────────────────────────────────────────────────────────────
Overall mean 1.49 2.75 2.26 2.26
×1.0 ×1.85 slower ×1.52 slower ×1.52 slower
†httpx measured with Client() session; httpx.get() stateless has ~68 ms
per-call overhead (creates/destroys a full transport each time).
Concurrency — 1 000 simultaneous threads
Each thread makes 5 sequential requests (5 000 total). Higher req/s and lower p99 latency is better; fewer errors is better.
Mode Library req/s p99 ms errors
──────────────────────────────────────────────────
stateless pyureq 268 7 242 26 / 5000 ← fewest errors
curl_cffi 226 10 039 457 / 5000
requests 203 15 248 404 / 5000
session pyureq 261 9 206 46 / 5000
curl_cffi 232 10 038 450 / 5000
requests 244 10 015 327 / 5000
At 1 000 threads pyureq delivers 19 % more throughput and 17× fewer timeouts than curl_cffi in stateless mode. The advantage comes from lower Python-layer overhead per request: because pyureq's response parsing happens entirely in Rust, it holds the GIL for a shorter window (~0.3 ms vs ~0.8 ms), reducing GIL pile-up under heavy concurrency.
GIL behaviour
All four libraries release the GIL during network I/O — background Python threads can run while requests are in flight. The difference is how much Python overhead each library runs with the GIL held before and after each I/O call.
Library GIL held per request (approx)
──────────────────────────────────────────
pyureq ~0.3 ms (parsing in Rust)
curl_cffi ~0.8 ms (CFFI + Python result handling)
requests ~1.2 ms (urllib3 pure-Python stack)
Run the benchmarks yourself:
pip install requests httpx curl_cffi
python benchmarks/bench.py --iterations 500 # latency
python benchmarks/bench_threads.py --requests 5 # concurrency
python benchmarks/bench_gil.py --iterations 200 # GIL behaviour
How it works
Python call
│
▼
pyureq Python layer ← thin: arg normalisation, exception mapping
│
│ py.allow_threads() ← GIL released here
▼
Rust / ureq ← HTTP request + response parsing, all in Rust
│
▼
native-tls ← system TLS on macOS/Windows, bundled OpenSSL on Linux
│
▼
TCP socket
The GIL is released for the entire Rust call — connection, TLS handshake,
send, recv, and response parsing all happen without blocking other Python
threads. The GIL is re-acquired only to build the final Python Response
object.
The Rust core uses ureq, a synchronous (no async runtime) HTTP/1.1 client built on Rust's standard blocking I/O.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyureq-0.1.4-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: pyureq-0.1.4-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5b72f1d5076899cdbbf3714992ade3499a935f4d01c32c3c5dac5424da8b462
|
|
| MD5 |
6fd1dd8aafa5332132419d4a94ba66b4
|
|
| BLAKE2b-256 |
ac0e389731442b96a952dc2ea2fdf6d51b18341753a903bf403cff3923b70061
|
Provenance
The following attestation bundles were made for pyureq-0.1.4-cp39-abi3-win_amd64.whl:
Publisher:
ci.yml on vuongphu/pyureq
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyureq-0.1.4-cp39-abi3-win_amd64.whl -
Subject digest:
a5b72f1d5076899cdbbf3714992ade3499a935f4d01c32c3c5dac5424da8b462 - Sigstore transparency entry: 1004897362
- Sigstore integration time:
-
Permalink:
vuongphu/pyureq@d968ba57bd3d634c3a4b9b67c35abc801a450224 -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/vuongphu
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d968ba57bd3d634c3a4b9b67c35abc801a450224 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyureq-0.1.4-cp39-abi3-manylinux_2_38_x86_64.whl.
File metadata
- Download URL: pyureq-0.1.4-cp39-abi3-manylinux_2_38_x86_64.whl
- Upload date:
- Size: 3.5 MB
- Tags: CPython 3.9+, manylinux: glibc 2.38+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be2cf2b8ad5daecf4ce0dfa5a04163409938108d5c1aa5d5ddcfdfebfc650248
|
|
| MD5 |
2221762dd0eee172d2bf801db0d9e381
|
|
| BLAKE2b-256 |
ff1002334ba897c17229039b6b852b483168a8c9ba4bc78e2454b490972eebc2
|
Provenance
The following attestation bundles were made for pyureq-0.1.4-cp39-abi3-manylinux_2_38_x86_64.whl:
Publisher:
ci.yml on vuongphu/pyureq
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyureq-0.1.4-cp39-abi3-manylinux_2_38_x86_64.whl -
Subject digest:
be2cf2b8ad5daecf4ce0dfa5a04163409938108d5c1aa5d5ddcfdfebfc650248 - Sigstore transparency entry: 1004897361
- Sigstore integration time:
-
Permalink:
vuongphu/pyureq@d968ba57bd3d634c3a4b9b67c35abc801a450224 -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/vuongphu
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d968ba57bd3d634c3a4b9b67c35abc801a450224 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyureq-0.1.4-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: pyureq-0.1.4-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0cc09e2140002b00641da76b437b50bef9b0c1952f89b92fb24c87cf53c0ef2
|
|
| MD5 |
3c75512405d898fad4d4888561a8fdb2
|
|
| BLAKE2b-256 |
620355a3cfb4f645ca75f0203b314a222f83929662d571f5d1bc3200dfae12c4
|
Provenance
The following attestation bundles were made for pyureq-0.1.4-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
ci.yml on vuongphu/pyureq
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyureq-0.1.4-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
a0cc09e2140002b00641da76b437b50bef9b0c1952f89b92fb24c87cf53c0ef2 - Sigstore transparency entry: 1004897357
- Sigstore integration time:
-
Permalink:
vuongphu/pyureq@d968ba57bd3d634c3a4b9b67c35abc801a450224 -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/vuongphu
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d968ba57bd3d634c3a4b9b67c35abc801a450224 -
Trigger Event:
push
-
Statement type: