Route Selenium Chrome through ProxyHat residential proxies — authenticated gateway proxies that just work in vanilla Selenium, plus a sticky residential IP pinned for the whole session, geo-targeting, and rotation.
Tip
Recommended proxies — ProxyHat residential IPs. Every feature in this package is tested end-to-end against ProxyHat and works great. First-class integration; also works with any proxy, or none.
Chrome's --proxy-server flag can't carry a username and password, so pointing Selenium at a credentialed residential gateway normally means an ugly page.authenticate / CDP dance or an extra proxy dependency. And running a real browser from a datacenter IP gets flagged, CAPTCHA-walled, and blocked anyway.
selenium-proxyhat fixes both. It plugs ProxyHat's residential IPs (50M+ across 148+ countries) into Selenium and handles gateway auth by auto-generating a tiny in-memory Chrome extension that answers the proxy's auth challenge — works with vanilla Selenium, no selenium-wire required. One pinned residential IP per session by default, so cookies and fingerprint stay consistent while your script works.
pip install selenium-proxyhatselenium>=4.10 and proxyhat come with it. Bring your own Chrome + chromedriver (Selenium Manager resolves the driver for you).
from selenium_proxyhat import proxyhat_driver
# An API key (PROXYHAT_API_KEY) auto-selects an active residential sub-user:
driver = proxyhat_driver(country="us") # sticky US IP for the whole session
driver.get("https://httpbin.org/ip")
print(driver.find_element("tag name", "body").text)
driver.quit()Get an API key at proxyhat.com.
Prefer to build the driver yourself? Get configured ChromeOptions (proxy flag + auth extension already applied) and launch Chrome your way:
from selenium import webdriver
from selenium_proxyhat import proxyhat_chrome_options
options = proxyhat_chrome_options(country="de", sticky="1h")
options.add_argument("--headless=new") # add whatever else you need
driver = webdriver.Chrome(options=options)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 |
proxyhat_chrome_options(
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
)Same keyword arguments work on proxyhat_driver(...) and proxyhat_seleniumwire_options(...).
A browser session takes many steps against the same site — logging in, clicking, scrolling. 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 fingerprint coherent.
Want a fresh IP on every request instead (e.g. many independent one-shot fetches)? Turn stickiness off:
proxyhat_chrome_options(country="us", sticky=False) # rotating residential IP per connectionSet a custom lifetime with sticky="2h".
Chrome takes the proxy host/port from --proxy-server=gate.proxyhat.com:8080, but a residential gateway needs a username (the ProxyHat targeting string) and password. Since the flag can't carry them, proxyhat_chrome_options generates a minimal Manifest V3 extension in memory whose background service worker answers chrome.webRequest.onAuthRequired with your credentials, and adds it via ChromeOptions.add_extension. The MV3 blocking auth listener is enabled by the webRequestAuthProvider permission. Nothing is written to your project — the packed extension lives in a temp file that Chrome reads at launch.
The targeting username (e.g. <user>-country-us-sid-<id>-ttl-30m) is built by the official proxyhat SDK, so a sticky session mints a single session id shared across the run.
Already using selenium-wire? Skip the extension and let selenium-wire carry credentials in the upstream-proxy URL:
from seleniumwire import webdriver # pip install selenium-proxyhat[seleniumwire]
from selenium_proxyhat import proxyhat_seleniumwire_options
driver = webdriver.Chrome(
seleniumwire_options=proxyhat_seleniumwire_options(country="us"),
)
# -> {"proxy": {"http": "http://<user>:<pass>@gate.proxyhat.com:8080", "https": ..., "no_proxy": ...}}selenium-wire is an optional extra; proxyhat_seleniumwire_options() only builds the dict and never imports it. It also supports protocol="socks5" for an authenticated SOCKS5 upstream (which the extension path does not).
MIT © ProxyHat