Skip to main content

curlpro

An HTTP client with a browser's network fingerprint: the TLS ClientHello, the HTTP/2 and HTTP/3 frames, header order and header case.

pip install curlpro
import curlpro

with curlpro.Session("chrome-151-windows") as s:
    r = s.get("https://example.com")
    print(r.status, r.text[:200])

Neither Go nor a compiler is needed: the native library and all 50 profiles are already inside the wheel, and the profiles load themselves.

Why another one

The existing clients keep their browser profiles in compiled code: a new Chrome comes out every four weeks, and each time that means editing C or Go, rebuilding and releasing. Here a profile is data, and it can be registered at runtime:

curlpro.register_profile({
    "name": "chrome-152-windows",
    "based_on": "chrome-151-windows",
    "headers": {"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/152.0.0.0 ..."},
})

Your own fingerprint, without a request

What a server would see is computed locally, from the same ClientHello bytes that would go on the wire. No network, no oracle:

with curlpro.Session("chrome-151-windows") as s:
    fp = s.fingerprint()
    print(fp.ja4)       # t13d1516h2_8daaf6152771_806a8c22fdea
    print(fp.akamai)    # 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p

Checked against 50 captures: JA4 50/50, JA3N 50/50, Akamai 50/50.

audit() answers the second question — the one people actually lose days to. Not "does my fingerprint look right" but "does anything here disagree with anything else", because that is what gives a client away:

for finding in s.audit():
    print(finding)

Personas

Profile, proxy, device, headers and cookies — one identity, one file:

p = curlpro.Persona.new("chrome-151-windows", proxy="http://user:pass@host:8080")
p.save("accounts/user42.json")

p = curlpro.Persona.load("accounts/user42.json")
with p.session() as s:
    s.get("https://example.com/")
p.save()          # the cookies moved on; the identity did not

requests compatibility

import curlpro.requests as requests

r = requests.get("https://example.com/", timeout=10)

Existing code changes one import. It is a subset, and it says so: an argument the shim cannot honour is refused with a reason rather than ignored.

What is inside

A thin ctypes wrapper over a native library written in Go: the handshake is driven by uTLS, HTTP/2 by fhttp, QUIC by uquic.

The fingerprint is checked against tls.browserleaks.com: Chrome 151 gives t13d1516h2_8daaf6152771_806a8c22fdea — the same JA4 as the live browser.

Boundaries

The library covers the network layer. It does not forge the JS fingerprint (canvas, WebGL, navigator) — that is the browser's level, and the answer there is Playwright. Matching the network fingerprint is necessary but not sufficient: modern systems score JA4 together with JA4H, JA3S/JARM and behaviour.

Cleartext http:// and ws:// work too. There is no ClientHello and so no TLS fingerprint there, but the HTTP/1.1 half of the profile still applies — the header order and case — and that is all a plain-HTTP peer can see anyway. The point is not masking: a caller whose own service speaks plain HTTP, a solver or an internal API, should not need a second HTTP client beside this one.

HTTP/1.1, HTTP/2, HTTP/3 and WebSocket are supported, along with cookies, redirects, proxies (HTTP CONNECT and SOCKS5), multipart, streaming reads and uploads, and an asynchronous API.

# WebSocket: the handshake follows the profile's template, permessage-deflate works
with curlpro.Session() as s:
    with s.websocket("wss://echo.websocket.org/", max_message_size=1 << 20) as ws:
        ws.send("hello")         # str   -> a text frame
        ws.send(b"\x00\xff")     # bytes -> a binary one
        for message in ws:       # until the server closes: curlpro.WebSocketClosed;
            print(message)       # a silence timeout is CurlProError with .code == "timeout"

Three limits, each by name: connect_timeout for connecting, response_timeout for the wait until the response headers arrive, timeout for the whole request. The middle one is the gap the other two leave — a server that accepts and then thinks — and it does not bound the body. Expect(encoding="utf-8") refuses a page that came back in cp1251 instead of silently decoding it into mojibake.

# A large file goes as a stream rather than through memory
with curlpro.Session() as s:
    s.post("https://example.com/upload", body_file="archive.zip")

# The connection is reused between requests, as a browser's is. keep_alive=False
# gives every request its own — needed when a balancer pins a client to one node.
with curlpro.Session(keep_alive=False) as s:
    s.get("https://example.com/")

The HTTP/3 fingerprint is checked against Chrome 144 on quic.browserleaks.com:

with curlpro.Session("chrome-151-windows", http3=True) as s:
    print(s.get("https://quic.browserleaks.com/fp").json()["h3_text"])
    # 1:65536;6:262144;7:100;51:1;GREASE|GREASE|984832|m,a,s,p

The QPACK dynamic table is supported by a decoder of our own: the profile advertises a capacity as Chrome does, and a server that uses it gets parsed.

JA4H can be left out of the build: it is the one component under a different licence (FoxIO License 1.1, patent-pending), and -tags nofoxio excludes it while leaving every other fingerprint intact. fingerprint().ja4h_available says which build is in use. The wheels here are built with it.

Install

pip install curlpro

Nothing to compile: the native library and all 50 profiles travel inside the wheel. Wheels are built for Linux (x86-64 and ARM64, glibc 2.28+), macOS 13+ (Intel and Apple Silicon) and Windows x64. The macOS 13 floor is not ours to choose: that is what Go 1.27 requires, and the native part is built with it.

