Skip to main content

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+ Downloads

Installation

pip install pyureq

Pre-built wheels are available for Linux, macOS, and Windows (Python 3.9–3.14). 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, so repeated calls to the same host reuse a single TCP connection:

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)
proxies dict Proxy URLs keyed by scheme — see Proxies

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

Proxies

Pass a proxies dict keyed by URL scheme, exactly as you would with requests:

PROXIES = {
    "http":  "http://proxy.example.com:8080",
    "https": "http://user:pass@proxy.example.com:8080",
}

r = pyureq.get("https://api.example.com", proxies=PROXIES)

SOCKS proxies are supported too:

PROXIES = {
    "http":  "socks5://proxy.example.com:1080",
    "https": "socks5://proxy.example.com:1080",
}

Use "all" as a catch-all key (same as requests):

pyureq.get(url, proxies={"all": "http://proxy:8080"})

Set proxies session-wide via Session.proxies; per-request proxies= overrides individual schemes:

with pyureq.Session() as s:
    s.proxies = {"https": "http://proxy:8080"}
    s.get("https://api.example.com")                       # uses proxy
    s.get("https://api.example.com", proxies={"https": ""})  # bypass proxy

Pass an empty dict or empty-string values to disable system proxies (HTTP_PROXY / HTTPS_PROXY env vars):

pyureq.get(url, proxies={"http": "", "https": ""})   # no proxy

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.detach()  ← 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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyureq-0.1.7.tar.gz (57.3 kB view details)

Uploaded Source

Built Distributions

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

pyureq-0.1.7-cp39-abi3-win_amd64.whl (565.2 kB view details)

Uploaded CPython 3.9+Windows x86-64

pyureq-0.1.7-cp39-abi3-manylinux_2_38_x86_64.whl (2.9 MB view details)

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

pyureq-0.1.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.9 MB view details)

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

pyureq-0.1.7-cp39-abi3-macosx_11_0_arm64.whl (593.9 kB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

File details

Details for the file pyureq-0.1.7.tar.gz.

File metadata

  • Download URL: pyureq-0.1.7.tar.gz
  • Upload date:
  • Size: 57.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for pyureq-0.1.7.tar.gz
Algorithm Hash digest
SHA256 720586b9309348bfc322571285e72c20dce1c63697d008cba4bd85025c3e866d
MD5 f4d3898bc8ca4a478e86c5f7af07ea9d
BLAKE2b-256 c24c656b5dfaf7382266786f3d789a3a1bc59ae6f8a6b2d179950b4ef241709d

See more details on using hashes here.

File details

Details for the file pyureq-0.1.7-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: pyureq-0.1.7-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 565.2 kB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.0

File hashes

Hashes for pyureq-0.1.7-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 b0e376e9a43bb82daf1504d2d5a3666c41bc3410ceb141247679254b4634e948
MD5 55b8ceeee90845c079fba68bafb38aaf
BLAKE2b-256 a98b6cffadda34062ab8b72c72d0ead47af60d7eb4aba9f693ab87503d889d74

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pyureq-0.1.7-cp39-abi3-manylinux_2_38_x86_64.whl
Algorithm Hash digest
SHA256 3f3e83e01e0494f45016f432f03ad5d58ba33a17c75d4987fd611778adc67e46
MD5 ea408a798a8cb1a8a0c20e41beb43d31
BLAKE2b-256 25e58af4e77b7750a3462cce8434888f1ed9c9cbf81d62a7768978c9f08585ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyureq-0.1.7-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.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyureq-0.1.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 6e3a03e7eb18db700925786b20ecce04d1abd6fcb5369d4b0bb564d8092c9422
MD5 65dcd3fd7cc47f0137602b8fa2610394
BLAKE2b-256 57417748c6e560fb39a8bc8667fdcf501233a1eaaacbb17b67cc17c85a8a182b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pyureq-0.1.7-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e15d8465ba46db5e3b816c422940f0f7403464aa8aec0d3b570e627fafbec4a9
MD5 866a6f072cbfb678ff13f266c76bce25
BLAKE2b-256 2f68d33b4b541c3d7ceb90a948dcc9af4e481f206aa344dafa6df77111578cd4

See more details on using hashes here.

Provenance

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

Release history Release notifications | RSS feed

0.1.8

3 files

This release

0.1.7 This release

5 files

0.1.6

3 files

0.1.5

3 files

0.1.4

3 files

0.1.3

3 files

0.1.2

3 files

0.1.1

3 files

0.1.0

3 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page