Home/Blog/HTTPX Proxy Setup
Developer Guide

HTTPX Proxy Setup in Python

HTTPX is the modern Python HTTP client with first-class sync and async APIs. This guide covers the current proxy parameter, per-route mounts, SOCKS5 via the httpx[socks] extra, authentication, and how to pair an AsyncClient with a dedicated mobile IP.

8 min read·Python·Last updated: July 2026

Quick Answer

Pass a proxy URL to the client with the singular proxy argument: httpx.Client(proxy="http://user:pass@host:port"). The same argument works on httpx.AsyncClient. The old plural proxies argument was deprecated in HTTPX 0.26 and removed in 0.28, so code copied from older tutorials raises a TypeError on current versions.

  • proxy= takes one URL for all traffic; use mounts= for per-scheme or per-host routing
  • SOCKS5 needs pip install httpx[socks], which pulls in the socksio package
  • Credentials go in the URL userinfo: scheme://username:password@host:port

The examples below use the placeholder credentials from a mobileproxies.org dashboard: host proxy.mobileproxies.org, HTTP port 8000, SOCKS5 port 1080, user u_4a9c, password p_2X7q. Substitute your own values. Everything shown is documented HTTPX configuration; the patterns apply to any HTTP or SOCKS5 proxy endpoint, including the dedicated 4G/5G proxies used for web scraping.

The proxy parameter (sync client)

The supported way to route an HTTPX client through a proxy is the proxy argument at client initialization. It accepts a single proxy URL that handles all requests made by that client:

import httpx

PROXY = "http://u_4a9c:p_2X7q@proxy.mobileproxies.org:8000"

with httpx.Client(proxy=PROXY, timeout=20.0) as client:
    resp = client.get("https://api.ipify.org?format=json")
    resp.raise_for_status()
    print(resp.json())  # egress IP of the mobile proxy, not your machine

The top-level shortcut functions accept it too, for example httpx.get(url, proxy=PROXY), though a Client is preferred for more than one request because it reuses connections.

proxies= no longer exists

Per the official changelog, HTTPX 0.26.0 (December 2023) added proxy and deprecated the dict-style proxies argument, and 0.28.0 (November 2024) removed proxies entirely. If you see TypeError: ... unexpected keyword argument 'proxies', replace it with proxy= (one URL) or mounts= (per-route routing, below).

Proxy authentication

HTTPX reads proxy credentials from the userinfo section of the proxy URL, as shown in the official docs: http://username:password@localhost:8030. If your password contains characters like @, : or /, URL-encode the parts first:

import os
from urllib.parse import quote

import httpx

user = quote(os.environ["MP_USER"])        # e.g. u_4a9c
password = quote(os.environ["MP_PASS"])    # e.g. p_2X7q
host = "proxy.mobileproxies.org"

PROXY = f"http://{user}:{password}@{host}:8000"

with httpx.Client(proxy=PROXY) as client:
    print(client.get("https://api.ipify.org").text)

Keep credentials in environment variables or a secrets manager rather than hard-coding them; the proxy URL ends up in tracebacks and logs surprisingly easily.

Async: AsyncClient with a proxy

httpx.AsyncClient takes exactly the same proxy, mounts and trust_env parameters as the sync client, per the HTTPX API reference. That makes concurrent fetching through one authenticated proxy a small amount of code:

import asyncio
import httpx

PROXY = "http://u_4a9c:p_2X7q@proxy.mobileproxies.org:8000"

URLS = [
    "https://example.com/page/1",
    "https://example.com/page/2",
    "https://example.com/page/3",
]

async def fetch(client: httpx.AsyncClient, url: str) -> int:
    resp = await client.get(url)
    return resp.status_code

async def main() -> None:
    limits = httpx.Limits(max_connections=5)
    async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=20.0) as client:
        results = await asyncio.gather(*(fetch(client, u) for u in URLS))
        print(results)

asyncio.run(main())

One practical note when the upstream is a dedicated mobile proxy: every concurrent request exits from the same carrier IP, because a dedicated slot is one SIM and one device. That is the point - the IP is consistently yours and carries carrier-grade reputation - but it also means concurrency should be tuned to look like one busy client, not a thousand. Capping httpx.Limits(max_connections=...) to a small number is usually the right call. Request patterns matter as much as the IP itself, as we cover in how websites detect proxies.

SOCKS5 proxies in HTTPX

SOCKS support is not built into the base install. The official docs require the optional extra, which installs the socksio package:

pip install "httpx[socks]"

