Why Use Proxies in Python?
Python dominates the data extraction landscape. Libraries like Requests, httpx, and Scrapy make HTTP calls trivial, but without proxies, your scripts hit IP bans within minutes. Using proxies in Python lets you rotate IP addresses, bypass geo-restrictions, and scale your scraping operations reliably.
In this guide, you will learn how to integrate proxies into Python using the ProxyHat Python SDK and the standard requests library. Every section includes copy-paste-ready code you can run immediately.
Whether you are building a web scraping pipeline, monitoring SERP results, or collecting pricing data, this guide covers authentication, proxy rotation, geo-targeting, error handling, and production scaling.
Installation and Setup
Installing the ProxyHat SDK and Requests
Install the ProxyHat Python SDK and the requests library using pip:
pip install proxyhat requests
For async workflows, also install httpx and aiohttp:
pip install httpx aiohttp
Getting Your API Credentials
Sign up at ProxyHat and retrieve your API key from the dashboard. You will need your username and password (or API key) for proxy authentication. Full authentication details are available in the ProxyHat API documentation.
Authentication and Basic Configuration
Using the ProxyHat SDK
The SDK handles authentication, rotation, and connection management for you:
from proxyhat import ProxyHat
client = ProxyHat(
api_key="your_api_key_here"
)
# Test the connection
info = client.get_account_info()
print(f"Traffic remaining: {info['traffic_remaining']} GB")
Using Raw Proxy Credentials with Requests
If you prefer using requests directly, configure the proxy URL:
import requests
proxy_url = "http://username:password@gate.proxyhat.com:8080"
proxies = {
"http": proxy_url,
"https": proxy_url,
}
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
timeout=30
)
print(response.json())
# {"origin": "185.xxx.xxx.xxx"}
Simple GET Request with a Proxy
Here is a complete example that sends a GET request through a ProxyHat residential proxy:
from proxyhat import ProxyHat
client = ProxyHat(api_key="your_api_key_here")
# Make a proxied GET request
response = client.get("https://httpbin.org/ip")
print(f"Status: {response.status_code}")
print(f"IP: {response.json()['origin']}")
print(f"Headers: {response.headers}")
Or with the standard requests library:
import requests
proxies = {
"http": "http://user:pass@gate.proxyhat.com:8080",
"https": "http://user:pass@gate.proxyhat.com:8080",
}
response = requests.get(
"https://example.com/api/data",
proxies=proxies,
timeout=30,
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
)
print(response.status_code)
print(response.text[:500])
Choosing the Right Proxy Type
ProxyHat offers three proxy types. Choose based on your use case. For a deeper comparison, read our guide on residential vs datacenter vs mobile proxies.
| Type | Best For | Speed | Detection Risk | Cost |
|---|---|---|---|---|
| Residential | Web scraping, SERP tracking | Medium | Very low | Per GB |
| Datacenter | High-volume, speed-critical tasks | Fast | Higher | Per IP/month |
| Mobile | Social media, app testing | Medium | Lowest | Per GB |
Switching Proxy Types in Code
from proxyhat import ProxyHat
client = ProxyHat(api_key="your_api_key_here")
# Residential proxy (default)
response = client.get(
"https://example.com",
proxy_type="residential"
)
# Datacenter proxy
response = client.get(
"https://example.com",
proxy_type="datacenter"
)
# Mobile proxy
response = client.get(
"https://example.com",
proxy_type="mobile"
)
Rotating vs Sticky Sessions
Rotating proxies assign a new IP for every request, ideal for large-scale scraping where you need maximum anonymity. Sticky sessions keep the same IP for a set duration, essential for multi-step workflows like login sequences or paginated navigation.
Rotating Proxies (New IP Each Request)
from proxyhat import ProxyHat
client = ProxyHat(api_key="your_api_key_here")
urls = [
"https://httpbin.org/ip",
"https://httpbin.org/ip",
"https://httpbin.org/ip",
]
for url in urls:
response = client.get(url, session_type="rotating")
print(f"IP: {response.json()['origin']}")
# Each request uses a different IP:
# IP: 185.xxx.xxx.1
# IP: 92.xxx.xxx.47
# IP: 78.xxx.xxx.203
Sticky Sessions (Same IP for Duration)
from proxyhat import ProxyHat
client = ProxyHat(api_key="your_api_key_here")
# Create a sticky session (maintains IP for up to 30 minutes)
session = client.create_session(duration_minutes=30)
# All requests in this session use the same IP
for page in range(1, 6):
response = session.get(f"https://example.com/products?page={page}")
print(f"Page {page}: IP {response.headers.get('X-Proxy-IP')}")
# Same IP across all pages:
# Page 1: IP 185.xxx.xxx.42
# Page 2: IP 185.xxx.xxx.42
# Page 3: IP 185.xxx.xxx.42
Geo-Targeted Requests
Need data from a specific country? ProxyHat supports geo-targeting across 195+ locations. This is critical for localized SERP scraping, price monitoring, and content verification.
from proxyhat import ProxyHat
client = ProxyHat(api_key="your_api_key_here")
# Target a specific country
response = client.get(
"https://www.google.com/search?q=best+restaurants",
country="US"
)
# Target a specific city
response = client.get(
"https://www.google.com/search?q=best+restaurants",
country="US",
city="New York"
)
# Using raw proxy URL with geo-targeting
# Format: username-country-US:password@gate.proxyhat.com:8080
import requests
proxies = {
"http": "http://user-country-DE:pass@gate.proxyhat.com:8080",
"https": "http://user-country-DE:pass@gate.proxyhat.com:8080",
}
response = requests.get("https://www.google.de", proxies=proxies, timeout=30)
print(f"Accessed from Germany: {response.status_code}")
Error Handling and Retries
Network requests fail. Proxies timeout. Targets block you. Robust error handling separates production scrapers from toy scripts.
Basic Retry Logic
import time
import requests
from requests.exceptions import ProxyError, Timeout, ConnectionError
def fetch_with_retry(url, proxies, max_retries=3, timeout=30):
"""Fetch a URL with automatic retry on failure."""
for attempt in range(max_retries):
try:
response = requests.get(
url,
proxies=proxies,
timeout=timeout,
headers={"User-Agent": "Mozilla/5.0"}
)
response.raise_for_status()
return response
except (ProxyError, Timeout, ConnectionError) as e:
wait = 2 ** attempt # Exponential backoff
print(f"Attempt {attempt + 1} failed: {e}. Retrying in {wait}s...")
time.sleep(wait)
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
wait = 10 * (attempt + 1)
print(f"Rate limited. Waiting {wait}s...")
time.sleep(wait)
elif e.response.status_code >= 500:
time.sleep(2 ** attempt)
else:
raise
raise Exception(f"Failed to fetch {url} after {max_retries} attempts")
# Usage
proxies = {
"http": "http://user:pass@gate.proxyhat.com:8080",
"https": "http://user:pass@gate.proxyhat.com:8080",
}
response = fetch_with_retry("https://example.com/data", proxies)
Using the SDK's Built-in Retry
from proxyhat import ProxyHat
client = ProxyHat(
api_key="your_api_key_here",
max_retries=3,
timeout=30,
retry_on_status=[429, 500, 502, 503]
)
# The SDK handles retries automatically
response = client.get("https://example.com/data")
print(response.status_code)
Concurrent Scraping with Threading
Sequential requests are slow. For production workloads, use Python's concurrent.futures to parallelize requests through proxies.
from concurrent.futures import ThreadPoolExecutor, as_completed
from proxyhat import ProxyHat
client = ProxyHat(api_key="your_api_key_here")
urls = [
"https://example.com/product/1",
"https://example.com/product/2",
"https://example.com/product/3",
"https://example.com/product/4",
"https://example.com/product/5",
]
def scrape(url):
"""Scrape a single URL through the proxy."""
response = client.get(url, proxy_type="residential")
return {"url": url, "status": response.status_code, "length": len(response.text)}
# Run 5 concurrent requests
results = []
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(scrape, url): url for url in urls}
for future in as_completed(futures):
try:
result = future.result()
results.append(result)
print(f"OK: {result['url']} ({result['length']} bytes)")
except Exception as e:
print(f"Error: {futures[future]} - {e}")
print(f"\nCompleted: {len(results)}/{len(urls)}")
Async Scraping with asyncio and httpx
import asyncio
import httpx
async def scrape_urls(urls, proxy_url, max_concurrent=10):
"""Scrape multiple URLs concurrently using async proxies."""
semaphore = asyncio.Semaphore(max_concurrent)
async def fetch(client, url):
async with semaphore:
response = await client.get(url, timeout=30)
return {"url": url, "status": response.status_code}
async with httpx.AsyncClient(proxy=proxy_url) as client:
tasks = [fetch(client, url) for url in urls]
return await asyncio.gather(*tasks, return_exceptions=True)
# Usage
proxy_url = "http://user:pass@gate.proxyhat.com:8080"
urls = [f"https://example.com/page/{i}" for i in range(1, 51)]
results = asyncio.run(scrape_urls(urls, proxy_url))
successful = [r for r in results if not isinstance(r, Exception)]
print(f"Scraped {len(successful)}/{len(urls)} pages")
Integration with Popular Python Libraries
Using with Requests (Session)
import requests
session = requests.Session()
session.proxies = {
"http": "http://user:pass@gate.proxyhat.com:8080",
"https": "http://user:pass@gate.proxyhat.com:8080",
}
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
})
# All requests in this session use the proxy
response = session.get("https://example.com/api/products")
print(response.json())
Using with httpx
import httpx
proxy_url = "http://user:pass@gate.proxyhat.com:8080"
# Synchronous
with httpx.Client(proxy=proxy_url) as client:
response = client.get("https://httpbin.org/ip")
print(response.json())
# Asynchronous
async with httpx.AsyncClient(proxy=proxy_url) as client:
response = await client.get("https://httpbin.org/ip")
print(response.json())
Using with aiohttp
import aiohttp
import asyncio
async def fetch_with_aiohttp():
proxy_url = "http://user:pass@gate.proxyhat.com:8080"
async with aiohttp.ClientSession() as session:
async with session.get(
"https://httpbin.org/ip",
proxy=proxy_url,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
data = await response.json()
print(f"IP: {data['origin']}")
asyncio.run(fetch_with_aiohttp())
Using with Scrapy
Add ProxyHat to your Scrapy spider by configuring the settings.py:
# settings.py
DOWNLOADER_MIDDLEWARES = {
"scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": 110,
}
HTTP_PROXY = "http://user:pass@gate.proxyhat.com:8080"
# Or set per-request in your spider:
import scrapy
class ProductSpider(scrapy.Spider):
name = "products"
start_urls = ["https://example.com/products"]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url,
meta={"proxy": "http://user:pass@gate.proxyhat.com:8080"},
callback=self.parse
)
def parse(self, response):
for product in response.css(".product-card"):
yield {
"name": product.css("h2::text").get(),
"price": product.css(".price::text").get(),
}
Production Tips
Connection Pooling and Timeouts
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
# Configure retry strategy
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
)
adapter = HTTPAdapter(
max_retries=retry_strategy,
pool_connections=10,
pool_maxsize=20
)
session.mount("http://", adapter)
session.mount("https://", adapter)
session.proxies = {
"http": "http://user:pass@gate.proxyhat.com:8080",
"https": "http://user:pass@gate.proxyhat.com:8080",
}
# Robust, production-ready request
response = session.get("https://example.com/data", timeout=(5, 30))
print(response.status_code)
Logging and Monitoring
import logging
import time
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
logger = logging.getLogger("scraper")
def monitored_request(session, url):
"""Log request timing and status for monitoring."""
start = time.time()
try:
response = session.get(url, timeout=30)
elapsed = time.time() - start
logger.info(f"OK {response.status_code} {url} ({elapsed:.2f}s)")
return response
except Exception as e:
elapsed = time.time() - start
logger.error(f"FAIL {url} ({elapsed:.2f}s): {e}")
raise
Environment Variables for Credentials
Never hardcode credentials. Use environment variables:
import os
from proxyhat import ProxyHat
client = ProxyHat(
api_key=os.environ["PROXYHAT_API_KEY"]
)
# Or with raw proxy URL
proxy_url = os.environ.get(
"PROXY_URL",
"http://user:pass@gate.proxyhat.com:8080"
)
For a full list of available proxy plans and traffic options, visit our pricing page. For advanced use cases and endpoint reference, see the API documentation. You can also explore our guide on the best proxies for web scraping in 2026 for provider comparisons.
Key Takeaways
- Install in one command:
pip install proxyhat requestsgets you started immediately.- Use the SDK for simplicity: The ProxyHat Python SDK handles authentication, retries, and rotation automatically.
- Choose the right proxy type: Residential for scraping, datacenter for speed, mobile for social platforms.
- Rotate vs stick: Use rotating proxies for bulk scraping, sticky sessions for multi-step workflows.
- Geo-target when needed: Specify country and city for localized data collection.
- Handle errors properly: Implement exponential backoff and retry logic for production reliability.
- Scale with concurrency: Use
ThreadPoolExecutororasyncioto parallelize requests.- Never hardcode credentials: Store API keys in environment variables.
Frequently Asked Questions
How do I set up a proxy in Python Requests?
Pass a proxies dictionary to any requests method: requests.get(url, proxies={"http": "http://user:pass@host:port", "https": "http://user:pass@host:port"}). The ProxyHat SDK simplifies this further by handling proxy configuration internally.
What is the difference between rotating and sticky proxies in Python?
Rotating proxies assign a new IP address for every request, which is ideal for large-scale scraping. Sticky proxies maintain the same IP for a set duration (e.g., 10-30 minutes), which is necessary for login sessions, shopping carts, or paginated browsing where IP consistency matters.
Can I use ProxyHat proxies with asyncio and aiohttp?
Yes. ProxyHat proxies work with any HTTP client that supports proxy configuration, including aiohttp, httpx (async mode), and asyncio-based frameworks. Pass the proxy URL as the proxy parameter in your async client.
How do I handle proxy errors and timeouts in Python?
Wrap your requests in try/except blocks catching ProxyError, Timeout, and ConnectionError. Implement exponential backoff (doubling wait time between retries) and set a maximum retry count. The ProxyHat SDK includes built-in retry logic with configurable parameters.
Which Python library is best for web scraping with proxies?
For simple tasks, requests with the ProxyHat SDK is the easiest option. For high-concurrency async scraping, use httpx or aiohttp. For complex crawling with link following and data extraction, Scrapy with proxy middleware is the most powerful choice. All work seamlessly with ProxyHat proxies.






