horaa-tls Documentation

A high-performance Python HTTP client built to completely eliminate OpenSSL TLS fingerprint leakage. It executes in-process on an isolated Go networking core, providing exact JA3/JA4 browser emulation, 0-RTT HTTP/3 QUIC protocol racing, and Zero-GIL execution.

Why horaa-tls exists

Standard Python HTTP libraries (requests, httpx) fail modern anti-bot systems (Cloudflare, Akamai, Datadome) at the TLS handshake layer because Python's OpenSSL lacks browser GREASE extensions and uses fixed cipher orders. horaa-tls replaces the entire network stack with authentic browser Client Hellos.

from horaa_tls import Session, ClientProfile

# Exact Chrome 133 TLS ClientHello (BoringSSL stack)
with Session(profile=ClientProfile.CHROME_133) as session:
    response = session.get("https://tls.peet.ws/api/all")
    
    data = response.json()
    print("Status:", response.status_code)
    print("JA3 Hash:", data["tls"]["ja3_hash"])
    print("JA4 Hash:", data["tls"]["ja4"])

Installation

horaa-tls is published to the official Python Package Index (PyPI) and includes compiled C-shared binaries for 64-bit systems.

TERMINAL COMMAND
# Install or upgrade via pip
pip install --upgrade horaa-tls

# Or install using uv (Astral)
uv add horaa-tls

Supported Platforms

Platform Architecture Driver Binary Compatibility
Windows AMD64 (x86_64) tls-client-windows-64.dll Python 3.10+
Linux x86_64 / arm64 tls-client-linux-amd64.so Python 3.10+
macOS Apple Silicon & Intel tls-client-darwin-arm64.dylib Python 3.10+

30-Second Quickstart

Use horaa-tls just like requests, but with authentic browser fingerprints.

main.py
from horaa_tls import Session, ClientProfile

# 1. Choose your browser target profile
with Session(profile=ClientProfile.CHROME_133) as session:
    
    # 2. Make standard GET with query parameters
    res = session.get("https://httpbin.org/get", params={"page": 1})
    print("Status:", res.status_code)
    print("URL:", res.url)

    # 3. Make POST with JSON
    post_res = session.post(
        "https://httpbin.org/post",
        json={"user": "alice", "action": "login"},
        timeout=15
    )
    print("Post Echo:", post_res.json()["json"])

Ctypes Native Bridge & Zero-GIL

In standard Python, socket I/O and OpenSSL handshakes execute under Python's Global Interpreter Lock (GIL). horaa-tls eliminates this bottleneck:

  1. Memory-Isolated C Bridge: Python transfers pointers directly to compiled Go machine code (backend.ctypes_go).
  2. Zero GIL Locking: During socket connection, TLS handshakes, ALPN negotiation, and compression, Python's GIL is completely released. Go handles the connection in its own lightweight goroutines.
  3. Automatic Memory Cleanup: Responses returned from Go are decoded into Python objects, and the underlying C-allocated memory is freed immediately.

Client Profiles

The ClientProfile enum defines pre-tested browser configurations that match genuine browser cryptographic stacks:

Profile Enum String Value Browser Engine
ClientProfile.CHROME_133 "chrome_133" Modern Chromium with Post-Quantum Kyber & HTTP/3
ClientProfile.CHROME_120 "chrome_120" Stable Chromium baseline
ClientProfile.CHROME_110 "chrome_110" Legacy Chromium 110
ClientProfile.CHROME_103 "chrome_103" Legacy Chromium 103
ClientProfile.FIREFOX_133 "firefox_133" Current Firefox Gecko TLS stack
ClientProfile.FIREFOX_123 "firefox_123" Stable Firefox baseline
ClientProfile.FIREFOX_117 "firefox_117" Extended legacy Firefox
ClientProfile.SAFARI_16_0 "safari_16_0" macOS Safari WebKit stack
ClientProfile.SAFARI_IOS_17_0 "safari_ios_17_0" Mobile iOS 17 Safari WebKit
ClientProfile.OPERA_90 "opera_90" Opera Desktop emulation

HTTP/3 QUIC Protocol Racing

Real web browsers do not send simple sequential TCP requests. They initiate protocol racing, attempting UDP QUIC (HTTP/3) concurrently with TCP TLS 1.3 to achieve 0-RTT connects.

http3_racing.py
from horaa_tls import Session, ClientProfile, Protocol

with Session(
    profile=ClientProfile.CHROME_133,
    with_protocol_racing=True
) as session:
    res = session.get("https://cloudflare.com")
    print("Negotiated Protocol:", res.protocol)  # Outputs Protocol.HTTP_3
Proxy Requirement for HTTP/3

Standard HTTP/HTTPS proxies only support TCP. If you run with_protocol_racing=True through an HTTP proxy, UDP packets would bypass the proxy and leak your real machine IP! horaa-tls strictly requires SOCKS5 proxies when protocol racing is active, raising BackendError if an HTTP proxy is configured.

Interactive Handshake & Fingerprint Tester

Select different profiles below to see how horaa-tls shapes the low-level TLS Client Hello and JA3/JA4 digests:

