Back

How to Use Rotating Residential Proxies With Scrapy in 2026

How to Use Rotating Residential Proxies With Scrapy in 2026

Scrapy is a fast, extensible Python scraping framework and one of the easiest tools to get blocked with. Its default request fingerprint is recognizable, and a single IP sending hundreds of requests will hit rate limits or be banned fast.

Rotating residential proxies fix this by cycling your requests through IPs tied to real home internet connections. To any target site, each request looks like a different person browsing from their couch.

This guide walks Python developers and data engineers through two integration methods, blocking evasion, the errors you'll actually hit, and the legal basics.

Key Takeaways

  • Scrapy's default fingerprint – user agent, TLS stack, header order – is easily detected by anti-bot systems.
  • Residential proxies appear as real ISP traffic and are far harder to flag than datacenter IPs on protected targets.
  • Two integration methods: a single gateway endpoint (simpler) or list-based rotation via middleware (more control).
  • IP rotation alone isn't enough. Rotate user agents, send realistic headers, and tune your delays too.
  • Scraping publicly accessible data is generally legal, but check robots.txt and terms of service first.

Tools you need before using rotating residential proxies with Scrapy

Scrapy is the foundation: a Python framework that handles request scheduling, response parsing, and data pipelines. Install it with pip install Scrapy.

A residential proxy provider gives you either a single rotating gateway endpoint or a list of individual proxy addresses with credentials. Residential IPs are assigned to real households by ISPs, which makes them much harder to detect than datacenter IPs on well-protected targets. Datacenter proxies are cheaper but get flagged more often on sites with active bot detection.

The scrapy-rotating-proxies library (pip install scrapy-rotating-proxies) is useful if your provider supplies individual addresses. It handles health monitoring and removes dead proxies automatically. If you're using a gateway endpoint, you don't need it.

For user agent rotation, pip install scrapy-user-agents or fake-useragent. 

For JavaScript-rendered targets, pip install scrapy-playwright, then playwright install chromium. Note that the install alone doesn't do anything: scrapy-playwright needs DOWNLOAD_HANDLERS entries and an asyncio reactor set in settings.py, so follow the project's setup docs before your first run. Use it only when a plain HTTP request doesn't return the content you need.

How to use rotating residential proxies with Scrapy (step-by-step)

Step 1: Install Scrapy and create a project

bash

pip install scrapy

scrapy startproject myproject

cd myproject

This scaffolds the standard structure: settings.py, middlewares.py, pipelines.py, and a spiders directory. All proxy configuration goes into settings.py and middlewares.py.

Step 2: Get your residential proxy credentials

Sign up at GoProxies and open the rotating residential proxies section of the dashboard. You'll get four things: a gateway hostname, a port, a username, and a password. The dashboard is also where you set your geo targeting and generate the session parameters you'll need later if any of your flows require a sticky IP.

Keep all four out of source control. The next step loads them from environment variables.

Step 3: Method A – single gateway endpoint (recommended)

With a gateway provider, all IP rotation happens on the provider's side. Add a custom middleware to middlewares.py:

python

import os

class ResidentialProxyMiddleware:

    def process_request(self, request, spider):

        proxy_user = os.environ.get("PROXY_USER")

        proxy_pass = os.environ.get("PROXY_PASS")

        proxy_host = os.environ.get("PROXY_HOST")

        proxy_port = os.environ.get("PROXY_PORT")

        proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"

        request.meta["proxy"] = proxy_url

Then register it in settings.py. You'll add the user agent middleware to this same dict in Step 5, so treat the block in Step 5 as the final version:

python

DOWNLOADER_MIDDLEWARES = {

    "myproject.middlewares.ResidentialProxyMiddleware": 350,

}

Step 4: Method B – list-based rotation with scrapy-rotating-proxies

If your provider gives you individual proxy addresses, add the list and enable both middlewares in settings.py:

python

ROTATING_PROXY_LIST = [

    "http://user:pass@proxy1.example.com:8000",

    "http://user:pass@proxy2.example.com:8000",

    "http://user:pass@proxy3.example.com:8000",

]

DOWNLOADER_MIDDLEWARES = {

    "rotating_proxies.middlewares.RotatingProxyMiddleware": 610,

    "rotating_proxies.middlewares.BanDetectionMiddleware": 620,

}

BanDetectionMiddleware marks a proxy dead on any non-200 response or empty body. That default is aggressive, and it's worth understanding before you run it: a target that returns a 302 redirect or a non-200 on a perfectly legitimate response will chew through your pool in minutes. This is the direct cause of the "spider closes because all proxies are dead" failure covered further down, so extend the policy to match your target's actual behavior rather than accepting the default.

