IPRoyal Rotating Residential IP Proxy in Python

IPRoyal Python Rotating IP

IPRoyal’s residential IP pool has 20M+ IPs—the trick is knowing how to use them. Sticky mode (30 min per IP) suits account login scenarios, while rotating mode (new IP per request) excels at web scraping, bulk registration, and anti-detection. This guide implements a complete rotating + retry solution in Python.

Two modes explained

ModeIP Switch FrequencyUse Case
StickyRotate every 10/30 minutesAccount login, API calls, long-running quant sessions
RotatingNew IP on every requestWeb scraping, bulk registration, anti-detection

IPRoyal’s dashboard lets you generate credentials for both modes.

Rotating proxy credentials format

Log in to IPRoyal Dashboard → Residential → Generate → Select Rotating → You’ll get:

Proxy: gw.iproyal.com
Port: 12321
Username: YOUR_USER
Password: YOUR_PASS_country-us_session-rotate

session-rotate is key: every request pulls a fresh IP from the pool.

Python basics

import requests

proxy_url = "http://USER:[email protected]:12321"
proxies = {"http": proxy_url, "https": proxy_url}

for i in range(5):
    r = requests.get("https://ipinfo.io/json", proxies=proxies, timeout=10)
    print(f"Request {i+1}: {r.json()['ip']} ({r.json().get('city')})")

Expected output:

Request 1: 142.176.xx.xx (San Diego)
Request 2: 71.192.xx.xx (New York)
Request 3: 98.44.xx.xx (Austin)
Request 4: 24.128.xx.xx (Boston)
Request 5: 104.128.xx.xx (Chicago)

Every request uses a different residential IP—like real users from different locations hitting your target.

Real-world 1: Anti-scraping for crawlers

Large sites (Amazon pricing, Google search results) often block scrapers. Rotating IPs makes the target think you’re “many different users” rather than a bot.

import requests
import time
from urllib.parse import quote

proxy = "http://USER:[email protected]:12321"
PROXIES = {"http": proxy, "https": proxy}

def scrape_url(url, max_retries=3):
    """Retries automatically switch to a fresh IP"""
    for attempt in range(max_retries):
        try:
            # session-rotate ensures each retry is a new IP
            r = requests.get(url, proxies=PROXIES, timeout=15,
                             headers={"User-Agent": "Mozilla/5.0 ..."})
            if r.status_code == 200:
                return r.text
            elif r.status_code in (403, 429, 503):
                print(f"  Blocked ({r.status_code}), rotating IP [attempt {attempt+1}]")
                time.sleep(2)
                continue
            else:
                print(f"  HTTP {r.status_code}")
                return None
        except requests.Timeout:
            print(f"  Timeout, retrying [attempt {attempt+1}]")
            time.sleep(3)
        except requests.RequestException as e:
            print(f"  error: {e}")
            time.sleep(2)
    return None

# Example: scrape 100 URLs
urls = [f"https://example.com/product/{i}" for i in range(100)]
for u in urls:
    html = scrape_url(u)
    if html: process(html)  # Your parsing logic
    time.sleep(0.5)  # Be gentle

Key insight: session-rotate gives each requests.get() call a new IP, so failed requests automatically retry with a different IP—no manual switching needed.

Real-world 2: Bypass geographic restrictions

Some sites only serve specific regions. IPRoyal supports country targeting:

# US IP
us_proxy = "http://USER:[email protected]:12321"

# Japan IP
jp_proxy = "http://USER:[email protected]:12321"

# UK IP
uk_proxy = "http://USER:[email protected]:12321"

def get_by_country(url, country="us"):
    proxy_url = f"http://USER:PASS_country-{country}[email protected]:12321"
    return requests.get(url, proxies={"http": proxy_url, "https": proxy_url}).text

# Scrape Netflix US vs. Japan content
html_us = get_by_country("https://netflix.com/browse", "us")
html_jp = get_by_country("https://netflix.com/browse", "jp")

Real-world 3: Bulk account registration

(Legal note: Ensure the target site’s ToS permits this. Common legitimate scenarios: registering multiple test accounts for your own service, or compliance testing)

import random

def register_account(email, password, country="us"):
    proxy_url = f"http://USER:PASS_country-{country}[email protected]:12321"
    PROXIES = {"http": proxy_url, "https": proxy_url}
    
    # Simulate human behavior: random delays
    time.sleep(random.uniform(2, 5))
    
    # Visit signup page first (get CSRF token)
    session = requests.Session()
    session.proxies = PROXIES
    session.headers["User-Agent"] = random_user_agent()
    
    resp = session.get("https://target.com/register")
    csrf = extract_csrf(resp.text)
    
    time.sleep(random.uniform(3, 8))  # Simulate "form filling" time
    
    resp = session.post("https://target.com/register", data={
        "email": email,
        "password": password,
        "_csrf": csrf,
    })
    return resp.status_code == 200

# Bulk registration
emails = load_emails()
for email in emails:
    success = register_account(email, "StrongPass123!", random.choice(["us", "gb", "ca"]))
    print(f"{email}: {'✓' if success else '✗'}")
    time.sleep(random.uniform(30, 120))  # Space out accounts

Real-world 4: Distribute quant strategy requests

Binance enforces per-IP rate limits. If your quant strategy hammers the API, rotating IPs spreads the load:

import random

PROXY_POOL = [
    f"http://USER:[email protected]:12321",
    f"http://USER:[email protected]:12321",
    f"http://USER:[email protected]:12321",
]

def binance_request(path):
    """Distribute requests across regional residential IPs"""
    proxy = random.choice(PROXY_POOL)
    return requests.get(f"https://api.binance.com{path}",
                        proxies={"https": proxy}, timeout=5)

# Run concurrently; each request lands on a different region's fresh IP

Better retry pattern with tenacity

Manual retry logic is error-prone. Use the tenacity library:

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=2, max=30))
def scrape_with_retry(url):
    r = requests.get(url, proxies=PROXIES, timeout=15)
    r.raise_for_status()  # 4xx/5xx triggers retry
    return r.text

Automatic exponential backoff (2s, 4s, 8s, 16s, 30s), and session-rotate guarantees a fresh IP on each attempt.

Cost comparison

SolutionMonthly (10GB traffic)ReliabilityIP Quality
IPRoyal Residential Rotating$70⭐⭐⭐⭐⭐Residential broadband
Bright Data$300⭐⭐⭐⭐⭐Residential broadband (pricier)
Free proxy lists$0⭐95% dead
DIY VPS$20-50⭐⭐⭐Datacenter IP, easily blocked

IPRoyal offers the best bang for buck, but watch bandwidth costs (charged per GB). Heavy image/video scraping burns through traffic fast—budget accordingly.

Takeaway

IPRoyal Rotating mode solves:

  • Scraper blocking & detection
  • Geographic restrictions
  • Bulk operations
  • API rate limit distribution

Python + session-rotate suffix = automatic IP rotation. Use tenacity for cleaner retry logic.

Ready to start?
→ IPRoyal offers pay-as-you-go pricing; prepay $5 to get going (20M+ residential IP pool)

Important notes:

  • Respect target site ToS
  • Add reasonable delays to crawlers—don’t DDoS their infrastructure
  • Privacy: IPRoyal sees your request URLs but not the content; HTTPS is encrypted

Further reading: