15% off your first purchase with code WELCOME15, up to 25 GB.

See pricing

Integration · HTTP clients

How to use a proxy with HTTPX

In HTTPX, the proxy goes on the client: httpx.Client(proxy="http://user:pass@host:port"), or the same on AsyncClient. The older proxies= argument was deprecated in 0.26 and removed in 0.28, which is why code copied from older answers now fails with a TypeError.

Reviewed 30 Oct 2026

your proxy, as a URL
http://USERNAME_pool-arcfast_country-de:PASSWORD@proxy.arcproxies.io:8000

The short answer

  • HTTPX 0.28 takes proxy= (one URL) or mounts= (routing per scheme or domain); proxies= is gone.
  • One client per identity: the proxy is fixed when the client is created, so a new sticky session means a new client.
  • Rejected credentials raise httpx.ProxyError on HTTPS targets; the built-in transport retries only connection errors.
  • SOCKS5 needs httpx[socks], then a socks5:// proxy URL.

Setup

Set up HTTPX with a proxy, step by step

  1. 01

    Install HTTPX

    Check the version first if you are reusing older code: anything that passes proxies= needs 0.27 or earlier.

    shell
    pip install httpxpip install "httpx[socks]"     # optional, for SOCKS5python -c "import httpx; print(httpx.__version__)"
  2. 02

    Create a client with the proxy and check the exit

    The credentials are the userinfo part of the proxy URL. The IP echo response shows the exit the target sees.

    check_exit.py
    import httpxwith httpx.Client(proxy="http://USERNAME_pool-arcfast_country-de:PASSWORD@proxy.arcproxies.io:8000", timeout=30) as client:    r = client.get("https://api.ipify.org?format=json")    print(r.json())   # {"ip": "…"}
  3. 03

    Async: one client per sticky session

    AsyncClient takes the same argument. Because the proxy is set per client, a task that needs its own IP gets its own client with its own _session- handle. A semaphore caps concurrent tasks.

    tasks.py
    import asyncioimport secretsimport httpxlimit = asyncio.Semaphore(10)async def crawl_listing(start_url, pages=3):    proxy = f"http://USERNAME_pool-arcfast_country-de_session-{secrets.token_hex(4)}_sesstime-10:PASSWORD@proxy.arcproxies.io:8000"    async with limit, httpx.AsyncClient(proxy=proxy, timeout=30) as client:        out = []        for page in range(1, pages + 1):            r = await client.get(start_url, params={"page": page})            out.append(r.status_code)        return outasync def main():    starts = ["https://example.com/c/lamps", "https://example.com/c/chairs"]    print(await asyncio.gather(*(crawl_listing(u) for u in starts)))asyncio.run(main())
  4. 04

    Route different domains through different pools with mounts

    mounts maps URL patterns to transports, each with its own proxy. Here one domain goes through ARC Basic (_pool-arc, rotating ISP) and everything else through ARC Fast in France. The most specific pattern wins.

    mounts.py
    import httpxmounts = {    "all://api.example.com": httpx.HTTPTransport(        proxy="http://USERNAME_pool-arc_country-de:PASSWORD@proxy.arcproxies.io:8000",    ),    "all://": httpx.HTTPTransport(        proxy="http://USERNAME_pool-arcfast_country-fr:PASSWORD@proxy.arcproxies.io:8000",    ),}with httpx.Client(mounts=mounts, timeout=30) as client:    print(client.get("https://api.example.com/v1/items").status_code)    print(client.get("https://example.fr/catalogue").status_code)
  5. 05

    Handle 407, timeouts and rate limits

    Catch httpx.ProxyError and check for 407: that is a configuration problem, not a transient one. Retry timeouts and 429/5xx on a new handle with backoff. HTTPTransport(retries=…) only covers ConnectError and ConnectTimeout, so status-based retries are yours to write.

    fetch.py
    import secretsimport timeimport httpxdef fetch(url, attempts=4):    for attempt in range(attempts):        proxy = f"http://USERNAME_pool-arcfast_country-de_session-{secrets.token_hex(4)}_sesstime-5:PASSWORD@proxy.arcproxies.io:8000"        transport = httpx.HTTPTransport(proxy=proxy, retries=1)   # connect errors only        try:            with httpx.Client(transport=transport, timeout=30) as client:                r = client.get(url)        except httpx.ProxyError as err:            if "407" in str(err):                raise RuntimeError("proxy rejected the credentials") from err        except httpx.TimeoutException:            pass        else:            if r.status_code == 407:                raise RuntimeError("proxy rejected the credentials")            if r.status_code != 429 and r.status_code < 500:                return r        time.sleep(2 ** attempt)    raise RuntimeError(f"gave up on {url}")