You can also extend it in the other direction, to catch site-specific signals like a 200 response containing a CAPTCHA body.

To load proxies from a file instead of the settings dict:

python

ROTATING_PROXY_LIST_PATH = "/path/to/proxies.txt"

Step 5: Rotate user agents alongside proxies

Rotating IPs while keeping a fixed user agent is a common oversight – the pattern stays detectable. This is the complete DOWNLOADER_MIDDLEWARES block, replacing the one from Step 3:

python

DOWNLOADER_MIDDLEWARES = {

    "myproject.middlewares.ResidentialProxyMiddleware": 350,

    "scrapy.downloadermiddlewares.useragent.UserAgentMiddleware": None,

    "scrapy_user_agents.middlewares.RandomUserAgentMiddleware": 400,

}

The built-in UserAgentMiddleware has to be disabled so the random one can take over. If you leave both enabled, you'll get inconsistent results depending on priority order.

Step 6: Test your setup

Create a quick test spider to confirm the proxy is working:

python

import scrapy

class ProxyTestSpider(scrapy.Spider):

    name = "proxy_test"

    start_urls = ["https://httpbin.org/ip"]

    def parse(self, response):

        self.logger.info(f"Current IP: {response.json()['origin']}")

Run scrapy crawl proxy_test. The IP in the log should match your proxy pool, not your own machine. Run it several times and confirm the IP changes.

How to avoid being blocked when scraping with Scrapy

Rotation covers one signal out of many. Here's what else to cover.

Use high-quality rotating proxies

IPs with poor reputation or heavy scraping history get flagged fast regardless of your other settings. Residential proxies sourced from real ISP connections read as organic traffic and hold up far better on protected targets.

GoProxies' rotating residential proxies give you a 30 million IP pool with city-level targeting, which is the depth you need to scrape at scale without burning through reputation.

Handle sessions and cookies correctly

Scrapy's CookiesMiddleware is enabled by default and manages cookie state within a session. For multi-step workflows that need the same IP across a sequence of requests, use your provider's sticky session support. Rotating IPs mid-session can trigger session anomaly detection on stricter targets.

Use headless browser scraping for JavaScript targets

Scrapy makes plain HTTP requests and doesn't execute JavaScript. For targets with dynamic content or JS-based bot challenges, scrapy-playwright integrates a full browser engine into your Scrapy pipeline. It adds significant overhead, so reach for it only when a static request doesn't return what you need.

Avoid detection with headers, delays, and user agents

Replace Scrapy's default user agent string and send a full browser header set including Accept-Language, Accept-Encoding, and Referer. Match Accept-Language to the geographic region of your proxy IP, since a mismatch is a detectable inconsistency.

Use AutoThrottle to vary request timing dynamically based on server latency:

python

AUTOTHROTTLE_ENABLED = True

AUTOTHROTTLE_START_DELAY = 1

AUTOTHROTTLE_MAX_DELAY = 30

AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0

Avoid fixed DOWNLOAD_DELAY values. Even spacing is itself a bot signal.

Configure retries and error handling

Scrapy's RetryMiddleware is enabled by default. Configure it explicitly for scraping:

python

RETRY_ENABLED = True

RETRY_TIMES = 5

RETRY_HTTP_CODES = [403, 408, 429, 500, 502, 503, 504]

With rotation active, each retry goes out from a different IP, so a blocked IP doesn't permanently stall a request. Note that 403 isn't in Scrapy's default retry list – adding it only helps if your rotation is genuinely working, otherwise you're retrying the same block five times.

Limit parallelization sensibly

High concurrency amplifies every detection signal. Reduce it when scraping protected targets:

python

CONCURRENT_REQUESTS = 8

CONCURRENT_REQUESTS_PER_DOMAIN = 2

Main challenges when scraping with Scrapy

Scrapy's default fingerprint – user agent string, Twisted TLS stack, and header ordering – is well documented by anti-bot vendors. Here are the detection vectors that matter and how to deal with them.

Scrapy's default TLS fingerprint

Scrapy's TLS fingerprint (cipher suites, extension order) differs from real browser traffic. Sites with TLS-based detection will catch standard Scrapy requests even behind a realistic user agent. Use scrapy-impersonate or scrapy-playwright to send a genuine browser TLS stack – both require their own DOWNLOAD_HANDLERS configuration, so check each project's docs.

Header consistency and CAPTCHA challenges

This is where Scrapy differs from browser-based tools. A browser sends a specific set of headers in a specific order, and Scrapy doesn't reproduce either by default. A Chrome user agent arriving without Sec-Ch-Ua or Sec-Fetch-Dest fails consistency checks on sophisticated targets, and header order is checked too.

