Skip to main content

Send local HTTP requests with a byte-exact macOS Safari 26.5 network fingerprint (TLS/HTTP2/HPACK/headers).

Project description

never_safari (Python)

Send local HTTP requests with a byte-exact macOS Safari 26.5 network fingerprint — TLS ClientHello (JA3/JA4), HTTP/2 (SETTINGS/WINDOW_UPDATE/ pseudo-header order), HPACK (Huffman + dynamic table), request-header order, download flow-control cadence, and TLS-record framing — like curl_cffi, but for Safari. Built on a patched BoringSSL + a from-scratch HTTP/2 layer.

Install

pip install never_safari          # prebuilt wheels: macOS / Linux / Windows
# or from source (needs cmake, go, ninja, a C/C++ compiler):
pip install maturin && maturin develop -m crates/safari-py/Cargo.toml

Use — a requests-style API

import never_safari

# Session keeps cookies + a connection pool (like a browser). `Client` is an alias.
s = never_safari.Session()
r = s.get("https://tls.peet.ws/api/all", params={"pretty": "1"})
print(r.status_code, r.ok)
print(r.json()["tls"]["ja4"])       # t13d2013h2_a09f3c656075_7f0f34a4126d

# POST: json= takes a Python object; data= takes a dict (form) / str / bytes.
r = s.post("https://api.example.com/v1/items",
           json={"name": "x"},
           headers={"authorization": "Bearer TOKEN"})
r.raise_for_status()                # raises never_safari.HTTPError on 4xx/5xx

# Module-level, exactly like requests:
never_safari.get("https://example.com", params={"q": "1"})
never_safari.post("https://api.example.com", data={"k": "v"}, timeout=10)

# Session options
s = never_safari.Session(
    ios=False,            # iOS Safari 26 profile (no post-quantum MLKEM768)
    verify=True,          # TLS cert verification (like requests)
    proxies={"https": "http://user:pass@host:8080"},
    multiplex=True,       # browser-style: concurrent requests share one connection
    timeout=30.0, connect_timeout=10.0, max_redirects=10,
)

Matches requests' surface:

  • Verbs: get/post/put/patch/delete/head/options/request — on the Session and at module level.
  • Request kwargs: params=, data= (dict → form / str / bytes), json= (any object), headers=, cookies=, referer=, timeout= (seconds, per-request), allow_redirects= (default True).
  • Headers: your headers= dict is merged over Safari's template — Safari's own headers keep their fixed (fingerprint) order and any you omit are auto-filled at their canonical position, while each custom header is placed right after whichever Safari header precedes it in your dict (so a captured request replays with its layout). Header names are lowercased for HTTP/2.
  • Response: .status_code (.status alias), .ok, .reason, .headers, .content, .text, .json(), .url, .resumed, .cookies, .raise_for_status().
  • Cookies (dict-like, like curl_cffi / httpx): s.cookies["k"], s.cookies.get("k", domain=…, path=…), .get_dict(domain=…, path=…), .set("k", "v", domain=…, path=…), .delete("k", domain=…, path=…), .clear(domain=…, path=…), .update(), .keys()/.values()/.items(), "k" in s.cookies, dict(s.cookies); r.cookies for what a response set.
  • never_safari.HTTPError, Session (alias of Client).

The network call releases the GIL, so many Python threads can crawl concurrently.

Async — AsyncSession (like curl_cffi)

import asyncio, never_safari

async def main():
    async with never_safari.AsyncSession() as s:      # max_workers= bounds concurrency
        r = await s.get("https://example.com", params={"q": "1"})
        r = await s.post(url, json={"a": 1})
        results = await asyncio.gather(*[s.get(u) for u in urls])   # runs concurrently

asyncio.run(main())

Same verbs, kwargs, Response and .cookies as the sync Session; every verb is a coroutine. It runs the (GIL-releasing) sync core in a bounded thread pool, so requests run genuinely in parallel. AsyncClient is an alias.

See the project README for the full fingerprint details and scope.

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.

never_safari-0.4.3-cp39-abi3-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.9+Windows x86-64

never_safari-0.4.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.5 MB view details)

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

never_safari-0.4.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.4 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

never_safari-0.4.3-cp39-abi3-macosx_11_0_arm64.whl (1.3 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

never_safari-0.4.3-cp39-abi3-macosx_10_12_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file never_safari-0.4.3-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: never_safari-0.4.3-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.4 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for never_safari-0.4.3-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 454fc40f5034e0db4cae385de9c683b711ce17b863285887cf227f6f288d7523
MD5 6fad1bec527195e3eea394bb460434fb
BLAKE2b-256 5ca5f0e151adb68cd211852394ad2d42180616f2fc511931c9adfb685616bf40

See more details on using hashes here.

File details

Details for the file never_safari-0.4.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for never_safari-0.4.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2204a3b771ec16d58a499ac615b3a8393691488ac02218269e1202fa84be21e0
MD5 a684ff59f894aa8324be2ba4de4c5d47
BLAKE2b-256 060932cdab9181e6fb4af0cda80b17793e1c387cdd24cd452a111cd9dfb82b5c

See more details on using hashes here.

File details

Details for the file never_safari-0.4.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for never_safari-0.4.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 064d0dffbbb5fb82a197e66242cd01938417d06ddfb5796baf5450a23a871f04
MD5 1df7c0d710acb870b61aca070ff4947a
BLAKE2b-256 79befd9c574435c66356b5f722ad3fba874278b6bbce2172c5ed9e68fa687f0b

See more details on using hashes here.

File details

Details for the file never_safari-0.4.3-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for never_safari-0.4.3-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b8e6d3360b200d052640353b1ae76fd817ac7abc965d541b3a771c8a4912d9cc
MD5 4f02b9fc2849671f28134f4e4fade4a0
BLAKE2b-256 ea9924dc279837afceec9b9e1847cebd41b1f4c68e647b4b1c4cf875030be18f

See more details on using hashes here.

File details

Details for the file never_safari-0.4.3-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for never_safari-0.4.3-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d08b8ac923d7d6d7735e3f081849be896c82ab82582c0c9d6e8f1631a9eb1324
MD5 ce3037c827bc70fad094782d7f65e452
BLAKE2b-256 f999c67ff823cc6775a4c5ec64d345cc17269a9a5b1b2409213c16b79e1a90f0

See more details on using hashes here.

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