Skip to main content

Modern Python wrapper for tls-client with httpx-like API and browser fingerprinting

Project description

tlshttp

Modern Python wrapper for tls-client with an httpx-like API and browser TLS fingerprinting.

Python 3.10+ License: MIT

Features

  • httpx-like API - Familiar Client/AsyncClient pattern
  • Browser TLS fingerprinting - 50+ profiles (Chrome, Firefox, Safari, Opera)
  • Auto-download binaries - No manual setup required
  • True async support - Using anyio (works with asyncio and trio)
  • Fixes common wrapper bugs:
    • cookies.clear() actually works
    • No memory leaks (proper cleanup with context managers)
    • Case-insensitive headers (RFC 7230 compliant)

Installation

pip install tlshttp

Or with uv:

uv add tlshttp

Quick Start

Synchronous

import tlshttp

with tlshttp.Client() as client:
    response = client.get("https://httpbin.org/get")
    print(response.json())

Asynchronous

import asyncio
import tlshttp

async def main():
    async with tlshttp.AsyncClient() as client:
        response = await client.get("https://httpbin.org/get")
        print(response.json())

asyncio.run(main())

Client Configuration

client = tlshttp.Client(
    # Browser fingerprint profile
    profile="chrome_120",  # Default

    # Request defaults
    timeout=30.0,
    follow_redirects=True,

    # Proxy support
    proxy="http://user:pass@proxy.example.com:8080",

    # TLS settings
    verify=True,  # Verify SSL certificates
    http2=True,   # Enable HTTP/2

    # Default headers for all requests
    headers={"User-Agent": "MyApp/1.0"},

    # Default cookies
    cookies={"session": "abc123"},

    # Base URL for relative paths
    base_url="https://api.example.com",
)

HTTP Methods

All standard HTTP methods are supported:

with tlshttp.Client() as client:
    # GET
    response = client.get("https://httpbin.org/get")

    # POST with JSON
    response = client.post(
        "https://httpbin.org/post",
        json={"key": "value"}
    )

    # POST with form data
    response = client.post(
        "https://httpbin.org/post",
        data={"username": "john", "password": "secret"}
    )

    # PUT
    response = client.put(
        "https://httpbin.org/put",
        json={"updated": True}
    )

    # PATCH
    response = client.patch(
        "https://httpbin.org/patch",
        json={"field": "new_value"}
    )

    # DELETE
    response = client.delete("https://httpbin.org/delete")

    # HEAD
    response = client.head("https://httpbin.org/get")

    # OPTIONS
    response = client.options("https://httpbin.org/get")

Request Parameters

Query Parameters

response = client.get(
    "https://httpbin.org/get",
    params={"page": 1, "limit": 10}
)
# Requests: https://httpbin.org/get?page=1&limit=10

Headers

response = client.get(
    "https://httpbin.org/headers",
    headers={"Authorization": "Bearer token123"}
)

JSON Body

response = client.post(
    "https://httpbin.org/post",
    json={"message": "Hello", "count": 42}
)

Form Data

response = client.post(
    "https://httpbin.org/post",
    data={"username": "john", "password": "secret"}
)

Raw Content

response = client.post(
    "https://httpbin.org/post",
    content=b"raw bytes here"
)

Timeout

# Per-request timeout (overrides client default)
response = client.get(
    "https://httpbin.org/delay/5",
    timeout=10.0
)

Response Object

response = client.get("https://httpbin.org/get")

# Status
response.status_code      # 200
response.is_success       # True (2xx)
response.is_redirect      # False (3xx)
response.is_client_error  # False (4xx)
response.is_server_error  # False (5xx)
response.is_error         # False (4xx or 5xx)

# Content
response.content          # Raw bytes
response.text             # Decoded string (auto-detects encoding)
response.json()           # Parse as JSON

# Headers (case-insensitive)
response.headers["Content-Type"]
response.headers["content-type"]  # Same value
response.headers.get("X-Custom", "default")

