Your scraper runs fine for the first few hundred requests, then the responses turn into a wall of HTTP 429 (Too Many Requests) and occasional 503 (Service Unavailable). The data stops flowing, retries make it worse, and eventually the target site stops answering your IP entirely. This is not a bug in your code. It is rate limiting doing exactly what it was designed to do.
Why one IP hits the wall
Most sites track requests per IP address over a sliding time window. When a single address sends too many requests too quickly, the server pushes back. The usual escalation looks like this:
- First, you get HTTP 429 with a
Retry-Afterheader asking you to slow down. - Ignore that, and the site starts returning 503 or serving CAPTCHAs.
- Keep hammering, and the IP lands on a temporary or permanent block list.
The core problem is concentration. Every request you send carries the same source IP, so from the server's point of view all that traffic comes from one client that is clearly behaving like a bot. A human visitor loads a handful of pages per minute. A scraper on one IP can fire thousands, and the pattern is trivial to spot.
Slowing down helps, but it also kills throughput. If you need to collect data from a hundred thousand pages and one IP can safely handle a few requests per second, a single address turns a one-hour job into a multi-day crawl. You need a way to spread the load without tripping the per-IP threshold on any single address.
How rotating proxies spread the load
A rotating proxy pool gives you many source IPs instead of one. Each request (or each batch, or each session) goes out through a different address, so the request count on any individual IP stays comfortably under the site's limit. The server sees traffic distributed across many clients that each look ordinary, and no single address crosses the line that triggers a 429.
The math is simple. If a site tolerates 60 requests per minute per IP and you rotate across 50 addresses, your effective ceiling climbs to roughly 3,000 requests per minute while every individual IP stays polite. Add automatic rotation and each request picks a fresh exit without you managing state by hand.
Here is the pattern in practice, cycling through a pool per request:
import requests
from itertools import cycle
proxies = [
"http://user:pass@ip1:port",
"http://user:pass@ip2:port",
"http://user:pass@ip3:port",
]
pool = cycle(proxies)
def fetch(url):
proxy = next(pool)
resp = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=15)
if resp.status_code == 429:
proxy = next(pool) # rotate again and retry
resp = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=15)
return resp
for page in range(1, 5000):
print(fetch(f"https://example.com/items?page={page}").status_code)
Combine rotation with respect for Retry-After and a small random delay per address, and the 429 responses drop off sharply.
Where to get a rotating pool
For this to work, you need real IPs that rotate cleanly and enough of them to keep each address under the threshold. WinGate.me rotating proxies provide private IPv4 and SOCKS5 with automatic rotation drawn from a worldmix pool, so requests spread across many exits without extra plumbing on your side.
The service supports HTTP, HTTPS, and SOCKS5, handles up to 5,000 threads for large parallel jobs, and comes with unlimited traffic, which matters when scraping volume is unpredictable. Rotation can run manually, on a schedule, or through the API, so you control whether the exit changes per request or per session.
If you want to confirm it clears your 429 problem before committing, there is a free 2-hour test. Point your scraper at the pool, watch the status codes, and check the rotating proxy setup against your own targets. When each IP stays under the limit, the 429s stop and your throughput goes back up.
Top comments (0)