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.3-cp39-abi3-win_amd64.whl (1.1 MB view details)

Uploaded CPython 3.9+Windows x86-64

pyureq-0.1.3-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.3-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.3-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: pyureq-0.1.3-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.3-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 aac0b4395837fde4849351d50a3ebfc2883b0c2bde36f9f6329a80063b40e456
MD5 62a0d6838043f20c3f845ec099866ca7
BLAKE2b-256 01685ff7f4437e5ead5647c26a1b4db976d5d9caeca554bc8639335ec22001b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyureq-0.1.3-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.3-cp39-abi3-manylinux_2_38_x86_64.whl.

File metadata

File hashes

Hashes for pyureq-0.1.3-cp39-abi3-manylinux_2_38_x86_64.whl
Algorithm Hash digest
SHA256 ac0024db56631104339275c63da1e2ce06f36f23334cb51401737d206995cc10
MD5 f6d1897541875af59562b62f8d15a9ba
BLAKE2b-256 97de73efe8432a7dd1b903c47670a44b00a49d74b0d27961799b3e62887fa93d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyureq-0.1.3-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.3-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyureq-0.1.3-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 fc596dc1f2dfaa6bd9abc31bb71ef844d7c215ebd7a78b827e381506960d7ec4
MD5 aa5f13cdbde48e73b1cb68172e7d0b58
BLAKE2b-256 44f6d2b8e0cbf84025cdafb05aa9e7b20efbeff29d868c28b75d8559f99d291a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyureq-0.1.3-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