HTTPX proxy arguments by version

Most broken HTTPX proxy code is version drift. The changelog is explicit:

Versionproxies=proxy=mounts=
0.25 and earlierSupportedNot availableSupported
0.26 (Dec 2023)Deprecated, still worksAddedSupported
0.28 (Nov 2024) and laterRemovedSupportedSupported

A proxies={"http://": …, "https://": …} dict translates to mounts={"http://": httpx.HTTPTransport(proxy=…), "https://": httpx.HTTPTransport(proxy=…)}, or to a single proxy= when both point at the same gateway, which is the ARC case.

Rotation, keep-alive and connection limits

A client pools connections (by default up to 100, with 20 kept alive). An HTTPS request runs inside a CONNECT tunnel that stays on one exit, so a rotating username on a long-lived client can keep the same IP for many requests to one host. For a guaranteed change, use a new _session- handle and a new client; for a guaranteed hold, use a sticky handle and keep the client.

httpx.Limits(max_connections=…, max_keepalive_connections=…) caps how hard one client pushes. Throughput through more exits does not change a site's own limits; handling 429 rate limits covers pacing.

SOCKS5 in HTTPX

With httpx[socks] installed, httpx.Client(proxy="socks5://user:pass@host:port") works against the same gateway port with the same credentials. There is no advantage over http:// for HTTPS targets, which are encrypted end to end either way; use SOCKS5 when a tool in the chain only speaks SOCKS.

The problem

Common pitfalls

proxies= on HTTPX 0.28

Raises TypeError: Client.__init__() got an unexpected keyword argument 'proxies'. Switch to proxy= or mounts=.

Passing both proxy= and transport=

Put the proxy on the transport (HTTPTransport(proxy=…, retries=…)) when you need transport options; a client-level proxy= builds its own transports for matching URLs.

One client shared by tasks that need different IPs

The proxy is fixed per client. Tasks that need separate sticky sessions need separate clients.

trust_env picking up a stray proxy

HTTPX reads HTTP_PROXY and HTTPS_PROXY by default (trust_env=True). An explicit proxy= makes the intent clear.

FAQ

Questions

Something not covered? Write to contact@arcproxies.io.

How do I set a proxy in HTTPX?

Pass it when creating the client: httpx.Client(proxy="http://user:pass@host:port") or httpx.AsyncClient(proxy=…). For one-off calls, httpx.get(url, proxy=…) takes the same argument.

Why does HTTPX say unexpected keyword argument 'proxies'?

proxies= was deprecated in HTTPX 0.26 and removed in 0.28. Use proxy= for a single proxy URL, or mounts= with httpx.HTTPTransport(proxy=…) to route different schemes or domains differently.

How do I use different proxies for different sites in HTTPX?

Use mounts: a dict from URL patterns such as "all://example.com" to httpx.HTTPTransport(proxy=…). The most specific pattern wins, and "all://" catches everything else.

Does HTTPX retry failed proxy requests?

Only connection failures, and only if you set HTTPTransport(retries=n): it retries ConnectError and ConnectTimeout. Retries on 429 or 5xx responses, or on read timeouts, are up to your code.

Sources & review

Last reviewed by the ARC Proxies team. Prices are read from the live catalog at build time.

  1. [1]HTTPX: Proxies
  2. [2]HTTPX: Transports: retries and mounts
  3. [3]encode/httpx: CHANGELOG (0.26.0, 0.28.0)

Next

What people build with it

