Proxies for Web Scraping

Which targets need residential IPs, how to size concurrency, and what a page really costs · checked 3 November 2026
Web scraping

Most scraping projects fail for the same reason: they treat every target the same way. A sitemap and a marketplace product page have nothing in common — one is a static file, the other runs ASN checks, request-rate scoring and TLS fingerprinting. Paying for residential traffic on the first is waste; using a datacenter IP on the second is a guaranteed block. The work is in the classification, not in the proxy list.

Classify the target before you choose a pool

Target classExamplesWhat it checksPool to use
OpenSitemaps, static docs, public APIs, government dataRate limits onlyDirect, or datacenter
Light defenceNews sites, blogs, small shopsUser agent, crude rate limitsDatacenter, rotate on 429
ASN-awareSearch engines, marketplaces, travelASN, IP reputation, request rateResidential, rotating
Fingerprint-awareSocial platforms, ticketing, large retailersASN + TLS/HTTP2 fingerprint + behaviourResidential + matching browser profile

The practical consequence: route per host. A single crawl usually touches a protected HTML page, an unprotected JSON API and an image CDN, and only the first needs a residential exit. Splitting traffic this way commonly cuts residential gigabytes by a third without touching success rate.

Rotate per request, and control the two things the target can see

You cannot control how a site scores IP reputation. You can control requests per IP and request shape. Rotating per request handles the first; realistic headers, a real user agent, cookie persistence and paced intervals handle the second.

# Scrapy: rotation at the middleware level + a humanly possible rate
# settings.py
CONCURRENT_REQUESTS = 16
CONCURRENT_REQUESTS_PER_DOMAIN = 8
DOWNLOAD_DELAY = 1.0
RANDOMIZE_DOWNLOAD_DELAY = True
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 4.0
RETRY_TIMES = 2
DOWNLOADER_MIDDLEWARES = {
    "scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": None,
    "myproject.middlewares.RotatingProxy": 543,
}
# middlewares.py — new session id per request
import uuid

class RotatingProxy:
    def __init__(self, pwd):
        self.pwd = pwd

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings.get("PROXY_PASSWORD"))

    def process_request(self, request, spider):
        sid = uuid.uuid4().hex[:12]
        user = "ACCOUNT-country-us-sid-%s" % sid
        request.meta["proxy"] = "http://%s:%s@GATEWAY_HOST:PORT" % (user, self.pwd)
Retry on a new IP, not the same one. Scrapy's default retry reuses the request meta, and therefore the same session. Regenerate sid inside the retry path — a 403 usually means that specific exit is burned, not that the URL is unreachable.

Do not render what you can read

A headless browser is 10 to 50 times the bandwidth of a plain HTTP request, and gigabytes are what you are buying. Check three things before you reach for Playwright: does the page ship data in JSON-LD or a __NEXT_DATA__ block, is there a public or internal JSON endpoint the page calls, and does the HTML already contain what you need without JavaScript. In practice, roughly two thirds of product and listing pages answer yes to one of those.

# price and stock often sit in structured data, no browser needed
import json, re, requests

r = requests.get(url, proxies=PROXY, timeout=30)
m = re.search(r'', r.text, re.S)
if m:
    data = json.loads(m.group(1))
    print(data.get("offers", {}).get("price"))

What a page actually costs

Work in billable gigabytes, not in requests. Take a 400 KB HTML page, fetched once, at a 95% success rate:

ItemValueHow it is derived
Page weight400 KBHTML only; images blocked
Pages per GB2,5001,000,000 KB / 400 KB
Attempts per 1,000 usable pages1,0531,000 / 0.95
Billable traffic0.42 GB1,053 × 400 KB
Cost at $1/GB$0.42 per 1,000 pagesTraffic × unit price

Two things follow. First, retries are not free — every blocked request is paid for and produces nothing, which is why success rate dominates the price comparison. Second, the cheapest optimisation is usually fewer bytes per page, not a cheaper proxy: dropping a headless browser in favour of the embedded JSON often cuts cost by 80% before you negotiate anything.

A crawl loop that survives real targets

  1. Fetch the cheap way first. Plain HTTP, no browser, per-request rotation.
  2. Detect the block, not just the status. A 200 page that says "unusual traffic" is a block. Match on known markers, page length and the presence of the field you came for.
  3. Escalate once. If the field is missing, retry with a new session; if it is still missing, try the country-specific exit; only then consider rendering.
  4. Record the outcome per host. Success rate, median latency, bytes per usable row. This table tells you which targets deserve residential traffic and which do not.
  5. Back off on 429. Cut concurrency per domain in half and raise the delay; do not simply add more IPs.

Concurrency, in numbers you can act on

Concurrency per domainWhen it worksWatch for
1–2Fingerprint-aware sites, first exploratory runNothing yet — this is the baseline
4–8ASN-aware marketplaces with a 95% success rateSuccess rate dropping below 90%
8–16Light-defence sites on datacenter exits429 bursts; halve and retest
16+Your own infrastructure or an authorised testAnything else — you are the problem

Treat these as starting points to tune against your own 403 rate, not as limits. The single biggest mistake is raising concurrency to compensate for a low success rate — that makes the block worse, not better.

Frequently asked questions

Do I need residential proxies for web scraping?

Only for targets that defend themselves. Static HTML sites, public APIs and sitemaps do not care where you come from — scraping them through residential traffic is paying a premium for nothing. Marketplaces, search engines, social platforms, travel sites and real-estate portals do run ASN and behaviour checks, and there a residential exit is the difference between 60% and 95% success.

How many concurrent requests can I run?

Concurrency is limited by the target, not by us: one account can run as many parallel requests as your workers need. As a starting point keep 5–10 concurrent requests per domain, watch the 429/403 rate, and raise it in steps. More concurrency on one domain gets you blocked faster than more throughput.

Is rotating better than sticky for scraping?

Rotating, for anything read-only. One request per exit keeps per-IP counts low, which is the strongest signal you can control. Use sticky sessions only when a workflow needs continuity — login, cart, paginated results tied to a session cookie.

How much does scraping cost per 1,000 pages?

At a 400 KB page and a 95% success rate, roughly 0.42 GB of billable traffic per 1,000 usable pages, so about $0.42 at $1/GB. Blocked requests still cost traffic, so your real figure is higher — measure it on your own target rather than trusting a generic estimate.

Do I need a headless browser?

Only when the data is rendered client-side or the site requires JavaScript to issue a token. A headless browser costs 10–50x the bandwidth of a plain HTTP request, so try the API or the embedded JSON first. Many product pages ship price and stock in a __NEXT_DATA__ or JSON-LD block.

What about robots.txt and terms of service?

Read them. Proxies change your origin, not your obligations: only collect public data you have a right to, respect robots directives and rate limits, and do not use residential exits to bypass an access decision a site has already made about you. Lawful basis is the operator's responsibility.

Run your own target list first

Residential from $1/GB billed by traffic, static ISP from $2/IP per month from 15 IPs. No monthly commitment — measure your success rate before you scale.

Create account