Platforms outside that list — Alpine and other musl distributions, Windows on ARM, older glibc or macOS — have no wheel. There the source archive is built by hand, and Go and a C compiler are required: pip install alone would leave the package without its native part, and the failure would come at the first call rather than at install time.

pip download curlpro --no-binary :all: --no-deps
tar -xzf curlpro-*.tar.gz && cd curlpro-*/go
CGO_ENABLED=1 go build -buildmode=c-shared -o ../curlpro/lib/libcurlpro.so ./lib

The library is looked up through CURLPRO_LIBRARY, then in curlpro/lib/, then in dist/.

Full documentation and sources — github.com/int3re/curlpro.

Metadata

Release files for curlpro 0.15.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 curlpro 0.15.0
File Size Uploaded
curlpro-0.15.0.tar.gz 614.4 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for curlpro 0.15.0
File
curlpro-0.15.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
curlpro-0.15.0-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
curlpro-0.15.0-py3-none-manylinux_2_28_aarch64.whl Python 3 none Linux glibc 2.28+ ARM64 Details
curlpro-0.15.0-py3-none-macosx_13_0_x86_64.whl Python 3 none macOS 13.0+ x86-64 Details
curlpro-0.15.0-py3-none-macosx_13_0_arm64.whl Python 3 none macOS 13.0+ ARM64 Details

Total release size: 43.1 MB

Release files / curlpro-0.15.0.tar.gz

Download URL curlpro-0.15.0.tar.gz
Size 614.4 kB
Tags Source
SHA-256 checksum
How to use checksums
7f861512acc153cd1553e6ff2edc85683b7d7af8f16416cf21b15fe2f9692e62
BLAKE2b-256 checksum
How to use checksums
9bf65656b0a92b780988e95518aa1727c1054c819a32c8679976c838cc8227b4
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 Oct 1, 2026.

Transparency log

Release files / curlpro-0.15.0-py3-none-win_amd64.whl

Download URL curlpro-0.15.0-py3-none-win_amd64.whl
Size 10.4 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
e5ae93371758cd7d976cd57fa4045df737c91e4a7da011aa21b28f806d88c7f5
BLAKE2b-256 checksum
How to use checksums
c761a4e54b0be152eb4be572f630b2747b9b2f0bcf79c5e80f33c208a9272547
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 Oct 1, 2026.

Transparency log

Release files / curlpro-0.15.0-py3-none-manylinux_2_28_x86_64.whl

Download URL curlpro-0.15.0-py3-none-manylinux_2_28_x86_64.whl
Size 10.6 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
466bc97c7eb11b279e5ac8f04ec1702e17579472b15db82721945587640f8753
BLAKE2b-256 checksum
How to use checksums
cb5f0a212a9cf69b4fe7074cf5bf23cd9f3cfab6b8f8fda433e4a68d8611023d
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 Oct 1, 2026.

Transparency log

Release files / curlpro-0.15.0-py3-none-manylinux_2_28_aarch64.whl

Download URL curlpro-0.15.0-py3-none-manylinux_2_28_aarch64.whl
Size 9.7 MB
Tags Linux glibc 2.28+ ARM64 Python 3
SHA-256 checksum
How to use checksums
3e9cbbac11a73ec1e7775e5694d3cc3d33643637fd5a8522c2be223f1cac2e3b
BLAKE2b-256 checksum
How to use checksums
b85d5abebb7cc2ba7d27ac55cc33c32857f04ba1d331087d445ba6b2b55be9ae
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 Oct 1, 2026.

Transparency log

Release files / curlpro-0.15.0-py3-none-macosx_13_0_x86_64.whl

Download URL curlpro-0.15.0-py3-none-macosx_13_0_x86_64.whl
Size 6.1 MB
Tags Python 3 macOS 13.0+ x86-64
SHA-256 checksum
How to use checksums
e3f2189b5eefac455a9a26fb0d12bfaad7526ea814eed3bbc56e42f1f88d9d72
BLAKE2b-256 checksum
How to use checksums
4373b07539e13e052dc035a49bb527e933d034f419f85c3421eaef7b1dce6a55
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 Oct 1, 2026.

Transparency log

Release files / curlpro-0.15.0-py3-none-macosx_13_0_arm64.whl

Download URL curlpro-0.15.0-py3-none-macosx_13_0_arm64.whl
Size 5.6 MB
Tags Python 3 macOS 13.0+ ARM64
SHA-256 checksum
How to use checksums
7ab4c651b5f7cade01778fbd1a176a0aae4a221886b61a27eb5e7bd5dbf54650
BLAKE2b-256 checksum
How to use checksums
4f0610fb61c8508945b2d48f8ff6ceac73ec4df46b5fc754b6e3a34a34fa455e
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 Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

0.17.1

6 release files

0.17.0

6 release files

This release

0.15.0 This release

6 release files

0.14.0

6 release files

0.13.0

6 release files

0.12.0

6 release files

0.11.0

6 release files

0.10.1

6 release files

0.10.0

6 release files

0.9.0

6 release files

0.8.0

6 release files

0.7.2

6 release files

0.7.1

6 release files

0.7.0

6 release files

0.6.0

6 release files

0.5.2

6 release files

0.5.1

6 release files

0.5.0

6 release files

0.4.3

6 release files

0.4.2

6 release files

0.4.1

6 release files

0.4.0

6 release files

0.3.0

6 release files

0.2.1

6 release files

0.2.0

6 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