Scrapy Rotating Proxy: Middleware, Retries, Status Codes
A working proxy setup for Scrapy, the retry rules that matter, and how to stay polite to the sites you crawl.
One endpoint, two ways to set it
The rotating proxy is one HOST and PORT with a username and password; the exit IP changes on the server side, so you do not manage an IP list. In Scrapy the proxy is read from the request meta key proxy, and credentials can sit in the URL as http://username:password@some_proxy_server:port. The meta value takes precedence over the http_proxy and https_proxy environment variables.
Set it on each Request when only some requests need the proxy, or in a small downloader middleware when all of them do. The existing guide to Scrapy proxy setup covers environment variables and special characters in passwords; this page is about rotation and retries.
# middlewares.py
class ProxyMiddleware:
def process_request(self, request, spider):
# Only set it when the request does not carry its own proxy.
request.meta.setdefault("proxy", "http://LOGIN:PASSWORD@HOST:PORT")
# settings.py
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
Example only. Priority 350 runs before the built-in HttpProxyMiddleware, which reads the meta value.
A rotating endpoint gives a new IP for each new connection. Scrapy may keep a connection open and send several requests over it, so several requests can share one IP. Do not assume one IP per request: log the exit IP as in the spider below, then decide.
When a task must stay on one address, for example a login followed by a few pages, generate the proxy in the sticky mode ("Choose region / sticky IP") and keep those requests together. For independent pages, use the "Choose region / new IP per connection" mode.
import scrapy
PROXY = "http://LOGIN:PASSWORD@HOST:PORT"
class IpCheckSpider(scrapy.Spider):
name = "ip_check"
def start_requests(self):
for i in range(5):
yield scrapy.Request(
"https://ipinfo.io/ip?n=%d" % i,
meta={"proxy": PROXY, "max_retry_times": 3},
dont_filter=True,
)
def parse(self, response):
self.logger.info("exit IP: %s", response.text.strip())
Example only. Fill in the proxy from My assets, run it with scrapy crawl ip_check and read the logged IPs. start_requests is still the classic entry point; newer Scrapy versions also offer an async start().
The site asks you to slow down: lower the rate and respect Retry-After
403, 404
No
A repeat gives the same answer; check the URL and the site rules
407
No
Proxy credentials rejected: fix LOGIN and PASSWORD, do not retry
Source: Scrapy downloader middleware documentation (default RETRY_HTTP_CODES), read 12 November 2026; the "what to do" column is a recommendation. Defaults can differ between Scrapy versions.
# settings.py
ROBOTSTXT_OBEY = True
DOWNLOAD_TIMEOUT = 30
# retries (the documented defaults)
RETRY_ENABLED = True
RETRY_TIMES = 2 # extra tries after the first download
RETRY_HTTP_CODES = [408, 429, 500, 502, 503, 504, 522, 524]
# be gentle with the target site
DOWNLOAD_DELAY = 1.0
CONCURRENT_REQUESTS_PER_DOMAIN = 4
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 30.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0
Example only. RETRY_TIMES counts extra tries after the first download. Per request you can set meta["max_retry_times"], or meta["dont_retry"] = True to skip retries.
AutoThrottle is off by default. When enabled it sets the delay from how fast the site answers, never goes below DOWNLOAD_DELAY, and responses other than 200 cannot shorten it, so errors slow the crawl down instead of speeding it up. START_DELAY is 5.0, MAX_DELAY 60.0 and TARGET_CONCURRENCY 1.0 by default.
Keep ROBOTSTXT_OBEY enabled, set DOWNLOAD_DELAY and CONCURRENT_REQUESTS_PER_DOMAIN explicitly instead of relying on defaults, and read the site terms. A proxy changes where requests come from; it does not change how many requests the site can take.
Run the ip_check spider and read the five logged IPs.
Expected result Five IPs, with the country you chose.
Step 4: Add the retry and delay settings
Paste the settings, run a small crawl and look at the retry and 429 counts in the Scrapy stats at the end.
Troubleshooting
Every response is 407 or the proxy never answers
Check Is the meta value a full URL with scheme, and are LOGIN and PASSWORD copied exactly?
Fix Fix the URL, percent-encode special characters in the password, and test with curl -x first.
Stop when Do not raise RETRY_TIMES: a wrong password fails every time.
Many 429 answers
Check Look at CONCURRENT_REQUESTS_PER_DOMAIN, DOWNLOAD_DELAY and the Retry-After header.
Fix Lower the concurrency, raise the delay, turn on AutoThrottle.
Stop when If 429 continues at a low rate, the site may not allow this crawl; stop.
Logged-in pages lose the session
Check Did the exit IP change between the login and the next page?
Fix Use the sticky mode for the flow and keep its requests together.
FAQ
How do I use a rotating proxy in Scrapy?
Set request.meta["proxy"] to http://LOGIN:PASSWORD@HOST:PORT on each Request, or set it in a small downloader middleware.
Which status codes does Scrapy retry by default?
408, 429, 500, 502, 503, 504, 522 and 524, up to RETRY_TIMES extra attempts (2 by default). Check the documentation of your Scrapy version.
Should I retry a 403 or 407?
No. A 407 means the proxy rejected the credentials; fix them. A 403 usually repeats, so check the URL and the site rules instead of retrying.
Does each request get a new IP?
Not necessarily. A new IP comes with each new connection, and Scrapy can send several requests over one connection. Log the exit IP to see what happens in your setup.
What does Proxy302 rotating residential cost?
The Budget line is US$1.50 per GB, with lower per-GB prices in larger packages, and purchased traffic has no expiry. There is no free trial.
Product facts file of the site, checked 12 November 2026: rotating residential, new IP on each new connection, sticky mode available, HTTP port 2222 and SOCKS5 port 3333 on proxy.proxy302.com, Budget line US$1.50 per GB, no free trial.
Read 12 November 2026: HttpProxyMiddleware sets the proxy from the request meta key proxy, accepts http://username:password@some_proxy_server:port, and the meta value takes precedence over the environment variables; RetryMiddleware defaults RETRY_ENABLED True, RETRY_TIMES 2, RETRY_HTTP_CODES [408, 429, 500, 502, 503, 504, 522, 524]; meta keys max_retry_times and dont_retry; RobotsTxtMiddleware follows ROBOTSTXT_OBEY.
Read 12 November 2026: disabled by default; AUTOTHROTTLE_START_DELAY 5.0, AUTOTHROTTLE_MAX_DELAY 60.0, AUTOTHROTTLE_TARGET_CONCURRENCY 1.0; the delay is derived from latency, never goes below DOWNLOAD_DELAY, and non-200 responses cannot shorten it.
Defaults of the development version of the docs; check your installed Scrapy version.
Read 12 November 2026: DOWNLOAD_DELAY is the minimum seconds between two consecutive requests to the same domain; CONCURRENT_REQUESTS_PER_DOMAIN limits simultaneous requests to one domain; ROBOTSTXT_OBEY makes Scrapy respect robots.txt; DOWNLOAD_TIMEOUT is how long the downloader waits before timing out.