Skip to main content

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.

CI PyPI Python 3.9+

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

Requires Rust and maturin:

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

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyureq-0.1.4-cp39-abi3-win_amd64.whl (1.1 MB view details)

Uploaded CPython 3.9+Windows x86-64

pyureq-0.1.4-cp39-abi3-manylinux_2_38_x86_64.whl (3.5 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.38+ x86-64

pyureq-0.1.4-cp39-abi3-macosx_11_0_arm64.whl (1.1 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

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

Hashes for pyureq-0.1.4-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 a5b72f1d5076899cdbbf3714992ade3499a935f4d01c32c3c5dac5424da8b462
MD5 6fd1dd8aafa5332132419d4a94ba66b4
BLAKE2b-256 ac0e389731442b96a952dc2ea2fdf6d51b18341753a903bf403cff3923b70061

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyureq-0.1.4-cp39-abi3-win_amd64.whl:

Publisher: ci.yml on vuongphu/pyureq

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyureq-0.1.4-cp39-abi3-manylinux_2_38_x86_64.whl.

File metadata

File hashes

Hashes for pyureq-0.1.4-cp39-abi3-manylinux_2_38_x86_64.whl
Algorithm Hash digest
SHA256 be2cf2b8ad5daecf4ce0dfa5a04163409938108d5c1aa5d5ddcfdfebfc650248
MD5 2221762dd0eee172d2bf801db0d9e381
BLAKE2b-256 ff1002334ba897c17229039b6b852b483168a8c9ba4bc78e2454b490972eebc2

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyureq-0.1.4-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyureq-0.1.4-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a0cc09e2140002b00641da76b437b50bef9b0c1952f89b92fb24c87cf53c0ef2
MD5 3c75512405d898fad4d4888561a8fdb2
BLAKE2b-256 620355a3cfb4f645ca75f0203b314a222f83929662d571f5d1bc3200dfae12c4

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page