Chat with us, powered by LiveChat
Tutorial

Python requests SOCKS5 Proxy Examplewith Auth, DNS, Retries & Rotation

Complete guide to using SOCKS5 proxies with Python requests library including authentication, DNS resolution, error handling, and proxy rotation patterns.

Oct 10, 2024
15 min read

Quick Start: Minimal SOCKS5 example

import requests

# For credentials with special chars, use URL encoding:
# from urllib.parse import quote
# PROXY = f"socks5h://{quote('user@123')}:{quote('p@ss!')}@host:port"
PROXY = "socks5h://username:password@host:port"  # e.g. socks5h://user:pass@1.2.3.4:1080
proxies = {"http": PROXY, "https": PROXY}

resp = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=20)
resp.raise_for_status()
print(resp.json())

Important Notes

  • • Install SOCKS support with pip install "requests[socks]" (this pulls in PySocks)
  • socks5h ensures the proxy does DNS resolution. Plain socks5 resolves locally

Production-Ready Session: Timeouts + Retries (with backoff)

Use a Session, mount an adapter with urllib3.Retry, and share connections efficiently:

import os
from urllib.parse import quote
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# Build a socks5h proxy URL (remote DNS)
user = os.getenv("PROXY_USER")
pw = os.getenv("PROXY_PASS", "")
host = os.getenv("PROXY_HOST", "127.0.0.1")
port = int(os.getenv("PROXY_PORT", "1080"))

PROXY = (
    f"socks5h://{quote(user)}:{quote(pw)}@{host}:{port}"
    if user else f"socks5h://{host}:{port}"
)

session = requests.Session()
session.trust_env = False  # optional: ignore env proxies (HTTP_PROXY/HTTPS_PROXY)
session.proxies.update({"http": PROXY, "https": PROXY})

retries = Retry(
    total=5,
    backoff_factor=0.5,  # 0.5, 1, 2, 4... seconds
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=frozenset(["HEAD", "GET", "OPTIONS"]),
    respect_retry_after_header=True,  # explicit for portability with urllib3 v1.26+
)

adapter = HTTPAdapter(max_retries=retries, pool_connections=50, pool_maxsize=50)
session.mount("http://", adapter)
session.mount("https://", adapter)

def fetch(url):
    r = session.get(url, timeout=(5, 30))  # (connect, read)
    r.raise_for_status()
    return r

if __name__ == "__main__":
    print(fetch("https://httpbin.org/ip").json())

Pro tip: Retry provides exponential backoff and respects Retry-After headers (default in urllib3 v2.0+, set explicitly for v1.26 compatibility).

Environment variables: session.trust_env = False prevents HTTP_PROXY/HTTPS_PROXY environment variables from interfering with your explicit proxy config.

⚠️ POST Safety: We removed POST from default retries. If you must retry POST requests, use idempotency keys and accept the risk of duplicate operations.

The socks5 vs socks5h difference (and when it bites)

socks5://...

Local DNS resolution (your machine looks up the hostname, then asks the proxy to connect to that IP).

socks5h://...

Remote DNS resolution on the proxy (prevents DNS leaks and is required for .onion and some providers).

Some proxy networks even require remote DNS (hostname-only targets), so use socks5h.

IP Rotation: On-Demand & Round-Robin Patterns

Mobile proxy providers typically offer two rotation approaches: API-triggered IP changes or built-in rotating gateways.

Option 1: API-Triggered Rotation (Coronium.io example)

Many providers give you a restart/rotation endpoint. You call it before each scraping batch to get a fresh IP:

import os
import requests
from urllib.parse import quote
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# Your modem restart token (copy from dashboard.coronium.io)
ROTATION_TOKEN = os.getenv("ROTATION_TOKEN", "19a18981f48fa3907f04c19bdcba8666")
ROTATION_URL = f"https://dashboard.coronium.io/api/v1/modems/restart-by-token/{ROTATION_TOKEN}/"

# Proxy credentials
user = os.getenv("PROXY_USER")
pw = os.getenv("PROXY_PASS", "")
host = os.getenv("PROXY_HOST", "127.0.0.1")
port = int(os.getenv("PROXY_PORT", "1080"))

PROXY = f"socks5h://{quote(user)}:{quote(pw)}@{host}:{port}" if user else f"socks5h://{host}:{port}"

session = requests.Session()
session.trust_env = False
session.proxies.update({"http": PROXY, "https": PROXY})

retries = Retry(
    total=5,
    backoff_factor=0.5,
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=frozenset(["HEAD", "GET", "OPTIONS"]),
    respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retries, pool_connections=50, pool_maxsize=50)
session.mount("http://", adapter)
session.mount("https://", adapter)

def rotate_ip():
    """Trigger modem restart to get a new IP (wait ~30-60s for reconnection)."""
    try:
        resp = requests.get(ROTATION_URL, timeout=10)
        resp.raise_for_status()
        print("✓ IP rotation triggered")
        return True
    except Exception as e:
        print(f"⚠ Rotation failed: {e}")
        return False

def fetch(url):
    r = session.get(url, timeout=(5, 30))
    r.raise_for_status()
    return r

if __name__ == "__main__":
    # Rotate before starting
    rotate_ip()
    # Wait for modem to reconnect (adjust timing based on your provider)
    import time
    time.sleep(45)

    # Now scrape with fresh IP
    print(fetch("https://httpbin.org/ip").json())

Tip: You can copy your restart link directly from dashboard.coronium.io - it includes your unique token. Call it whenever you need a new IP (e.g., every N requests or after rate limits).

Option 2: Round-Robin with Multiple Proxies

If you have multiple proxies, use itertools.cycle to rotate through them:

import itertools
from urllib.parse import quote

# List of proxies
PROXY_LIST = [
    "socks5h://user1:pass1@host1:1080",
    "socks5h://user2:pass2@host2:1080",
    "socks5h://user3:pass3@host3:1080",
]

proxy_pool = itertools.cycle(PROXY_LIST)

def fetch_with_rotation(url):
    proxy = next(proxy_pool)
    proxies = {"http": proxy, "https": proxy}
    r = requests.get(url, proxies=proxies, timeout=(5, 30))
    r.raise_for_status()
    return r

# Each call uses the next proxy in rotation
for i in range(5):
    print(f"Request {i+1}:", fetch_with_rotation("https://httpbin.org/ip").json())

⚠️ Note: Round-robin works best when your proxies are independent. For mobile proxies with API rotation, use the on-demand pattern above to control exactly when IPs change.

References & Further Reading

Need Reliable SOCKS5 Proxies?

Get premium mobile proxies with authentic carrier IPs, perfect for your Python automation needs.