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.

PyPI version Python 3.10+ License: MIT Downloads

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
)

Authentication

# Basic auth - httpx-style tuple
response = client.get(
    "https://httpbin.org/basic-auth/user/pass",
    auth=("user", "pass")
)

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.2.0.tar.gz (42.6 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.2.0-py3-none-any.whl (32.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tlshttp-0.2.0.tar.gz
  • Upload date:
  • Size: 42.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tlshttp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 eba6031d0aa5db65f1d8158b7db77e9007f70029a35c662b377d8a3574a20189
MD5 3f770137b65a0ac344b64fc530b780c9
BLAKE2b-256 820f6bdc4d2c6cbd75c1e5c8b886b5b36fb8a218de0124ce1ad082a15f40deba

See more details on using hashes here.

Provenance

The following attestation bundles were made for tlshttp-0.2.0.tar.gz:

Publisher: publish.yml on Sekinal/tlshttp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

  • Download URL: tlshttp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 32.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tlshttp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cd64e6ccc618f4befed95d330fc1e3d5856451e9dcaec6b859ddf936530cc83a
MD5 822f895401721c00d2be113b37a7fb64
BLAKE2b-256 356adaf84ccb1a2e9e812abbf84cdd2113276c72ae22cdf14d13f97364dbde2c

See more details on using hashes here.

Provenance

The following attestation bundles were made for tlshttp-0.2.0-py3-none-any.whl:

Publisher: publish.yml on Sekinal/tlshttp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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