Skip to main content

tlsmask

Python HTTP requests with a real browser's TLS and HTTP/2 fingerprint.

PyPI Python Downloads CI License

bundles tls-client v1.16.0

tlsmask makes HTTP requests from Python that look like they come from Chrome, Firefox or Safari. Not just the headers: the TLS handshake and the HTTP/2 settings match the browser too. It runs bogdanfinn's tls-client, a Go library, and ships its latest official build inside the package.

It is also a drop-in replacement for the tls-client package, which hasn't had a release since February 2024.

import tlsmask

with tlsmask.Session("chrome_150") as session:
    r = session.get("https://tls.peet.ws/api/all")
    print(r.status_code, r.protocol)  # 200 HTTP/2.0
    print(r.json()["tls"]["ja4"])  # Chrome's fingerprint, not Python's

Why

Every HTTPS connection starts with a TLS "ClientHello": a list of the ciphers, extensions and curves the client supports, in a particular order. Each browser sends its own recognisable version (often summarised as a JA3 or JA4 fingerprint), and so does Python. A server can tell them apart before it reads a single header, which is why requests with a Chrome User-Agent still doesn't look like Chrome.

bogdanfinn's tls-client reproduces real browsers' handshakes and HTTP/2 behaviour. The tls-client package made it usable from Python, but it stopped at version 1.0.1. It still bundles tls-client v1.7.2, so its newest browser is Chrome 120, and a number of its bugs were never fixed.

tlsmask carries on from there: the current library, the same API, those bugs fixed, and a weekly check for new tls-client releases.

tlsmask compared with tls-client

tls-client (the old wrapper) tlsmask
bogdanfinn/tls-client inside v1.7.2 v1.16.0
Browser & app profiles 51 83
Newest Chrome 120 152
Newest Firefox 120 148
Newest iOS Safari 16.0 26.0
Brave none 146
Last updated Feb 2024 Sep 2026
Native library checked against a pinned SHA-256 no yes, at build and at load
Keeps itself current no yes, a weekly bot

Installation

pip install tlsmask

Prebuilt wheels are available for:

  • Windows, 64-bit and 32-bit
  • macOS 13 or newer, Apple Silicon and Intel
  • Linux x86-64, ARM64 and ARMv7 (glibc 2.17 or newer)

Each wheel already contains the tls-client library for its platform, so nothing is downloaded when your code runs. tlsmask needs Python 3.9 or newer and has no other dependencies.

Usage

import tlsmask

session = tlsmask.Session("chrome_150")
session.headers.update(
    {
        "User-Agent": (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
            "(KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36"
        ),
        "Accept-Language": "en-US,en;q=0.9",
    }
)

r = session.get("https://example.com", params={"q": "tls"})
print(r.status_code, r.headers["content-type"])

r = session.post("https://httpbin.org/post", json={"hello": "world"})
print(r.json())

session.close()

With asyncio:

import asyncio

import tlsmask


async def main():
    async with tlsmask.AsyncSession("firefox_148") as session:
        pages = await asyncio.gather(
            *(session.get(f"https://example.com/?page={i}") for i in range(10))
        )
        print([page.status_code for page in pages])


asyncio.run(main())

The guide covers the rest: header order, cookies, proxies, redirects, timeouts, errors, threads, custom TLS fingerprints and how to check your install.

Switching from tls-client

- import tls_client
+ import tlsmask as tls_client

For most code that's the only change. The constructor arguments, the request methods, response.json() and the session.cookies helpers work the same way. A few things behave differently on purpose, mostly because the old behaviour was a bug:

tls-client tlsmask
Unknown client_identifier (including typos like chrome112) silently sends a different browser raises ValueError and suggests the closest names
A panic inside the Go library kills the whole Python process raises TLSClientException (catch_panics is on by default)
proxies={"https": ...} ignored, so the request goes out without the proxy used
post(json=...) adds Content-Type to every later request adds it to that request only
Binary responses (images, PDFs) corrupted returned byte for byte
Header order tls-client's own order the order you wrote them in (or header_order=)
cert_compression_algo silently ignored by current tls-client translated to the new setting
params on a URL that already has ? url?a=1?b=2 url?a=1&b=2
Deleting a cookie still sent, because a second copy lives inside Go gone from the next request
Different timeout_seconds or insecure_skip_verify per request ignored after the first request honoured every time
Default profile chrome_120 tls-client's current default (tlsmask.DEFAULT_PROFILE)
get(url, cookies={...}) also stored those cookies in session.cookies sends them with that request only, and only to that site, even across redirects
A proxy written as host:port failed treated as http://host:port, like requests
Authorization on a redirect kept whenever the host name matches kept only on the same origin, or on an upgrade from http to https
client_identifier=None built a client from whatever was set, failing later if something was missing checks up front that ja3_string and the settings its extensions need are there, and raises ValueError naming what's missing; ALPN defaults to what browsers send
additional_decode= decoded one extra encoding not needed any more (tls-client decodes everything); it warns and is ignored

