Noble TLS is an advanced HTTP library based on requests and tls-client. Fully async, with auto-updating JA3 fingerprints.
pip install noble-tls
- Auto-update TLS client libs from bogdanfinn/tls-client
- Async support
- Proxy support (HTTP, HTTPS, SOCKS4, SOCKS5)
- Custom JA3 string
- Custom H2 and H3 settings
- Custom supported signature algorithms
- Custom supported versions
- Custom key share curves (including post-quantum)
- Custom cert compression algorithm
- Custom pseudo header order
- Custom connection flow
- Custom header order
- 76 preset browser profiles (Chrome, Firefox, Opera, Safari, iOS, iPadOS, Android)
- Random TLS extension order
- HTTP/3 (QUIC) with protocol racing
- Certificate pinning (HPKP)
- ECH (Encrypted Client Hello) and ALPS
- Session lifecycle management (close, get/add cookies from Go layer)
- Response streaming to file
- Protocol detection (
response.used_protocol) - Millisecond-precision timeouts
- IPv4/IPv6 control
- SNI and Host header overrides
- Rotating proxy support
-
requests-stylehistoryandallow_redirects
You can now explicitly destroy sessions and manage cookies at the Go layer:
session = noble_tls.Session(client=Client.CHROME_133)
# Do your work...
res = await session.get("https://example.com")
# Manage cookies in the Go session directly
cookies = await session.get_cookies("https://example.com")
await session.add_cookies("https://example.com", [
{"name": "sid", "value": "abc", "domain": "example.com", "path": "/"},
])
# Free Go memory and connections when done
await session.close()
# Or nuke everything at once
await Session.close_all()Every response now tells you which protocol was actually negotiated. Using a Protocol enum:
from noble_tls import Protocol
res = await session.get("https://example.com")
print(res.used_protocol) # Protocol.HTTP_2
if res.used_protocol == Protocol.HTTP_3:
print("Running over QUIC")HTTP/3 (QUIC) support, with the ability to race HTTP/2 against HTTP/3 the way Chrome does (300ms head start for H2, H3 can still win):
session = noble_tls.Session(
ja3_string="...",
protocol_racing=True,
h3_settings={"QPACK_MAX_TABLE_CAPACITY": 4096},
h3_settings_order=["QPACK_MAX_TABLE_CAPACITY"],
h3_pseudo_header_order=[":method", ":authority", ":scheme", ":path"],
h3_send_grease_frames=True,
)You can also just disable H3 if it causes problems: disable_http3=True.
Per-session network and security settings:
session = noble_tls.Session(
client=Client.CHROME_133_PSK,
disable_ipv6=True, # IPv4 only
local_address="0.0.0.0:0", # Bind to specific interface (host:port)
server_name_overwrite="sni.com", # Override TLS SNI
is_rotating_proxy=True, # Force new connection per request
without_cookie_jar=True, # Disable cookie jar entirely
certificate_pinning={ # HPKP - reject if pin doesn't match
"example.com": ["sha256_pin_base64"],
},
)
# Per-request: millisecond timeout, host override, stream to file
res = await session.get(
"https://example.com",
timeout_milliseconds=2500,
request_host_override="other.host.com",
stream_output_path="/tmp/response.json",
stream_output_block_size=4096,
)Encrypted Client Hello, Application Layer Protocol Settings, and post-quantum key share curves for custom TLS configurations:
session = noble_tls.Session(
ja3_string="...",
alps_protocols=["h2"],
ech_candidate_payloads=[256],
ech_candidate_cipher_suites=[
{"kdfId": "HKDF_SHA256", "aeadId": "AEAD_AES_128_GCM"},
],
key_share_curves=["GREASE", "X25519MLKEM768", "X25519"],
supported_signature_algorithms=[
"ECDSAWithP256AndSHA256",
"PSSWithSHA256",
"Ed25519",
],
record_size_limit=16384,
allow_http=True,
stream_id=1,
)76 profiles now, synced with the latest Go source. New additions:
| Browser | New profiles |
|---|---|
| Chrome | 130 PSK, 144, 144 PSK, 146 PSK |
| Firefox | 123, 133, 146 PSK, 147 PSK |
| Safari | iOS 18.5, iOS 26.0 |
Removed CHROME_141 and CHROME_142
Separate from the regular headers dict, default_headers uses a multi-value format and acts as a fallback when no headers are specified on a request:
session = noble_tls.Session(
client=Client.CHROME_133,
default_headers={"Accept": ["text/html", "application/json"]},
)TLS fingerprinting alone won't get past modern bot protection. Hyper Solutions provides API endpoints that generate valid antibot tokens for:
Akamai | DataDome | Kasada | Incapsula
No browser automation. Simple API calls that return the cookies and headers these systems expect.
Get your API key | Docs | Discord
The syntax follows requests closely. Most things work the same way.
Example 1 -- Preset browser profile:
Available client identifiers (76 profiles)
| Chrome | Safari | Firefox | Opera |
|---|---|---|---|
CHROME_103 |
SAFARI_15_6_1 |
FIREFOX_102 |
OPERA_89 |
CHROME_104 |
SAFARI_16_0 |
FIREFOX_104 |
OPERA_90 |
CHROME_105 |
SAFARI_IPAD_15_6 |
FIREFOX_105 |
OPERA_91 |
CHROME_106 |
SAFARI_IOS_15_5 |
FIREFOX_106 |
|
CHROME_107 |
SAFARI_IOS_15_6 |
FIREFOX_108 |
|
CHROME_108 |
SAFARI_IOS_16_0 |
FIREFOX_110 |
|
CHROME_109 |
SAFARI_IOS_17_0 |
FIREFOX_117 |
|
CHROME_110 |
SAFARI_IOS_18_0 |
FIREFOX_120 |
|
CHROME_111 |
SAFARI_IOS_18_5 |
FIREFOX_123 |
|
CHROME_112 |
SAFARI_IOS_26_0 |
FIREFOX_132 |
|
CHROME_116_PSK |
FIREFOX_133 |
||
CHROME_116_PSK_PQ |
FIREFOX_135 |
||
CHROME_117 |
FIREFOX_146_PSK |
||
CHROME_120 |
FIREFOX_147 |
||
CHROME_124 |
FIREFOX_147_PSK |
||
CHROME_130_PSK |
|||
CHROME_131 |
|||
CHROME_131_PSK |
|||
CHROME_133 |
|||
CHROME_133_PSK |
|||
CHROME_144 |
|||
CHROME_144_PSK |
|||
CHROME_146 |
|||
CHROME_146_PSK |
| Mobile / App |
|---|
ZALANDO_ANDROID_MOBILE, ZALANDO_IOS_MOBILE |
NIKE_IOS_MOBILE, NIKE_ANDROID_MOBILE |
CLOUDSCRAPER |
MMS_IOS, MMS_IOS_1, MMS_IOS_2, MMS_IOS_3 |
MESH_IOS, MESH_IOS_1, MESH_IOS_2 |
MESH_ANDROID, MESH_ANDROID_1, MESH_ANDROID_2 |
CONFIRMED_IOS, CONFIRMED_ANDROID |
OKHTTP4_ANDROID_7 through OKHTTP4_ANDROID_13 |
import asyncio
import noble_tls
from noble_tls import Client
async def main():
await noble_tls.update_if_necessary()
session = noble_tls.Session(
client=Client.CHROME_133,
random_tls_extension_order=True
)
res = await session.get(
"https://www.example.com/",
headers={"key1": "value1"},
proxy="http://user:password@host:port"
)
print(res.status_code)
print(res.used_protocol)
print(res.text)
await session.close()
asyncio.run(main())Example 2 -- Custom JA3 fingerprint:
import asyncio
import noble_tls
async def main():
await noble_tls.update_if_necessary()
session = noble_tls.Session(
ja3_string="771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
h2_settings={
"HEADER_TABLE_SIZE": 65536,
"MAX_CONCURRENT_STREAMS": 1000,
"INITIAL_WINDOW_SIZE": 6291456,
"MAX_HEADER_LIST_SIZE": 262144
},
h2_settings_order=[
"HEADER_TABLE_SIZE",
"MAX_CONCURRENT_STREAMS",
"INITIAL_WINDOW_SIZE",
"MAX_HEADER_LIST_SIZE"
],
supported_signature_algorithms=[
"ECDSAWithP256AndSHA256",
"PSSWithSHA256",
"PKCS1WithSHA256",
"ECDSAWithP384AndSHA384",
"PSSWithSHA384",
"PKCS1WithSHA384",
"PSSWithSHA512",
"PKCS1WithSHA512",
],
supported_versions=["GREASE", "1.3", "1.2"],
key_share_curves=["GREASE", "X25519"],
cert_compression_algo="brotli",
pseudo_header_order=[":method", ":authority", ":scheme", ":path"],
connection_flow=15663105,
header_order=["accept", "user-agent", "accept-encoding", "accept-language"]
)
res = await session.post(
"https://www.example.com/",
headers={"key1": "value1"},
proxy="http://user:password@host:port"
)
print(res.text)
await session.close()
asyncio.run(main())More examples in the examples/ folder.
If you want to pack the library with Pyinstaller or Pyarmor, add this to your command:
Linux - Ubuntu / x86:
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-x86.so:tls_client/dependencies'
Linux Alpine / AMD64:
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-amd64.so:tls_client/dependencies'
MacOS M1 and older:
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-x86.dylib:tls_client/dependencies'
MacOS M2:
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-arm64.dylib:tls_client/dependencies'
Windows:
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-64.dll;tls_client/dependencies'
Package is named after Admiral Atticus Noble in Rebel Moon: Part One - A Child of Fire
Big shout out to Bogdanfinn for open sourcing his tls-client in Go, and FlorianREGAZ for the original Python wrapper.