Use caseProxies for web scraping and data collectionA second provider that just works — Scrapy, requests, Crawlee or Playwright, self-serve, no sales call, bandwidth that never expires.Use caseProxies for AI and LLM training-data collectionCollect public web text, images and product data at corpus scale from residential and ISP IPs, paid per gigabyte with no monthly commitment.Use caseProxies for SEO and SERP monitoringCollect search results the way a searcher in that city sees them — unpersonalised, per country and language, at a price per GB that survives daily runs.GlossaryHTTP CONNECTHTTP CONNECT is the HTTP request method a client sends to a proxy to ask for a tunnel to a host and port; once the proxy answers with a 2xx status it blindly forwards bytes in both directions, which is how HTTPS passes through an HTTP proxy with TLS intact end to end (RFC 9110, section 9.3.6).GlossarySOCKS5SOCKS5 is a proxy protocol, standardised in RFC 1928 in 1996, that relays TCP connections and UDP datagrams between a client and a destination without interpreting the application traffic, with username/password authentication defined separately in RFC 1929.GuideHandling 429 rate limitsWhat HTTP 429 means, how to read Retry-After, and how to retry politely with exponential backoff, jitter and per-host concurrency limits. With code.IntegrationaiohttpUse an authenticated proxy with aiohttp: proxy= per request or per session, why proxy_auth is deprecated, SOCKS5 through aiohttp-socks, and 407 handling.IntegrationPython requestsUse an authenticated proxy with Python requests: the proxies argument, sticky sessions per task, geo-targeting, SOCKS5 and retries that skip 407 errors.

Integrations

Other http clients

Python requestsUse an authenticated proxy with Python requests: the proxies argument, sticky sessions per task, geo-targeting, SOCKS5 and retries that skip 407 errors.aiohttpUse an authenticated proxy with aiohttp: proxy= per request or per session, why proxy_auth is deprecated, SOCKS5 through aiohttp-socks, and 407 handling.AxiosUse an authenticated proxy with Axios in Node.js: the proxy option on 1.16.1+, https-proxy-agent for older versions, sticky sessions, SOCKS5 and 407s.Node.js fetchNode's built-in fetch has no proxy option. Use undici's ProxyAgent as the dispatcher, or NODE_USE_ENV_PROXY, with sticky sessions, SOCKS5 and 407 handling.curlSend curl through an authenticated proxy with -x and -U, check the exit IP, test sticky sessions and countries, use socks5h, and read 407 failures.GoRoute Go's net/http through an authenticated proxy: Transport.Proxy with url.UserPassword, a sticky session per request context, SOCKS5 and 407 checks.RubyUse an authenticated proxy in Ruby: Net::HTTP proxy arguments, Faraday proxy options, sticky sessions per task, geo-targeting and 407 handling.PHP GuzzleRoute PHP Guzzle and plain cURL through an authenticated proxy: the proxy option, sticky sessions, geo-targeting, retries that stop on a 407.Java HttpClientUse an authenticated proxy with java.net.http.HttpClient: ProxySelector, Authenticator, the Basic-over-CONNECT setting, sticky sessions and 407s.C# HttpClientUse an authenticated proxy in .NET: WebProxy and NetworkCredential on SocketsHttpHandler, a client per sticky session, SOCKS5 and 407 handling.Rust reqwestRoute Rust reqwest through an authenticated proxy: Proxy::all with basic_auth, a Client per sticky session, socks5h, timeouts and 407 errors.wgetRun GNU Wget through an authenticated proxy: https_proxy, --proxy-user, a .wgetrc for credentials, sticky sessions for mirrors and exit codes.PostmanSend Postman requests through an authenticated proxy: the custom proxy settings, Proxy auth, checking the exit IP, sessions, countries and Newman.DockerGive Docker containers and builds an authenticated proxy: HTTP_PROXY, HTTPS_PROXY and NO_PROXY, build args, Compose, and why the daemon proxy differs.

Get started

Get credentials and paste them in.

Sign up, buy a gigabyte, generate a username in the dashboard. Code WELCOME15 takes 15% off your first purchase; bandwidth never expires.