So when a CAPTCHA appears at the start of a session, work through the causes in this order: incomplete or wrongly ordered headers, then an Accept-Language value inconsistent with the IP's region, then the IP's own fraud score. Set DEFAULT_REQUEST_HEADERS in settings.py to a complete, browser-accurate set before you assume the proxy is the problem.

IP reputation and blacklisting

Residential IPs used heavily for scraping accumulate history that anti-bot systems track. A large, actively refreshed pool reduces the chance of cycling into a flagged IP. If clean-looking IPs are still getting blocked, check your provider's fraud score guarantees.

Common errors when scraping with Scrapy and how to fix them

  1. 403 Forbidden: The site detected your request as automated. Work through the causes in order: default user agent, incomplete or misordered headers, poor-reputation IP, TLS fingerprint. If you're already on residential proxies and still seeing 403s, the problem is at the fingerprinting level, not the IP level.
  2. 429 Too Many Requests: You've exceeded the site's rate limit. Enable AutoThrottle, reduce CONCURRENT_REQUESTS_PER_DOMAIN, and check that rotation is actually distributing requests across different IPs. The Retry-After header tells you how long to back off.
  3. Proxy authentication failing. Check the URL format: http://username:password@host:port. URL-encode any special characters in the password. If you're using scrapy-rotating-proxies with authenticated proxies, note that credentials get stripped from request.meta['proxy'] during response handling, which makes health tracking fail silently.
  4. Spider closes because all proxies are dead. This happens with list-based rotation when ROTATING_PROXY_CLOSE_SPIDER is enabled and every proxy has been marked dead. Usually the ban detection policy is too aggressive for your target, as flagged in Step 4. Override BanDetectionPolicy to define what counts as a real ban versus a site-specific non-200, and raise ROTATING_PROXY_PAGE_RETRY_TIMES before a proxy gets retired.

What you can collect using Scrapy with rotating proxies

Scrapy paired with rotating residential proxies suits data-heavy collection at scale. The common use cases:

  • Product pricing and availability from e-commerce sites, marketplaces, and retail aggregators: prices, stock status, variants, and promotional pricing. Residential proxies matter here because retail platforms run aggressive anti-bot systems.
  • Search engine results pages: ranking data, featured snippets, ad placements, and organic URLs. Results are geo-sensitive, which is where location-targeted residential proxies earn their cost.
  • Job boards and recruitment listings: titles, locations, salary ranges, required skills, and company data for competitive intelligence and market research.
  • News and content feeds. Scrapy's link-following makes it efficient for crawling large volumes of articles, pulling text, publication metadata, and topic tags across many sources in parallel.
  • Public profiles and social signals: follower counts, posting frequency, engagement metrics, and biodata from platforms that surface this publicly.

Ready to collect at scale without fighting blocks? GoProxies offers a 30 million IP residential pool with city-level targeting and 99.99% uptime.

Is it legal to use Scrapy with rotating proxies?

Legality depends on what you scrape and how, not on the tools. A few principles:

Public data that's accessible without logging in or circumventing access controls is generally fair game. Courts in several jurisdictions have found that scraping publicly available data doesn't constitute unauthorized access, though the specifics vary by country.

Terms of Service often prohibit automated access. Violating a ToS is typically a civil matter rather than a criminal one, but it can result in access termination or liability. Check before you scrape.

Scrapy respects robots.txt when ROBOTSTXT_OBEY is True, which is the value set in a freshly generated project. Ignoring it isn't illegal in most jurisdictions, but it raises your ToS risk and is poor practice. 

Request rates matter ethically. Keep them reasonable, use AutoThrottle, and don't send volume that degrades anyone's service. If your use case is commercial and the target is high-profile, get qualified legal advice before running at scale.

Using Scrapy's built-in HTTP cache instead of scraping

Scrapy's HttpCacheMiddleware stores responses locally and serves them from disk on repeat requests. It isn't a replacement for live scraping, but it's genuinely useful during development – it stops you hitting a live target every time you adjust your spider.

Enable it while developing:

python

HTTPCACHE_ENABLED = True

HTTPCACHE_EXPIRATION_SECS = 0

HTTPCACHE_DIR = "httpcache"

Set HTTPCACHE_ENABLED = False in production. Cached responses don't reflect current page state, and they do nothing about IP bans or anti-bot detection on live runs.

Can you use rotating proxies in Scrapy for free?