# Cookies from response
response.cookies["session_id"]

# URL (final URL after redirects)
response.url

# HTTP version
response.http_version     # "HTTP/2" or "HTTP/1.1"

# Raise exception for error status codes
response.raise_for_status()  # Raises HTTPStatusError for 4xx/5xx

Cookies

Automatic Cookie Handling

Cookies are automatically stored and sent with subsequent requests:

with tlshttp.Client() as client:
    # Server sets a cookie
    client.get("https://httpbin.org/cookies/set/session/abc123")

    # Cookie is automatically sent
    response = client.get("https://httpbin.org/cookies")
    print(response.json())  # {"cookies": {"session": "abc123"}}

Manual Cookie Management

# Set cookies on client
client.cookies["token"] = "xyz789"
client.cookies.set("user", "john", domain="example.com")

# Get cookies
session = client.cookies.get("session")

# Clear all cookies (actually works, unlike other wrappers!)
client.cookies.clear()

# Clear cookies for specific domain
client.cookies.clear(domain="example.com")

# Iterate cookies
for name, value in client.cookies.items():
    print(f"{name}: {value}")

# Export to dict
all_cookies = client.cookies.to_dict()

Headers

Case-Insensitive Access

HTTP headers are case-insensitive per RFC 7230. tlshttp handles this correctly:

from tlshttp import Headers

headers = Headers({"Content-Type": "application/json"})

# All of these return the same value
headers["Content-Type"]
headers["content-type"]
headers["CONTENT-TYPE"]

Client Default Headers

client = tlshttp.Client(
    headers={"User-Agent": "MyBot/1.0", "Accept": "application/json"}
)

# Per-request headers merge with (and override) client defaults
response = client.get(
    "https://httpbin.org/headers",
    headers={"Accept": "text/html"}  # Overrides Accept, keeps User-Agent
)

Browser Profiles

tlshttp includes 50+ browser TLS fingerprint profiles that mimic real browsers:

Chrome

client = tlshttp.Client(profile="chrome_120")  # Recommended default
client = tlshttp.Client(profile="chrome_131")  # Newest
client = tlshttp.Client(profile="chrome_103")  # Oldest supported

Available: chrome_103 through chrome_131, plus PSK variants like chrome_120_psk

Firefox

client = tlshttp.Client(profile="firefox_120")
client = tlshttp.Client(profile="firefox_133")  # Newest
client = tlshttp.Client(profile="firefox_102")  # Oldest

Available: firefox_102 through firefox_133

Safari

client = tlshttp.Client(profile="safari_16_0")
client = tlshttp.Client(profile="safari_ios_16_0")
client = tlshttp.Client(profile="safari_ios_17_0")

Opera

client = tlshttp.Client(profile="opera_89")
client = tlshttp.Client(profile="opera_90")
client = tlshttp.Client(profile="opera_91")

List All Profiles

from tlshttp.profiles import BROWSER_PROFILES

for name in sorted(BROWSER_PROFILES.keys()):
    print(name)

Async Client

The AsyncClient has the same API as Client, but all methods are async:

import asyncio
import tlshttp

async def main():
    async with tlshttp.AsyncClient(profile="firefox_120") as client:
        # Single request
        response = await client.get("https://httpbin.org/get")

        # Concurrent requests
        urls = [
            "https://httpbin.org/get?id=1",
            "https://httpbin.org/get?id=2",
            "https://httpbin.org/get?id=3",
        ]

        tasks = [client.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)

        for response in responses:
            print(response.json()["args"]["id"])

asyncio.run(main())

Concurrency Limit

Control the maximum number of concurrent requests:

# Limit to 5 concurrent requests (default: 10)
client = tlshttp.AsyncClient(max_concurrent=5)

Proxy Support

# HTTP proxy
client = tlshttp.Client(proxy="http://proxy.example.com:8080")