The old misspelled TLSClientExeption still works. If your code imports tls_client.settings.ClientIdentifiers, use tlsmask.ClientIdentifiers instead. response.headers is case-insensitive now, so use dict(response.headers) where you need a plain dict (for json.dumps, say).

Choosing a profile

For most uses, pick a current browser, such as the default (tlsmask.DEFAULT_PROFILE), firefox_148 or safari_ios_26_0. Send a User-Agent that matches the profile, because a Firefox User-Agent over a Chrome handshake stands out on its own. The _PSK variants resume TLS sessions the way a returning visitor's browser does.

tlsmask.PROFILES holds every name, and editors autocomplete them through the tlsmask.ClientIdentifier type.

All 83 profiles in tls-client v1.16.0
Family Profiles
Chrome chrome_103 chrome_104 chrome_105 chrome_106 chrome_107 chrome_108 chrome_109 chrome_110 chrome_111 chrome_112 chrome_116_PSK chrome_116_PSK_PQ chrome_117 chrome_120 chrome_124 chrome_130_PSK chrome_131 chrome_131_PSK chrome_133 chrome_133_PSK chrome_144 chrome_144_PSK chrome_146 chrome_146_PSK chrome_150 chrome_150_PSK chrome_152 chrome_152_PSK
Firefox firefox_102 firefox_104 firefox_105 firefox_106 firefox_108 firefox_110 firefox_117 firefox_120 firefox_123 firefox_132 firefox_133 firefox_135 firefox_146_PSK firefox_147 firefox_147_PSK firefox_148
Safari (iOS & iPadOS) safari_ios_15_5 safari_ios_15_6 safari_ios_16_0 safari_ios_17_0 safari_ios_18_0 safari_ios_18_5 safari_ios_26_0 safari_ipad_15_6
Safari (macOS) safari_15_6_1 safari_16_0
Opera opera_89 opera_90 opera_91
Brave brave_146 brave_146_PSK
Mobile apps & others cloudscraper confirmed_android confirmed_ios mesh_android mesh_android_1 mesh_android_2 mesh_ios mesh_ios_1 mesh_ios_2 mms_ios mms_ios_1 mms_ios_2 mms_ios_3 nike_android_mobile nike_ios_mobile okhttp4_android_7 okhttp4_android_8 okhttp4_android_9 okhttp4_android_10 okhttp4_android_11 okhttp4_android_12 okhttp4_android_13 zalando_android_mobile zalando_ios_mobile

Where the library comes from

The native part of tlsmask is bogdanfinn's own release file, byte for byte. It isn't rebuilt or modified.

  1. When a wheel is built, the file is downloaded from tls-client's GitHub releases. The build refuses it unless its SHA-256 matches both the hash pinned in tlsmask/_pins.py and the digest GitHub publishes for the release.
  2. The first time your program makes a request, tlsmask hashes the file again and won't load it if it doesn't match.
  3. Releases are published to PyPI from GitHub Actions with trusted publishing and signed attestations, and every GitHub release includes a SHA256SUMS file.

To check your own installation:

$ python -m tlsmask
tlsmask     1.16.0
tls-client  1.16.0 (github.com/bogdanfinn/tls-client)
platform    win32/AMD64 (64-bit Python) -> windows-amd64
library     OK: ...\tlsmask\bin\tls-client-windows-64-1.16.0.dll (sha256 53dca636..., as pinned)

Staying up to date

Every Monday a scheduled job looks for a new tls-client release. When there is one, it downloads and verifies every file, reads the new list of profiles, compares the library's API with the previous version, and runs the full test suite on Windows, macOS and Linux, on both Intel and ARM machines.

If nothing important changed and the release is at least a week old, a new tlsmask version is published automatically. Anything unusual, like a removed profile or a changed API, waits for a person to look at it first.

FAQ

Is this an official bogdanfinn project? No. tlsmask is an independent project and isn't affiliated with or endorsed by Bogdan Finn. It bundles his library unmodified. Please report tlsmask problems here rather than to him.

Does it work on Alpine Linux? Not at the moment. tls-client's Alpine (musl) build can't be loaded into Python, so there is no Alpine wheel. In Docker, use a glibc-based image such as python:3.12-slim instead.

My platform has no wheel. Get a build of tls-client for it (from its releases, or build it yourself) and point tlsmask at the file with the TLSMASK_LIBRARY_PATH environment variable. On such a platform pip install tlsmask installs from the source distribution, which has no library of its own.

Does it work with multiprocessing? Yes. The library is only loaded when the first request is made, so child processes are fine as long as the parent hadn't made a request before it forked. If in doubt, use the spawn start method (the default on Windows and macOS).

Why aren't redirects followed by default? tls-client didn't follow them by default, so existing code keeps working. Pass allow_redirects=True.

What is it for? Making requests that look like a real browser: testing your own site's bot protection, research, or talking to APIs that turn away non-browser clients. Please respect the terms of the sites you use it with.

Contributing

Bug reports and pull requests are welcome. To set up a development copy:

