Metadata-Version: 2.4
Name: curl-cffi-proxyhat
Version: 0.1.0
Summary: ProxyHat residential proxies for curl_cffi — TLS/JA3 browser impersonation with sticky sessions, geo-targeting and rotation.
Project-URL: Homepage, https://proxyhat.com
Project-URL: Documentation, https://docs.proxyhat.com
Project-URL: Repository, https://github.com/ProxyHatCom/curl-cffi-proxyhat
Author-email: ProxyHat <support@proxyhat.com>
License-Expression: MIT
License-File: LICENSE
Keywords: curl-cffi,curl_cffi,impersonate,ja3,proxy,proxyhat,residential-proxy,scraping,sticky-session,tls-fingerprint
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: Proxy Servers
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.11
Requires-Dist: curl-cffi>=0.6
Requires-Dist: proxyhat>=0.2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# curl-cffi-proxyhat

Route [curl_cffi](https://github.com/lexiforest/curl_cffi) requests through [ProxyHat](https://proxyhat.com?utm_source=github&utm_medium=readme&utm_campaign=curl-cffi) residential proxies — real browser TLS/JA3 impersonation plus a sticky residential IP, geo-targeting and rotation.

[![CI](https://github.com/ProxyHatCom/curl-cffi-proxyhat/actions/workflows/ci.yml/badge.svg)](https://github.com/ProxyHatCom/curl-cffi-proxyhat/actions/workflows/ci.yml)
[![Compatible with curl_cffi latest](https://github.com/ProxyHatCom/curl-cffi-proxyhat/actions/workflows/compat.yml/badge.svg)](https://github.com/ProxyHatCom/curl-cffi-proxyhat/actions/workflows/compat.yml)
[![PyPI](https://img.shields.io/pypi/v/curl-cffi-proxyhat)](https://pypi.org/project/curl-cffi-proxyhat/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

## Why

`curl_cffi` already wins half the anti-bot fight: it impersonates a real browser's TLS/JA3 and HTTP2 fingerprint, so servers see Chrome or Safari instead of Python. The other half is the exit IP — a datacenter address gives you away no matter how good the fingerprint is. This package plugs ProxyHat's residential IPs (50M+ across 148+ countries) into curl_cffi so the fingerprint *and* the IP both look like a real user. One pinned residential IP per session by default, so cookies and TLS stay coherent. No fork, no boilerplate.

## Install

```bash
pip install curl-cffi-proxyhat
```

`curl_cffi` and the official [`proxyhat`](https://pypi.org/project/proxyhat/) SDK come as dependencies.

## Quick start

```python
from curl_cffi_proxyhat import ProxyHatSession

# An API key auto-selects an active residential sub-user.
# Sticky by default: one pinned US IP for the whole session.
with ProxyHatSession(country="us", impersonate="chrome") as s:
    r = s.get("https://tls.browserleaks.com/json")
    print(r.json())          # residential IP + a Chrome TLS fingerprint
```

`ProxyHatSession` is a drop-in `curl_cffi.requests.Session` subclass: same `get`/`post`/`request` methods and the same impersonation, but every request exits through a ProxyHat residential IP.

Get an API key at [proxyhat.com](https://proxyhat.com?utm_source=github&utm_medium=readme&utm_campaign=curl-cffi).

Just want the proxy dict for a plain `curl_cffi` call?

```python
import curl_cffi
from curl_cffi_proxyhat import proxyhat_proxies

proxies = proxyhat_proxies(country="de", sticky="1h")
r = curl_cffi.get("https://example.com", impersonate="chrome", proxies=proxies)
# proxyhat_proxies(...) -> {"http": "http://...@gate.proxyhat.com:8080", "https": "..."}
```

## Credentials

Pass them explicitly or via environment variables — options win over env:

| Option | Env var | Notes |
|---|---|---|
| `api_key` | `PROXYHAT_API_KEY` | Auto-selects an active sub-user with remaining traffic |
| `sub_user` | `PROXYHAT_SUBUSER` | Pick a specific sub-user by uuid or name (with an API key) |
| `username` | `PROXYHAT_USERNAME` | Explicit gateway `proxy_username` (skips the API) |
| `password` | `PROXYHAT_PASSWORD` | Explicit gateway `proxy_password` |

## Targeting

```python
ProxyHatSession(
    protocol="http",       # or "socks5"
    country="us",          # ISO code or "any" (default)
    region="california",
    city="new_york",
    filter="high",         # AI IP-quality tier
    sticky="30m",          # session lifetime (default); sticky=False rotates every request
    impersonate="chrome",  # any curl_cffi browser target; default "chrome", None disables
    timeout=30,            # any extra kwarg is forwarded to curl_cffi's Session
)
```

The same knobs work on `proxyhat_proxies(...)` (everything except `impersonate`, which is a curl_cffi request option, not a proxy one).

### Sticky IP per session (default)

A scraping session usually makes many requests against the same site — a search, then result pages, then details. If the exit IP changed mid-session the site would see a user teleporting between cities and block it. So this package is **sticky by default**: one residential IP is pinned for the whole session (`sticky="30m"`, renewed as you work), keeping cookies and TLS fingerprint coherent.

Want a fresh IP on **every** request instead (e.g. many independent one-shot fetches)? Turn stickiness off:

```python
with ProxyHatSession(country="us", sticky=False) as s:  # rotating residential IP per request
    for url in urls:
        s.get(url)
```

Set a custom lifetime with `sticky="2h"`.

## How it works

`ProxyHatSession` resolves your gateway credentials (via the official [`proxyhat`](https://pypi.org/project/proxyhat/) SDK — an API key auto-picks an active sub-user, or pass `username`/`password`), then builds a ProxyHat connection URL with the SDK's targeting grammar (`http://<user>-country-us-sid-<id>-ttl-30m:<pass>@gate.proxyhat.com:8080`) and hands `proxies={"http": url, "https": url}` plus your `impersonate` choice to `curl_cffi.requests.Session`. From there it *is* a curl_cffi session: it speaks the impersonated browser handshake over ProxyHat's residential exit. A sticky username pins one IP; a rotating one makes the gateway hand out a fresh IP per connection. `proxyhat_proxies(...)` builds the same dict for a bare `curl_cffi.get(...)` when you don't need a session.

## License

MIT © [ProxyHat](https://proxyhat.com)