Live Handshake Inspector
● READY FOR LIVE TEST
JA3 MD5 Digest
b32309a26951912be7dba376398abc3b
JA4 Fingerprint
t13d1516h2_8daaf6152771_e56270d43acb
Negotiated ALPN
["h3", "h2", "http/1.1"]
QUIC / HTTP3 State
Active 0-RTT Protocol Racing
Anti-Bot Detection Risk
0% (Clean Genuine Desktop Browser)

Proxies & Pool Rotation

horaa-tls provides native proxy pool management built directly into the Session constructor:

proxies.py
from horaa_tls import Session, ClientProfile

# 'failover' rotates proxy if connection errors occur
# 'request' rotates proxy on every HTTP call
session = Session(
    profile=ClientProfile.CHROME_133,
    proxies=[
        "socks5://user:pass@proxy1:1080",
        "socks5://user:pass@proxy2:1080"
    ],
    proxy_mode="failover"
)

res = session.get("https://api.ipify.org?format=json")
print("IP:", res.json()["ip"])

Concurrency Best Practices

Session Concurrency Rule

A Session is not thread-safe because cookies and proxy-rotator state are mutated per request. To achieve maximum multi-threaded performance, allocate one Session per thread.

concurrency.py
from concurrent.futures import ThreadPoolExecutor
from horaa_tls import Session, ClientProfile

def worker(urls):
    # Create an isolated Session per worker thread
    with Session(profile=ClientProfile.CHROME_133) as session:
        for url in urls:
            res = session.get(url)
            print(f"[{res.status_code}] {res.url}")

urls = [f"https://httpbin.org/get?page={i}" for i in range(50)]
chunks = [urls[i:i + 5] for i in range(0, len(urls), 5)]

with ThreadPoolExecutor(max_workers=10) as executor:
    executor.map(worker, chunks)

Resiliency Middlewares

horaa-tls automatically registers RetryMiddleware, ProxyRotatorMiddleware, and RedirectMiddleware on every session.

custom_retry.py
from horaa_tls import Session
from horaa_tls.middleware import RetryMiddleware

session = Session()

# Customize retry parameters
custom_retry = RetryMiddleware(
    max_retries=5,
    backoff_factor=1.0,
    retry_on_status=(429, 500, 502, 503, 504),
    jitter=True
)
session.middleware_pipeline.add(custom_retry)

Writing Custom Middlewares

Subclass BaseMiddleware to inspect or mutate payloads before requests go to the Go engine, or inspect responses returning from it:

custom_auth.py
from horaa_tls import Session, ClientProfile
from horaa_tls.middleware.base import BaseMiddleware

class AuthHeaderMiddleware(BaseMiddleware):
    def __init__(self, token: str):
        self.token = token

    def before_request(self, session, payload: dict) -> None:
        headers = payload.setdefault("headers", {})
        headers["Authorization"] = f"Bearer {self.token}"

session = Session(profile=ClientProfile.CHROME_133)
session.middleware_pipeline.add(AuthHeaderMiddleware("sk_test_12345"))

res = session.get("https://httpbin.org/headers")
print(res.json()["headers"])

API Reference: horaa_tls.Session

The primary connection and request manager in horaa-tls.

Constructor

Session(
    profile: str | ClientProfile = ClientProfile.CHROME_120,
    proxy: str | None = None,
    proxies: list[str] | None = None,
    proxy_mode: str = "failover",
    header_order: list[str] | None = None,
    pseudo_header_order: list[str] | None = None,
    insecure_skip_verify: bool = False,
    random_tls_extension_order: bool = False,
    use_mitm_when_active: bool = False,
    force_http1: bool = False,
    with_protocol_racing: bool = False,
    disable_http3: bool = False,
    cookies: dict[str, str] | None = None,
    timeout_seconds: float = 30,
)

Request Methods

  • session.get(url, **kwargs) -> Response: Sends an HTTP GET.
  • session.post(url, data=None, json=None, **kwargs) -> Response: Sends an HTTP POST.
  • session.put(url, **kwargs) -> Response: Sends an HTTP PUT.
  • session.delete(url, **kwargs) -> Response: Sends an HTTP DELETE.
  • session.patch(url, **kwargs) -> Response: Sends an HTTP PATCH.
  • session.head(url, **kwargs) -> Response: Sends an HTTP HEAD.
  • session.options(url, **kwargs) -> Response: Sends an HTTP OPTIONS.
  • await session.request_async(method, url, **kwargs) -> Response: Asynchronous coroutine.
  • session.close() -> None: Closes session and frees Go connection resources.

API Reference: horaa_tls.Response

The response object returned from all session request invocations:

Attribute / Method Type Description
response.status_code int HTTP response status integer (e.g. 200, 404).
response.protocol Protocol Negotiated protocol enum: Protocol.HTTP_3, Protocol.HTTP_2, or Protocol.HTTP_1_1.
response.text str Decoded unicode response body string.
response.content bytes Raw binary payload bytes.
response.json() dict | list Parses response body into native Python primitives.
response.headers CaseInsensitiveDict Case-insensitive dictionary of response headers.
response.cookies RequestsCookieJar Cookies set by the target server.
response.elapsed timedelta Total round-trip time for the request.

Exception Hierarchy

All exceptions inherit from horaa_tls.exceptions.HoraaTLSError:

HoraaTLSError
├── BackendError
│   └── BackendMissingError
├── TimeoutError
├── ProxyError
├── RedirectLoopError
└── TLSFingerprintError