Free proxy lists exist, but they rarely work well enough to be worth the effort. The core problem is IP reputation: free proxies are shared with no controls on use, so by the time you pull an IP from a public list it's almost certainly been flagged for scraping or spam already. Most well-protected sites have those ranges blocklisted before your first request lands.

They're also unreliable. Free proxies go offline without notice, respond slowly, and fail often. Running a Scrapy spider against them means spending more time managing dead proxies than collecting data.

There's a security angle too: free proxy operators can inspect your traffic, which matters if your workflow touches any authenticated session.

For production, a paid residential pool pays for itself. GoProxies' rotating residential proxies are pay-as-you-go with no minimums – you pay for what you use.

Conclusion

Setting up rotating residential proxies with Scrapy is an intermediate-level task. You need to be comfortable with Python, Scrapy's settings system, and basic middleware concepts. The gateway approach is simpler and covers most use cases. List-based rotation with scrapy-rotating-proxies gives you more control but requires tuning the ban-detection policy for your specific target.

The practices that matter: rotate user agents alongside IPs, send realistic browser headers in a realistic order, use AutoThrottle rather than fixed delays, and reach for scrapy-playwright only when you genuinely need JavaScript rendering. Test your proxy setup against an IP-check endpoint before pointing the spider at a real target.

Gintarė specializes in content related to proxies, web scraping, data collection, and internet infrastructure. With a solid background in information technology, networking, and cybersecurity, she understands both the technical and practical aspects of large-scale data acquisition.

Turn data insights into growth with GoProxies
Millions of IPs are just a click away!

What’s a Rich Text element?

The rich text element allows you to create and format headings, paragraphs, blockquotes, images, and video all in one place instead of having to add and format them individually. Just double-click and easily create content.

Static and dynamic content editing

A rich text element can be used with static or dynamic content. For static content, just drop it into any page and begin editing. For dynamic content, add a rich text field to any collection and then connect a rich text element to that field in the settings panel. Voila!

How to customize formatting for each rich text

Headings, paragraphs, blockquotes, figures, images, and figure captions can all be styled after a class is added to the rich text element using the "When inside of" nested selector system.

FAQ

What Are Rotating Residential Proxies?
Rotating Residential Proxies offer you the best solution for scaling your scraping without getting blocked.

Rotating proxies provide a different IP each time you make a request. With this automated rotation of IPs, you get unlimited scraping without any detection. It provides an extra layer of anonymity and security for higher-demand web scraping needs.

IP addresses change automatically, so after the initial set up you’re ready to scrape as long and much as you need. IPs may shift after a few hours, a few minutes or after each session depending on your configuration. We do this by pulling legitimate residential IPs from our pool.
Why Do You Need Rotating Residential Proxies?
There are a number of use cases for rotating residential proxies. One of the most common ones is bypassing access limitations.

Some websites have specific measures in place to block IP access after a certain number of requests over an extended period of time.

This limits your activity and hinders scalability. With rotating residential IP addresses, it's almost impossible for websites to detect that you are the same user, so you can continue scraping with ease.
When to Use Static Residential Proxies Instead?
There are particular cases where static residential proxies may be more useful for your needs, such as accessing services that require logins.

Rotating IPs might lead to sites not functioning well if they are more optimised for regular use from a single IP.

Learn if our static residential proxies are a better fit for your needs.
Can I choose the IP location by city?
Yes. GoProxies has IPs spread across almost every country and city worldwide.
Can I choose the IP location by country state?
Yes. GoProxies has IPs spread across X countries with localised IPs in every state.

Does Scrapy support proxies out of the box?

Yes. Scrapy's built-in HttpProxyMiddleware handles basic proxy routing via request.meta['proxy']. For production use with rotation, you need either custom middleware or a dedicated library, since the built-in handler does no rotation and no health monitoring.

What's the difference between gateway and list-based proxy rotation?

Gateway rotation routes all requests through one endpoint that handles IP switching on the provider's side. List-based rotation cycles through individual proxy addresses locally, using middleware to monitor health and retire dead proxies. Gateway is simpler; list-based gives you more visibility and control over per-proxy behavior.

Will rotating residential proxies prevent all blocks?

No. They reduce blocks substantially but don't eliminate them. A complete setup also needs realistic user agents, consistent browser headers, sensible request timing, and sometimes a real browser engine via scrapy-playwright. Rotation covers the IP; fingerprinting and behavioral detection need separate treatment.

Is it legal to use Scrapy with proxies?

Yes, using Scrapy with proxies is legal. The legality question applies to what you scrape and how, not to the tools. Scraping publicly accessible data is generally permitted, but check a site's Terms of Service and robots.txt before running at scale.