Python · setup guide

HTTPX proxy setup

An HTTPX proxy is one argument: proxy= on httpx.get, Client or AsyncClient, with the credentials inside the URL. The two things that trip people up are the older proxies= spelling, which 0.28 removed, and a default timeout of five seconds that a slow residential exit can run past.
You will need

Residentialresi.proxymonkey.io:8000

ISP or datacenterIP:PORT

CredentialsUSER:PASS

Copy your own from the dashboard, which also lists the host and port for every order. Where it differs from this page, the dashboard is right.

Install

Python 3.9 or newer. Add the socks extra, httpx[socks], only if you connect over SOCKS5.

terminal
pip install httpx

Rotating residential

The module-level httpx.get builds a fresh client, and so a fresh connection, on every call. Each one opens its own tunnel through the residential gateway, so the three lines should print three different addresses.

rotate.py
import httpx

PROXY = "http://USER:[email protected]:8000"

for _ in range(3):
    r = httpx.get("https://httpbin.org/ip", proxy=PROXY, timeout=30)
    print(r.json()["origin"])

A static datacenter or ISP IP

On a static datacenter IP, keep one client open so connections are reused. This is the async version: one AsyncClient fetching several URLs at once, with the connect timeout set apart from the rest. For an ISP order, use the host and port your dashboard lists for it.

static_async.py
import asyncio

import httpx

PROXY = "http://USER:PASS@IP:PORT"
URLS = ["https://httpbin.org/ip", "https://httpbin.org/headers"]


async def main():
    timeout = httpx.Timeout(30, connect=10)
    async with httpx.AsyncClient(proxy=PROXY, timeout=timeout) as client:
        responses = await asyncio.gather(*(client.get(url) for url in URLS))
        for r in responses:
            print(r.status_code, r.text[:80])


asyncio.run(main())

Keeping one identity

A Client or AsyncClient keeps a connection pool, and an HTTPS request through the proxy rides a tunnel that exits from one address. Reuse the client and requests to the same host share that tunnel and that IP, even on the rotating gateway, until the connection idles out. That suits a short multi-step flow and surprises you when you expected rotation. For a residential address that holds across new connections, use a sticky session; the session setting for your account is in the dashboard. python-telegram-bot runs on HTTPX, so the same pooling applies to a bot; Telegram bots with python-telegram-bot covers its two proxy settings.

Specific to HTTPX

Things worth knowing

The HTTPX proxy argument is singular now

HTTPX 0.26 added proxy= and deprecated proxies=; 0.28 removed the old one. To send different targets through different proxies, mounts= maps URL patterns to transports that each carry their own proxy. The most specific pattern wins.

mounts.py
import httpx

client = httpx.Client(mounts={
    "all://": httpx.HTTPTransport(proxy="http://USER:[email protected]:8000"),
    "all://example.com": httpx.HTTPTransport(proxy="http://USER:PASS@IP:PORT"),
})

Rotating residential inside one AsyncClient

A client per request is fine for a script and wasteful for ten thousand URLs. Keep one AsyncClient and tell it to keep no idle connections: every request then opens a new tunnel, which on the rotating gateway means a new address. max_connections caps how many run at once.

pool.py
import httpx

limits = httpx.Limits(max_connections=20, max_keepalive_connections=0)
client = httpx.AsyncClient(proxy="http://USER:[email protected]:8000", limits=limits, timeout=30)

Estimate billed bytes from the response

We meter request bytes plus response bytes, headers included. HTTPX exposes both sets of headers and num_bytes_downloaded, the body as it came off the wire before decompression. The sum below skips the request and status lines, and TLS overhead is real traffic too, so expect our figure to be slightly higher.

wire_bytes.py
import httpx


def wire_bytes(r: httpx.Response) -> int:
    def head(headers):
        return sum(len(k) + len(v) + 4 for k, v in headers.raw)

    sent = head(r.request.headers) + len(r.request.content)
    received = head(r.headers) + r.num_bytes_downloaded
    return sent + received
When it breaks

Common errors and fixes

  • httpx.ProxyError: 407 Proxy Authentication Required

    Why
    The gateway turned down the CONNECT for an HTTPS URL: wrong credentials, or a password with @, : or / that broke the URL. On a plain http:// target the same failure comes back as a response with status 407 instead of an exception.
    Fix
    Quote the username and password with urllib.parse.quote(value, safe='') before building the URL, and copy them fresh from the dashboard.
  • httpx.ReadTimeout or httpx.ConnectTimeout after about five seconds

    Why
    HTTPX times out after five seconds by default, which a slow residential exit or a heavy page can exceed.
    Fix
    Pass timeout=httpx.Timeout(30, connect=10) and retry on httpx.TimeoutException. Through the rotating gateway, a retry on a new connection leaves from a new address.
  • TypeError: Client.__init__() got an unexpected keyword argument 'proxies'

    Why
    Code written for HTTPX before 0.28, which removed proxies=.
    Fix
    Use proxy= for a single proxy, or mounts= with an HTTPTransport(proxy=...) per URL pattern.
  • httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED]

    Why
    The target's certificate, or a CA your environment does not trust. The proxy passes TLS through the tunnel untouched. If the message says WRONG_VERSION_NUMBER instead, the proxy URL starts with https:// and HTTPX is trying to speak TLS to the gateway.
    Fix
    Update certifi, or pass verify= an ssl.SSLContext loaded with your CA. Keep the proxy URL on http://. verify=False is for a one-off test only.

A failed connection that moved no data is not billed. Retries you send are billed like any other request, and each shows as its own line in your usage log.

The community layer

HTTPX still misbehaving?

Paste the error and the few lines that set up the proxy into Discord, with the password taken out. Someone there has seen it before.

Join the Discord

4,200+monkeys in the Discord

  • Help from humans

    Post your error, get an answer. Usually in minutes, usually from someone who has hit the same wall.

  • A status bot that tells on us

    Pool health, incidents and maintenance posted automatically. Including the bad days.

  • Deals and free traffic

    Bonus GB drops, early access to new pools, and the occasional giveaway for a good bug report.

Join the Discord4,200+ monkeys, free to lurk