IP Rotation Strategies: When to Use Rotating vs Sticky Proxies
Proxy management is the backbone of reliable web scraping. But not all proxy strategies are equal — choosing between rotating and sticky proxies can make or break your scraper. This guide breaks down when to use each approach and how to implement them in Python.
Rotating Proxies: A New IP Every Request
Rotating proxies assign a different IP address to each request. This is ideal when you need to:
- Scrape search results or catalog pages
- Make many independent requests
- Avoid rate limiting on high-volume targets
- Scrape sites that track request patterns per IP
import requests
from itertools import cycle
# Simple proxy rotation
proxy_list = [
"http://user:pass@proxy1.example.com:8080",
"http://user:pass@proxy2.example.com:8080",
"http://user:pass@proxy3.example.com:8080",
]
proxy_pool = cycle(proxy_list)
def scrape_with_rotation(urls: list[str]) -> list[dict]:
results = []
for url in urls:
proxy = next(proxy_pool)
try:
response = requests.get(
url,
proxies={"http": proxy, "https": proxy},
timeout=15
)
results.append({"url": url, "status": response.status_code})
except requests.RequestException as e:
print(f"Failed {url} via {proxy}: {e}")
return results
Sticky Proxies: Same IP for a Session
Sticky (or session) proxies maintain the same IP address for a defined period — typically 1 to 30 minutes. Use them when:
- Logging into websites
- Navigating multi-page flows (checkout, pagination)
- Maintaining session cookies
- Scraping sites that require consistent identity
import requests
class StickySession:
def __init__(self, proxy_endpoint: str, session_id: str):
self.session = requests.Session()
# Most providers use session ID in the username for sticky sessions
self.session.proxies = {
"http": f"http://user-session-{session_id}:pass@{proxy_endpoint}",
"https": f"http://user-session-{session_id}:pass@{proxy_endpoint}",
}
def login_and_scrape(self, login_url: str, target_url: str, credentials: dict):
# Login maintains cookies on the same IP
self.session.post(login_url, data=credentials)
# Subsequent requests use same IP and cookies
response = self.session.get(target_url)
return response.text
# Each StickySession gets a unique, consistent IP
session1 = StickySession("gate.proxy-provider.com:7777", "abc123")
session2 = StickySession("gate.proxy-provider.com:7777", "def456")
Hybrid Strategy: The Best of Both
In practice, most scraping projects need both strategies. Use rotating proxies for discovery and sticky proxies for deep scraping:
import requests
import uuid
class ProxyManager:
def __init__(self, gateway: str, username: str, password: str):
self.gateway = gateway
self.username = username
self.password = password
def rotating_request(self, url: str) -> requests.Response:
"""Each call gets a new IP"""
proxy = f"http://{self.username}:pass@{self.gateway}"
return requests.get(url, proxies={"https": proxy}, timeout=15)
def sticky_session(self, duration_minutes: int = 10) -> requests.Session:
"""Returns a session that keeps the same IP"""
session_id = uuid.uuid4().hex[:8]
session = requests.Session()
proxy = f"http://{self.username}-session-{session_id}-time-{duration_minutes}:{self.password}@{self.gateway}"
session.proxies = {"http": proxy, "https": proxy}
return session
# Usage: rotate for catalog, stick for details
manager = ProxyManager("gate.example.com:7777", "user", "pass")
# Phase 1: Discover product URLs (rotating)
product_urls = []
for page in range(1, 50):
resp = manager.rotating_request(f"https://shop.example.com/catalog?page={page}")
# parse product URLs...
# Phase 2: Scrape product details (sticky per product)
for url in product_urls:
session = manager.sticky_session(duration_minutes=5)
detail_page = session.get(url)
reviews_page = session.get(url + "/reviews")
# Same IP sees both requests — looks like a real user
Choosing a Proxy Provider
The right provider depends on your use case:
For automatic rotation with built-in rendering, ScraperAPI is excellent. It handles proxy rotation, CAPTCHA solving, and JavaScript rendering in a single API call — you don't manage proxies at all.
For residential proxies with flexible session control, ThorData offers both rotating and sticky residential IPs with granular session duration settings.
For comparing multiple providers, ScrapeOps aggregates proxy services so you can benchmark speed, success rates, and costs across providers through one interface.
Proxy Health Monitoring
Don't fly blind — track your proxy performance:
import time
from collections import defaultdict
class ProxyMonitor:
def __init__(self):
self.stats = defaultdict(lambda: {"success": 0, "fail": 0, "total_time": 0})
def record(self, proxy: str, success: bool, response_time: float):
self.stats[proxy]["success" if success else "fail"] += 1
self.stats[proxy]["total_time"] += response_time
def get_success_rate(self, proxy: str) -> float:
s = self.stats[proxy]
total = s["success"] + s["fail"]
return s["success"] / total if total > 0 else 0
def get_avg_response_time(self, proxy: str) -> float:
s = self.stats[proxy]
total = s["success"] + s["fail"]
return s["total_time"] / total if total > 0 else 0
def report(self):
for proxy, s in self.stats.items():
rate = self.get_success_rate(proxy)
avg_time = self.get_avg_response_time(proxy)
print(f"{proxy}: {rate:.0%} success, {avg_time:.2f}s avg")
Key Decision Matrix
| Scenario | Proxy Type | Why |
|---|---|---|
| Search results | Rotating | Independent requests, high volume |
| Login-required sites | Sticky | Need consistent session |
| E-commerce catalog | Rotating | Many pages, no login |
| Multi-step checkout | Sticky | Must maintain flow |
| Social media feeds | Rotating | Pattern detection is per-IP |
| API scraping | Rotating | Usually stateless |
Key Takeaways
- Rotating proxies for stateless, high-volume scraping
- Sticky proxies for session-based, multi-step flows
- Hybrid approach is usually best — rotate for discovery, stick for depth
- Monitor proxy health to catch degradation early
- Match proxy type to residential vs datacenter based on target site sophistication
Top comments (0)