A Python requests proxy is set with a proxies dict whose http and https keys both hold the proxy URL, with the username and password inside it. That is the whole setup: requests tunnels HTTPS through the proxy with CONNECT, so your TLS session still ends at the target.

import requests

proxy = "http://USERNAME:PASSWORD@HOST:PORT"
r = requests.get("https://api.ipify.org?format=json", proxies={"http": proxy, "https": proxy}, timeout=(5, 30))
print(r.json())

Every snippet on this page ran on 2026-09-29 with requests 2.34 and urllib3 2.8 against a local authenticating HTTP proxy and a SOCKS5 proxy that enforces username and password.

Before you start: copy your proxy details

  1. Open your order in the dashboard at https://app.proxyhive.io.
  2. Copy HOST, PORT, USERNAME and PASSWORD. Each ISP or datacenter IP is its own endpoint, and the order lists separate HTTP, HTTPS and SOCKS5 ports for every IP. The dashboard can also copy a line as USER:PASS@HOST:PORT and three other orderings, which saves some typing.
  3. Keep credentials out of your code. Export them once per shell:
export PROXY_HOST=HOST PROXY_PORT=PORT PROXY_USER=USERNAME PROXY_PASS=PASSWORD

If you are still picking a line, static ISP proxies are sold from a single IP, which is plenty for trying this guide end to end.

Python requests proxy setup: the proxies dict

Build the URL from the environment and reuse it. Percent-encode the password if it contains @, : or /, or requests will split the URL in the wrong place.

import os
from urllib.parse import quote

import requests

user = quote(os.environ["PROXY_USER"], safe="")
password = quote(os.environ["PROXY_PASS"], safe="")
proxy = f"http://{user}:{password}@{os.environ['PROXY_HOST']}:{os.environ['PROXY_PORT']}"
proxies = {"http": proxy, "https": proxy}

r = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=(5, 30))
print(r.status_code, r.json())

The dict keys name the scheme of the target URL, not the proxy. "https": "http://..." is correct and is what you want for HTTPS sites: the proxy only sees the hostname in the CONNECT line, never the path, headers or body.

Using a requests Session with a proxy

A Session keeps connections alive, which matters when every new TLS handshake runs through the proxy.

session = requests.Session()
session.proxies.update(proxies)
print(session.get("https://httpbin.org/ip", timeout=(5, 30)).json())

One trap, confirmed in our run: if HTTPS_PROXY is set in the environment, it overrides session.proxies because trust_env is on by default. The requests documentation says the same. Either pass proxies= on each call or turn environment lookup off with session.trust_env = False.

Authentication: username and password, or an IP allowlist

Credentials in the URL are sent as a Proxy-Authorization: Basic header. If you would rather not ship a password at all, switch the IP to allowlist authentication on the order in the dashboard and add your server's public IP. The URL then drops the credentials:

proxy = f"http://{os.environ['PROXY_HOST']}:{os.environ['PROXY_PORT']}"

The allowlist suits fixed servers. For laptops and CI runners whose address changes, stay with username and password.

Proxy environment variables: HTTP_PROXY, HTTPS_PROXY and trust_env

requests reads HTTP_PROXY, HTTPS_PROXY, ALL_PROXY and NO_PROXY (either case) when you pass no proxies:

export HTTPS_PROXY="http://$PROXY_USER:$PROXY_PASS@$PROXY_HOST:$PROXY_PORT"
python -c 'import requests; print(requests.get("https://api.ipify.org?format=json").json())'

This is handy for third-party code you cannot edit. It is also how a stray variable in a Docker image quietly routes traffic somewhere you did not expect, so set trust_env = False on sessions that must go direct.

SOCKS5 proxy with requests[socks]

Install the extra, then use the SOCKS5 port from the order:

pip install "requests[socks]"
socks = f"socks5h://{user}:{password}@{os.environ['PROXY_HOST']}:SOCKS5_PORT"
r = requests.get("https://api.ipify.org?format=json", proxies={"http": socks, "https": socks}, timeout=(5, 30))

The h matters. With socks5h:// the proxy resolves the hostname; with socks5:// your machine resolves it and sends only the IP. We watched both on the wire: socks5h sent api.ipify.org, socks5 sent a bare address. Use socks5h unless you have a reason not to. The HTTP vs SOCKS5 guide covers when SOCKS5 is worth it at all.

Verify the exit IP

Compare the address with and without the proxy. The proxied one should be the IP on your order.

direct = requests.get("https://api.ipify.org?format=json", timeout=10).json()["ip"]
via = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10).json()["ip"]
print(direct, "->", via)

Rotate across several static IPs

With several ISP or datacenter IPs, cycle through their endpoints. Put them in one variable, comma-separated:

import itertools
import os

import requests

endpoints = os.environ["PROXY_URLS"].split(",")
pool = itertools.cycle(endpoints)

for url in ["https://httpbin.org/ip"] * 3:
    proxy = next(pool)
    r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=(5, 30))
    print(r.json())

Round-robin is the simplest fair policy. For random choice, cooldowns and dropping a failing IP, see how to rotate proxies in Python.

Timeouts and retries with urllib3 Retry

requests has no default timeout, so a stalled connection can hang a worker forever. Always pass timeout=(connect, read). For retries, mount an adapter:

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(
    total=3,
    backoff_factor=1,
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET", "HEAD"],
)
session = requests.Session()
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies.update(proxies)

Retry honours Retry-After on 429 and 503 by default. Keep allowed_methods to idempotent verbs so a retried POST does not submit twice.

Common errors and fixes

ErrorLikely causeFix
ProxyError ... Tunnel connection failed: 407 Proxy Authentication RequiredWrong username or password, or a password with unencoded special charactersRe-copy from the order; quote() the password
Status 407 on an http:// targetSame as above, returned as a response instead of an exceptionSame fix
InvalidSchema: Missing dependencies for SOCKS supportrequests[socks] not installedpip install "requests[socks]"
ConnectTimeout or a refused connectionWrong host or port, or a port for a different protocolCheck you copied the port for the protocol in your URL
407 or a refusal with no credentials in the URLAllowlist auth, but the request comes from an IP not on the listAdd your current public IP to the order, or use credentials
Session goes direct or elsewhereHTTPS_PROXY overriding session.proxiessession.trust_env = False

For status codes from the target itself (403, 429, 503), see proxy error codes explained.

Next steps