# With authentication
client = tlshttp.Client(proxy="http://user:pass@proxy.example.com:8080")

# SOCKS5 proxy
client = tlshttp.Client(proxy="socks5://proxy.example.com:1080")

Error Handling

import tlshttp

try:
    with tlshttp.Client(timeout=5.0) as client:
        response = client.get("https://httpbin.org/delay/10")
        response.raise_for_status()

except tlshttp.TimeoutError:
    print("Request timed out")

except tlshttp.ConnectError:
    print("Failed to connect")

except tlshttp.HTTPStatusError as e:
    print(f"HTTP {e.response.status_code}: {e.response.text}")

except tlshttp.RequestError as e:
    print(f"Request failed: {e}")

Exception Hierarchy

TLSHTTPError (base)
├── RequestError
│   ├── ConnectError
│   ├── TimeoutError
│   └── ProxyError
└── HTTPStatusError (raised by raise_for_status())

Comparison with Other Libraries

Feature tlshttp requests httpx curl_cffi tls-client
TLS Fingerprinting ✅ 50+
Async Support
HTTP/2
httpx-like API
Cookie clear() works
No memory leaks
Auto-download binary N/A N/A

How It Works

tlshttp wraps the bogdanfinn/tls-client Go library via ctypes. On first use, it automatically downloads the appropriate binary for your platform from the tls-client GitHub releases.

The Go library provides:

  • Accurate browser TLS fingerprints (JA3, JA4, HTTP/2 settings, header order)
  • HTTP/2 and HTTP/3 support
  • Connection pooling

tlshttp adds:

  • Pythonic httpx-like API
  • Proper memory management (context managers, weak reference finalizers)
  • Case-insensitive headers (RFC 7230)
  • True async support via anyio thread pool
  • Bug fixes for issues in other wrappers

Platform Support

Platform Architecture Supported
Linux x86_64
Linux ARM64
Linux (Alpine/musl) x86_64
macOS x86_64 (Intel)
macOS ARM64 (M1/M2/M3)
Windows x86_64
Windows x86 (32-bit)

Development

# Clone
git clone https://github.com/Sekinal/tlshttp.git
cd tlshttp

# Install with dev dependencies
uv sync --group dev

# Run all tests
uv run pytest

# Skip integration tests (no network needed)
uv run pytest -m "not integration"

# Run specific test file
uv run pytest tests/test_headers.py -v

# Run with coverage
uv run pytest --cov=tlshttp

License

MIT License - see LICENSE for details.

This project uses binaries from tls-client which is licensed under BSD-4-Clause.

Credits

Project details


Download files

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

Source Distribution

tlshttp-0.1.0.tar.gz (41.7 kB view details)

Uploaded Source

Built Distribution

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

tlshttp-0.1.0-py3-none-any.whl (32.2 kB view details)

Uploaded Python 3

File details

Details for the file tlshttp-0.1.0.tar.gz.

File metadata

  • Download URL: tlshttp-0.1.0.tar.gz
  • Upload date:
  • Size: 41.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.10 {"installer":{"name":"uv","version":"0.9.10"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tlshttp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a3ee20d3e52e9c4c92125db6ec095b22d8c962e01d904d1a53db0abb1b13a903
MD5 00808ea661ce8fe48125e56e9e8f8f80
BLAKE2b-256 19a9359beb216d100d9f646d7ec82a22c9ee489f35ea3ce467250a08d08338c3

See more details on using hashes here.

File details

Details for the file tlshttp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tlshttp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 32.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.10 {"installer":{"name":"uv","version":"0.9.10"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tlshttp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f8fb9bf0b5a3841564cccc01e0b74cc601171002af4afbfce03c4d97ddde7c98
MD5 f6d5ca3f6136eac5be9ccf0853246d86
BLAKE2b-256 197ac5ed96a034bef9ae9d05f29d4a94ec59d7ef6abbf8bf492afdc046c1abd7

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