Scraping Without Blocks · Chapter 02
Picking Proxies for the Target
Sorts targets into four difficulty tiers by observable signs, matches a proxy type to each, and builds a routing ladder that starts cheap and escalates.
- Four target difficulty tiers
- Rotation versus sticky sessions
- Geo-targeting for localized content
- A routing ladder that escalates
5 min read · page 11 of the PDF
A parts catalog on a small shop's own server answers plain datacenter requests all day. A search page on a large marketplace may refuse the same request before any HTML is built. Proxy choice starts with knowing which kind of target is on the list.
Sorting targets by what can be observed
Target difficulty does not need guessing. A short probe at a polite rate shows it. Fetch one page with a plain HTTP client from a datacenter IP, then from a residential IP, then in a real browser. Note where the full page first appears.
Challenges or blocks a plain HTTP client even from residential IPs.
Refuses datacenter ranges on the first request but serves residential ones.
Serves datacenter IPs at low volume, then answers 429 or 403.
Serves full HTML to a plain client at a polite rate.
Tiers belong to endpoints, not to whole sites. Product pages may sit in tier 2 while search on the same domain sits in tier 4. Tiers also drift, so the probe is worth repeating whenever success rates move.
Which proxy type fits which tier
Each tier fails at a different detection layer, so each needs a different fix. The rule is to buy the cheapest type that clears the layer in question.
| Tier | Start with | Why |
|---|---|---|
| 1 · Open | Shared or rotating datacenter | Reputation is not checked, so the cheapest bytes win |
| 2 · Rate-limited | A larger datacenter pool, or static ISP | More addresses keep each one under the limit |
| 3 · Reputation-gated | Rotating residential or static ISP | Addresses are registered to consumer networks |
| 4 · Fingerprint-gated | Residential plus a browser, or a scraper API | The client must change, not only the IP |
Datacenter proxies are fast and cheap, and their ASNs are easy to recognize. ISP proxies are hosted in datacenters but registered to consumer providers. They stay fixed, which suits sticky work. Residential proxies exit through home connections, so they are slower and less stable but hard to tell from visitors.
Mobile proxies exit through carrier networks, where many subscribers share one address. Sites tend to be cautious about refusing those addresses. Mobile is the most expensive type and fits mobile-only content, or tier 3 targets where residential still fails.
| Proxy type | Billed by | Rough entry price |
|---|---|---|
| Rotating or shared datacenter | Traffic | Roughly $0.10 to $1.40 per GB |
| Dedicated datacenter | IP | Roughly $1 to $2.50 per IP per month |
| Static ISP | IP | Roughly $1 to $3.50 per IP per month |
| Rotating residential | Traffic | Roughly $1 to $5 per GB, less at volume |
| Rotating mobile | Traffic | Roughly $1 to $8.50 per GB |
These ranges are rough guidance only. Live figures are on the ProxyLook pricing page, and the cost calculator turns monthly traffic in GB into an estimated spend. Pool sizes on provider pages are vendor-stated and say little about success on one specific target.
Rotation per request or sticky sessions
With per-request rotation, every request leaves from a new exit IP. That fits independent URLs, such as 50,000 product pages where each response stands alone. No single address builds up a history.
A sticky session holds one exit IP for a window of time. Providers usually expose it through a session ID in the proxy username, in a form like USERNAME-country-us-session-a1b2c3. That syntax is illustrative. The exact parameters come from the provider's docs.
Sticky sessions fit paginated and stateful crawls. Some searches hand out a cursor token. Some listings keep their filters in a cookie. Both expect page 3 to come from the visitor who loaded page 2. A new IP mid-sequence can reset the state or draw a challenge.
Residential sticky sessions end when the provider's window closes or the home device goes offline. A paginated crawl should checkpoint its position and restart the sequence under a new session. Static ISP addresses hold for as long as they are rented.
Geo-targeting for localized content
Prices, currency, stock and search rankings change with the visitor's country, and sometimes with the city. A crawl for German prices needs German exits. Country targeting is set at the gateway, usually by another username parameter.
Three checks keep localized data honest. Verify the exit country with the proxy checker, because geolocation databases disagree. Send an Accept-Language header that matches the country. Confirm the currency or region marker in the response body. City-level targeting shrinks the usable pool, so it is worth using only when the data varies by city.
A routing ladder that starts cheap
A crawler with one proxy type overpays on easy targets or fails on hard ones. A ladder sends each domain through the cheapest route that works and moves up only on evidence.
- Start on datacenter. Every new domain begins on the cheapest rung with a plain HTTP client.
- Widen the pool. If 429s dominate, slow down first, then add addresses or move to ISP.
- Switch to residential. If 403s arrive on the first request, datacenter ranges are being refused.
- Add a browser. If residential IPs still fail with an HTTP client, the client is the problem.
- Hand off the remainder. Send the hardest endpoints to a scraper API, covered in chapter 5.
The class below keeps a rung per domain. It moves a domain up one rung when the success share over a recent window drops under a floor. The floor of 0.8 is an example value to tune, not a recommendation.
from collections import defaultdict, deque
LADDER = ("datacenter", "isp", "residential", "browser", "api")
WINDOW = 200 # recent results remembered per domain
FLOOR = 0.8 # example value: escalate below this success share
class Router:
def __init__(self) -> None:
self.rung: dict[str, int] = defaultdict(int)
self.recent: dict[str, deque[bool]] = defaultdict(
lambda: deque(maxlen=WINDOW)
)
def route(self, domain: str) -> str:
return LADDER[self.rung[domain]]
def record(self, domain: str, ok: bool) -> None:
window = self.recent[domain]
window.append(ok)
if len(window) < WINDOW:
return
top = len(LADDER) - 1
if sum(window) / WINDOW < FLOOR and self.rung[domain] < top:
self.rung[domain] += 1
window.clear()Two habits make the ladder pay off. Store the rung with every result, so cost can be reported per route. Send a small sample back down a rung now and then, because defenses change in both directions.
Quick decisions
The table compresses the chapter into first moves. Each row assumes the probe from the first section has already been run.
| If… | Then… |
|---|---|
| A plain client gets full pages from a datacenter IP | Stay on datacenter and spend the effort on rate limits |
| Requests succeed, then turn into 429s | Lower the rate, then widen the pool |
| Datacenter IPs get 403 on the first request | Move that domain to residential or ISP |
| The crawl is paginated or holds state in cookies | Use a sticky session for each sequence |
| The data differs by country | Set the country and verify it in the response |
| Residential IPs fail with an HTTP client | Change the client before buying mobile |
Key takeaways
- Probe each endpoint at a polite rate and sort it into one of four tiers.
- Buy the cheapest proxy type that clears the layer the target checks.
- Rotate per request for independent URLs and hold a sticky session for paginated or stateful sequences.
- Verify the exit country and match the language header when the content is localized.
- Route every domain through a ladder that starts on datacenter and escalates on evidence.
Prefer the designed PDF? Scraping Without Blocks is 49 pages, free, with no sign-up.
Download the PDF