git clone https://github.com/Ubaidullah71/tlsmask && cd tlsmask
python -m venv .venv && source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
python scripts/fetch_binaries.py  # downloads and verifies this machine's library into tlsmask/bin/
pytest                            # unit tests, plus tests against the real library and a local server
pytest -m live                    # fingerprint checks against a public echo service

tlsmask/_pins.py, tlsmask/profiles.py, tlsmask/_version.py and the tables in this README are generated by scripts/render.py, so please don't edit them by hand.

Credits

License

tlsmask's own code is under the MIT license. The bundled tls-client library is under the BSD 4-Clause license (LICENSE-tls-client).

Metadata

Release files for tlsmask 1.16.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tlsmask 1.16.0
File Size Uploaded
tlsmask-1.16.0.tar.gz 69.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for tlsmask 1.16.0
File
tlsmask-1.16.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
tlsmask-1.16.0-py3-none-win32.whl Python 3 none Windows x86-32 Details
tlsmask-1.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
tlsmask-1.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl Python 3 none Linux glibc 2.17+ ARMv7l Details
tlsmask-1.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
tlsmask-1.16.0-py3-none-macosx_13_0_x86_64.whl Python 3 none macOS 13.0+ x86-64 Details
tlsmask-1.16.0-py3-none-macosx_13_0_arm64.whl Python 3 none macOS 13.0+ ARM64 Details

Total release size: 50.7 MB

Release files / tlsmask-1.16.0.tar.gz

Download URL tlsmask-1.16.0.tar.gz
Size 69.5 kB
Tags Source
SHA-256 checksum
How to use checksums
ba38f3a154ad2a7e925c0d43cf760134f39bf727b86479dc96849374a31256c9
BLAKE2b-256 checksum
How to use checksums
340841e9153505346904d01d7bf7628866eb0135ffb372b794ec9ee0e70292e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tlsmask-1.16.0-py3-none-win_amd64.whl

Download URL tlsmask-1.16.0-py3-none-win_amd64.whl
Size 8.4 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
b532df1e48b125345f97207040595f543db7e54df11848b7bfbbb8aa6d3f2765
BLAKE2b-256 checksum
How to use checksums
f925d37f001a41fbaf83352c57b6639c33ec269e996e1341b26d4ce5736268f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tlsmask-1.16.0-py3-none-win32.whl

Download URL tlsmask-1.16.0-py3-none-win32.whl
Size 8.3 MB
Tags Python 3 Windows x86-32
SHA-256 checksum
How to use checksums
75b86e4476e3066fbecb99fc6ae503fdbb4802980955812efc75da5dd9823968
BLAKE2b-256 checksum
How to use checksums
e8dce512fa1f6c9f538fdbf97cc4cf72f185efd41b33656439a79f62df180017
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tlsmask-1.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL tlsmask-1.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 8.5 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
07b9826ec5c3493a06ed16de19ae0b9b6115f4b3069dfeb671390d73bbf93818
BLAKE2b-256 checksum
How to use checksums
2ede53249d9bf1e4bab6a5763e13582609354fc5474f72fcf4928678fc06920e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tlsmask-1.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl

Download URL tlsmask-1.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Size 8.1 MB
Tags Linux glibc 2.17+ ARMv7l Python 3
SHA-256 checksum
How to use checksums
35917ae97e275f5f23de170d112431f7618490db4c932fd4b370fb799ae0a633
BLAKE2b-256 checksum
How to use checksums
4f6c3d6e4b972de244460d02187d2caaf0e0606c3a58697e6069f9d6cb1bc3a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tlsmask-1.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL tlsmask-1.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 7.9 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
786be842c20586f427e86388c84b28318034d9f3f668d919bba1439cfc2f1763
BLAKE2b-256 checksum
How to use checksums
03890f9f15728a49ba947a5c356285e1f4d6241e39a8d363107af72f71dc7450
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tlsmask-1.16.0-py3-none-macosx_13_0_x86_64.whl

Download URL tlsmask-1.16.0-py3-none-macosx_13_0_x86_64.whl
Size 4.8 MB
Tags Python 3 macOS 13.0+ x86-64
SHA-256 checksum
How to use checksums
b0b86957d5dfb94eda05875d80ddda657144dce7f83af065e214b9a30777e92e
BLAKE2b-256 checksum
How to use checksums
b3fc25092c28e8f4bbab9b3a6aa9de9792c8f74e17fedf452b52cdf2d547663e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / tlsmask-1.16.0-py3-none-macosx_13_0_arm64.whl

Download URL tlsmask-1.16.0-py3-none-macosx_13_0_arm64.whl
Size 4.5 MB
Tags Python 3 macOS 13.0+ ARM64
SHA-256 checksum
How to use checksums
7ff314e9161d7d3d75c67897b612021c51650e34288860387af913e43809e7a9
BLAKE2b-256 checksum
How to use checksums
e5cf08363dedd421e9f2ff28020f38737371726ad972932b7806aa43344873d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.16.0 This release

8 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page