NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1244 most downloaded on PyPI
HTTP client that can impersonate web browsers
Last release 22 days ago
13 Sep 2026
Ships unpredictably
gaps range from 9 days to 10 months
Nearly every release is documented
notes for 31 of 31 stable releases
3 versions withdrawn
withdrawn after publishing
2 years old
34 releases · first in 2024
Release v2.0.1: hardening across TLS/H2/proxy/DNS/client + Chrome 153 / Edge 152-153 profiles by @deedy5 in #161
Full Changelog: v2.0.0...v2.0.1
feat!: v2.0.0 — native primp crate (workspace restructure) by @deedy5 in #160
Major release v2.0.0 — main → dev — 367 files +32k / -73k.
Former wrapper around forked reqwest / hyper / hyper-util / hyper-rustls / tokio-rustls replaced by a standalone crates/primp built directly on hyper, hyper-util, h2 and rustls. The Python extension is rebound to the new native core.
primp-reqwest, primp-hyper, primp-hyper-util, primp-hyper-rustls, primp-tokio-rustlscrates/primp is now the product; Cargo.toml workspace.members trimmed to primp, primp-rustls/rustls, primp-h2, primp-python[profile.bench] added (lto = false, codegen-units = 256)primp/Cargo.toml no longer re-exports reqwest/*; now declares hyper@1.8, hyper-util@0.1, tower@0.5 + tower-http@0.7 (follow-redirect, decompression-*), http-body / http-body-util, tokio, foldhash@0.2, rustls@0.23 (impersonate, aws-lc-rs, ml-dsa), hickory-resolver@0.26 optional, quinn / h3 optionalprimp-rustls 0.23.40 → 0.23.42, hashbrown 0.16 → 0.17primp-h2 tracks upstream 0.4.17 / 0.4.18default = [cookies, gzip, brotli, zstd, deflate, query, form, json, multipart, stream, system-proxy, charset]impersonate / default-tls / http2 / rustls-no-provider (always on via rustls / h2)charset / hickory-dns / http3 remainprimp 1.3.1 → 2.0.0primp-python 1.3.1 → 2.0.01.89, panic = "abort" / lto = "fat" / codegen-units = 1 unchangedpyo3 / pyo3-async-runtimes 0.28 → 0.29html2text 0.16 → 0.17jiter@0.16, log; crate-type = cdylib doc disabledsrc/{lib, config, error, connect, tls, tls_bridge, impersonation} + async_impl/{client, body, request, response, multipart, negotiate, range_guard, h1_client, h2_client, h3_client} + dns/{cache, lru, hosts, resolve, plain, doh, dot, gai, hickory} + imp/{chrome, edge, opera, firefox, safari} + retry / redirect / proxy / cookiebusy / idle_since, PlaceholderGuard, origin-form rewrite, lazy evict_stale (90s / 256)AtomicU64 last_used, active_streams refcount, owner-only abort + detach_driver_until_idle, watch sentinel dedup, busy-gate, single conn per key, REFUSED retry ≤32, backpressure 1 KiB gateclose_notify), DNS LRU 30s/5s + DynResolver::http_resolve + ChainedResolverchrome_144-152, edge_144-151, opera_126-135, firefox_140,146-151, safari_18.5/26/26.3/26.4 + randomemulator_extension_order per-version (GREASE 0x6a6a / 0x0a0a, ALPS 0x44cd, compress_certificate brotli-vs-zlib, ML-DSA 150+ / 134+)SelectedUnoffered cipher fallbackOnceLock HostsMap (Box<str> + Arc<[IpAddr]>), non-blocking threaded load (3.6s → 0.2s)DNSTimeout capping; SOCKS primp-owned (bracket-strip, ATYP=4, 255 B caps, 8 KiB CONNECT, with_timeout)Proxy-Authorization per-hop re-attachpsl@2.1) rejection, one-shot cookie merge, referer=False honoredDrop no-abort while active_streams > 0 → fixes broken pipe under 100 concurrent temp clients; off-runtime GC mem::forget leak, primp_body_error_to_pyerr is_timeout before is_decode (pool_concurrent_drop)foldhash::fast::RandomState for H1/H2/H3/DNS HashMap + h2 IndexMap (~2×: 17ms vs 107ms hash, 615ms → 278ms pool 256-entry)StatusError.status_code/urlrustls@0.23.42 / h2@0.4.17, header-abuse (>4× max_header_list_size → GOAWAY ENHANCE_YOUR_CALM vs 431+RST) + flow-control hardening; clippy clean, manylinux_2_28 for ppc64le (GCC 7.5), Windows TimeoutError for 127.0.0.1:1primp-python to native primp@2.0 (Client / AsyncClient / RequestBuilder / Response)future_into_coroutine (14 sites, create_task-checked), ACTIVE_TASKS + SHUTTING_DOWN + call_soon_threadsafe interpreter-exit safety, py.detach + block_on, get_runtime PyOnceLock::get_or_try_init → PyRuntimeErroraread cache _drained, exhausted Arc<AtomicBool> iterators (TOCTOU stream_exhausted → quiet EOF)chunk_size / timeout validation, text_markdown (html2text), DNSTimeoutError PyOnceLockh1_fallback, h2_pool_saturation, range_accept_encoding (RangeGuard → identity), cookie_psl, proxy_review, redirect, retry (tower×pool 3×33=99), tls_close_notify, pool_concurrent_drop, socks_ipv6, window_increment, test_pool_concurrent_drop.pytest_client / request_params / streaming / exceptionsprimp.pyi / examples updated; README primp = "2"Full Changelog: v1.3.1...v2.0.0
One column per month.
fix: backport upstream bug fixes across forked crates by @deedy5 in #149
✨ Security
🐛 Fixes
⚡ Performance
🏗️ Refactors & Chores
Full Changelog: v1.3.0...v1.3.1
hickory-resolver 0.25.2 → 0.26.1 (fixes NSEC3 DoS vulnerability)
✨ Features
🐛 Fixes
⚡ Performance
🏗️ Refactors & Chores
Full Changelog: v1.2.3...v1.3.0
feat: rename all forked crates to unique package names by @deedy5 in #138
Full Changelog: v1.2.2...v1.2.3
fix(tls): GREASE improvements, Firefox/Chrome key_share fixes, ECH payload fix by @deedy5 in #135
Full Changelog: v1.2.1...v1.2.2
feat: native OS root CAs, custom cert impersonation fix, unknown extension tolerance by @deedy5 in #133
Full Changelog: v1.2.0...v1.2.1
Impersonation : ChromeV146 , EdgeV146 , OperaV128/V129 , SafariV26.3 , FirefoxV147/V148 . Random selection support ( Impersonate::Chrome , ::Random ,
Impersonation: ChromeV146, EdgeV146, OperaV128/V129, SafariV26.3, FirefoxV147/V148. Random selection support (Impersonate::Chrome, ::Random, etc).
HTTP/2 fingerprinting: Custom header ordering, initial stream ID config, simplified priority settings.
TLS: Fixed Firefox cipher suite order, added status_request_v2 for Safari, expanded Firefox named groups.
Python bindings: __version__ export, improved cookie/URL handling.
Cleanup: Removed large amount of unused tests and fixtures.
MSRV: 1.84, license MIT, workspace resolver 3.
What's Changed Fix security vulnerabilities
Built on custom forked crates providing full control over browser impersonation and TLS fingerprinting:
Built on custom forked crates providing full control over browser impersonation and TLS fingerprinting:
| Crate | Description | Key Components |
|---|---|---|
| primp-reqwest | HTTP client with impersonation support | Client builder, request/response handling, multipart, proxy configuration |
| primp-h2 | HTTP/2 protocol implementation | Frame handling, HPACK encoding, stream management, flow control |
| primp-hyper | HTTP client/server library | HTTP/1.1 & HTTP/2 support, body handling, connection pooling |
| primp-hyper-util | Hyper utilities | Legacy client, pool management, Tokio runtime integration |
| primp-rustls | TLS implementation with fingerprinting | TLS 1.2/1.3, certificate verification, JA3/JA4 fingerprinting |
| Browser | Profiles | Default |
|---|---|---|
| 🌐 Chrome | chrome_144, chrome_145 |
chrome_144 |
| 🧭 Safari | safari_18.5, safari_26 |
safari_26 |
| 🔷 Edge | edge_144, edge_145 |
edge_144 |
| 🦊 Firefox | firefox_140, firefox_146 |
firefox_146 |
| ⭕ Opera | opera_126, opera_127 |
opera_126 |
| 🎲 Random | random |
- |
| OS | Value | User-Agent Impact |
|---|---|---|
| 🤖 Android | android |
Mobile Android user-agent |
| 🍎 iOS | ios |
Mobile iOS user-agent |
| 🐧 Linux | linux |
Desktop Linux user-agent |
| 🍏 macOS | macos |
Desktop macOS user-agent |
| 🪟 Windows | windows |
Desktop Windows user-agent |
| 🎲 Random | random |
Randomly selected OS |
import primp
# Browser impersonation
client = primp.Client(impersonate="chrome_145", impersonate_os="windows")
# Random browser/OS
client = primp.Client(impersonate="random", impersonate_os="random")Comprehensive exception hierarchy mirroring Python's requests library for familiar error handling:
RequestException (base class)
├── HTTPError # HTTP 4xx/5xx status codes
├── ConnectionError # Connection-related failures
│ ├── ConnectTimeout # Connection establishment timeout
│ ├── SSLError # SSL/TLS certificate errors
│ └── ProxyError # Proxy connection errors
├── Timeout # Timeout-related failures
│ └── ReadTimeout # Data read timeout
├── RequestError # Request building/sending errors
│ ├── InvalidURL # URL format errors
│ └── InvalidHeader # Header format errors
├── BodyError # Body-related errors
│ ├── StreamConsumedError # Stream already consumed
│ └── ChunkedEncodingError # Chunked encoding errors
├── DecodeError # Decoding errors
│ └── ContentDecodingError # Content decode errors (gzip, brotli, etc.)
├── JSONError # JSON-related errors
│ ├── InvalidJSONError # Invalid JSON in request body
│ └── JSONDecodeError # JSON decode failure in response
└── TooManyRedirects # Redirect limit exceeded
AsyncClient with async/await, context managers, and concurrent requests| Version | Standard | Free-threaded |
|---|---|---|
| Python 3.10 | ✅ | - |
| Python 3.11 | ✅ | - |
| Python 3.12 | ✅ | - |
| Python 3.13 | ✅ | - |
| Python 3.14 | ✅ | ✅ |
| Architecture | Runner | Notes |
|---|---|---|
| x86_64 | ubuntu-22.04 | Primary platform |
| x86 | ubuntu-22.04 | 32-bit support |
| aarch64 | ubuntu-22.04-arm | ARM64 servers |
| armv7 | ubuntu-22.04 | ARM embedded/IoT |
| s390x | ubuntu-22.04 | IBM mainframes |
| ppc64le | ubuntu-22.04 | PowerPC servers |
| Architecture | Runner | Notes |
|---|---|---|
| x86_64 | ubuntu-22.04 | Alpine compatible |
| x86 | ubuntu-22.04 | 32-bit Alpine |
| aarch64 | ubuntu-22.04 | ARM64 Alpine |
| armv7 | ubuntu-22.04 | ARM Alpine |
| Architecture | Runner | Notes |
|---|---|---|
| x64 | windows-latest | Primary platform |
| x86 | windows-latest | 32-bit support |
| aarch64 | windows-11-arm | ARM64 Windows |
| Architecture | Runner | Notes |
|---|---|---|
| x86_64 | macos-15-intel | Intel Macs |
| aarch64 | macos-latest | Apple Silicon (M1/M2/M3) |
| File | Description |
|---|---|
docs/README.md |
Documentation index |
docs/client.md |
Sync Client usage guide |
docs/async_client.md |
AsyncClient usage guide |
docs/response.md |
Response object reference |
docs/exceptions.md |
Exception handling guide |
| Example | Description |
|---|---|
examples/basic_usage.py |
GET, POST, params, headers |
examples/async_usage.py |
Async client, concurrent requests |
examples/authentication.py |
Basic auth, bearer tokens |
examples/proxy.py |
HTTP/SOCKS5 proxies |
examples/cookies.py |
Cookie management |
examples/streaming.py |
Response streaming |
examples/post_requests.py |
POST/PUT/PATCH/DELETE |
examples/error_handling.py |
Exception handling |
examples/html_conversion.py |
HTML to text conversion |
pip install -U primpMIT License
Implement Client(impersonate="random", impersonate_os="random") by @deedy5 in #106
Full Changelog: v0.14.0...v0.15.0
Implement response.stream() by @deedy5 in #99
Full Changelog: v0.13.0...v0.14.0
Improve response logic by @deedy5 in https://github.com/deedy5/primp/pull/95
Full Changelog: https://github.com/deedy5/primp/compare/v0.12.1...v0.13.0
Typing: improve by @deedy5 in https://github.com/deedy5/primp/pull/92
Full Changelog: https://github.com/deedy5/primp/compare/v0.12.0...v0.12.1
refactor: move request functions to python by @deedy5 in https://github.com/deedy5/primp/pull/83
Full Changelog: https://github.com/deedy5/primp/compare/v0.11.0...v0.12.0
Add impersonate_os by @deedy5 in https://github.com/deedy5/primp/pull/78
impersonate_os by @deedy5 in https://github.com/deedy5/primp/pull/78
Full Changelog: https://github.com/deedy5/primp/compare/v0.10.1...v0.11.0
Use tracing crate instead of log by @deedy5 in https://github.com/deedy5/primp/pull/76
tracing crate instead of log by @deedy5 in https://github.com/deedy5/primp/pull/76
Use tracing crate instead of log Enable proxy authentication during HTTP redirects traits: small performance improvements
Full Changelog: https://github.com/deedy5/primp/compare/v0.10.0...v0.10.1
Bugfix get_encoding_from_content by @deedy5 in https://github.com/deedy5/primp/pull/69
Full Changelog: https://github.com/deedy5/primp/compare/v0.9.3...0.10.0
[bugfix] POST Multipart-Encoded Files by @deedy5 in https://github.com/deedy5/primp/pull/67
Full Changelog: https://github.com/deedy5/primp/compare/v0.9.2...v0.9.3
Client: add [getter, setter] for cookies by @deedy5 in https://github.com/deedy5/primp/pull/63
- Client: add [getter, setter] for cookies
- Small performance improvements
- README: impersonates - add firefox_109, firefox_133
- cargo update
Full Changelog: https://github.com/deedy5/primp/compare/v0.9.1...v0.9.2
Client: add https_only, http2_only parameters; remove http1, http2
Full Changelog: https://github.com/deedy5/primp/compare/v0.8.2...v0.9.1
Bugfix errors with data and json parameters when post a request by @deedy5 in https://github.com/deedy5/primp/pull/55
data and json parameters when post a request by @deedy5 in https://github.com/deedy5/primp/pull/55Full Changelog: https://github.com/deedy5/primp/compare/v0.8.1...v0.8.2
Add armv7 build by @deedy5 in https://github.com/deedy5/primp/pull/52
armv7 build by @deedy5 in https://github.com/deedy5/primp/pull/52Full Changelog: https://github.com/deedy5/primp/compare/v0.8.0...v0.8.1
Use env variable CA_CERT_FILE to specify the CA Certificates
Full Changelog: https://github.com/deedy5/primp/compare/v0.7.0...v0.8.0
Using the pythonize crate to handle data and json parameters, and return the responce as json by @deedy5 in https://github.com/deedy5/primp/pull/47
pythonize crate to handle data and json parameters, and return the responce as json by @deedy5 in https://github.com/deedy5/primp/pull/47Full Changelog: https://github.com/deedy5/primp/compare/v0.6.5...v0.7.0
Bump rquest to v0.26 by @deedy5 in https://github.com/deedy5/primp/pull/45
Full Changelog: https://github.com/deedy5/primp/compare/v0.6.4...v0.6.5
ca_cert_file: updated the code to match the changes in the upstream package by @deedy5 in https://github.com/deedy5/primp/pull/44
Full Changelog: https://github.com/deedy5/primp/compare/v0.6.3...v0.6.4
Bump rquest to v0.21, cargo update
Full Changelog: https://github.com/deedy5/primp/compare/v0.6.2...v0.6.3
README: impersonate param - add chrome_128
Full Changelog: https://github.com/deedy5/primp/compare/v0.6.1...v0.6.2
Use Exception to raise error by @deedy5 in https://github.com/deedy5/primp/pull/40
zig to build for manylinux_2_17 by @deedy5 in https://github.com/deedy5/primp/pull/41Full Changelog: https://github.com/deedy5/primp/compare/v0.6.0...v0.6.1
CI: bugfix after dependencies update by @deedy5 in https://github.com/deedy5/primp/pull/33
anyhow for error handling by @deedy5 in https://github.com/deedy5/primp/pull/34ca_cert_file parameter by @deedy5 in https://github.com/deedy5/primp/pull/36json_dumps, json_loads, url_encode by @deedy5 in https://github.com/deedy5/primp/pull/37Full Changelog: https://github.com/deedy5/primp/compare/v0.5.5...v0.6.0
README: impersonate param - add `chrome_127`
Bugfix fn get_encoding_from_content()
Full Changelog: https://github.com/deedy5/primp/compare/v0.5.3...v0.5.4
[html2text] Response object: 1) add attribute text_plain, 2) rename attribute plaintext to text_markdown by @deedy5 in https://github.com/deedy5/pyreq
text_plain, 2) rename attribute plaintext to text_markdown by @deedy5 in https://github.com/deedy5/pyreqwest_impersonate/pull/27Full Changelog: https://github.com/deedy5/pyreqwest_impersonate/compare/v0.5.2...v0.5.3
Your coding agent can read these notes before it upgrades. Set up the MCP server →