Then use a socks5:// proxy URL, with credentials in the same userinfo position. Since HTTPX 0.28.0 the changelog also lists socks5h as a valid proxy scheme, which resolves DNS on the proxy instead of locally:

import httpx

# socks5://  -> DNS resolved locally
# socks5h:// -> DNS resolved by the proxy (HTTPX 0.28+)
PROXY = "socks5://u_4a9c:p_2X7q@proxy.mobileproxies.org:1080"

with httpx.Client(proxy=PROXY) as client:
    print(client.get("https://api.ipify.org").text)

# Works identically on the async client:
# async with httpx.AsyncClient(proxy=PROXY) as client: ...

Whether SOCKS5 or HTTP CONNECT is the better fit depends on the workload; the differences are smaller than most posts suggest, and we break them down in SOCKS5 vs HTTP proxy. If you are on the requests library instead of HTTPX, the equivalent setup lives in our Python requests SOCKS5 proxy example.

Per-route proxies with mounts

When one URL is not enough - different proxies per scheme, a proxy only for certain hosts, or some traffic going direct - HTTPX uses the mounts parameter. You map URL patterns to transport instances, each carrying its own proxy:

import httpx

PROXY = "http://u_4a9c:p_2X7q@proxy.mobileproxies.org:8000"

mounts = {
    # Note: the value for the "https://" key still uses an
    # http:// proxy URL. The docs call this out explicitly -
    # it is the scheme of the proxy connection, not the target.
    "http://": httpx.HTTPTransport(proxy=PROXY),
    "https://": httpx.HTTPTransport(proxy=PROXY),
}

with httpx.Client(mounts=mounts) as client:
    ...

Patterns can also target hosts. A mount key of "all://" matches everything, and wildcard forms like "all://*example.org" match a domain and its subdomains, so you can proxy only the sites that need a mobile IP and let internal traffic go direct (URLs that match no mount fall back to the client default transport, which connects directly when no proxy= is set):

import httpx

MOBILE = "http://u_4a9c:p_2X7q@proxy.mobileproxies.org:8000"

mounts = {
    # Only target-site traffic uses the mobile proxy slot us-mob-01
    "all://*target-site.com": httpx.HTTPTransport(proxy=MOBILE),
}

# Async variant: same mapping, async transport class
async_mounts = {
    "all://*target-site.com": httpx.AsyncHTTPTransport(proxy=MOBILE),
}

client = httpx.Client(mounts=mounts)
# async_client = httpx.AsyncClient(mounts=async_mounts)

For AsyncClient, mount httpx.AsyncHTTPTransport instances; the class accepts the same proxy argument as its sync counterpart.

Environment variables and trust_env

By default HTTPX honors the HTTP_PROXY, HTTPS_PROXY and ALL_PROXY environment variables, which set the proxy for http, https, or all requests respectively. That is convenient in containers, but it can silently route traffic you did not intend to proxy. To ignore the environment, set trust_env=False:

import httpx

# Explicit proxy only; ignore HTTP_PROXY / HTTPS_PROXY / ALL_PROXY
client = httpx.Client(
    proxy="http://u_4a9c:p_2X7q@proxy.mobileproxies.org:8000",
    trust_env=False,
)

The reverse is also useful: exporting ALL_PROXY once lets every HTTPX script in a job inherit the proxy without code changes.

Version cheat sheet

HTTPX versionProxy behavior (per official changelog)
0.22.0 (Jan 2022)SOCKS5 support added via the socksio package
0.26.0 (Dec 2023)proxy argument added; proxies deprecated
0.28.0 (Nov 2024)proxies removed; socks5h accepted as a proxy scheme

Where a dedicated mobile IP fits

HTTPX does not care what kind of proxy sits behind the URL, but the target site does. A dedicated 4G/5G mobile proxy is a real SIM in a real modem, so your HTTPX traffic exits from a carrier ASN address that ordinary phone subscribers share. The honest tradeoffs: a dedicated slot is one IP (rotated on demand, not per request), mobile bandwidth is slower than datacenter lines, and it costs more per IP. In exchange you get an egress identity that is stable, geolocated to a real market (US, UK, France or Netherlands), and far more expensive for sites to block outright - the same reasons detection systems treat carrier ranges carefully, as explained in how websites detect proxies. For scraping workloads where session consistency beats raw request volume, that combination is usually worth it - see our web scraping solutions for the broader setup.

Sources

Related Guides

Point HTTPX at a real mobile IP

Dedicated 4G/5G proxies on real carrier SIMs in the US, UK, France and Netherlands. HTTP and SOCKS5 endpoints that drop straight into the proxy= examples above.