Key takeaways
- Datacentre proxies are fast and cheap; residential proxies are trusted and expensive. Almost every real deployment ends up using both.
- The right question is not "which is better" but "what is the cheapest pool that still gets an acceptable success rate on this specific target?"
- Sticky sessions matter more than most buyers expect: anything with a login, a cart or a multi-step form breaks when the exit address changes mid-flow.
- Cost per successful request is the only metric worth optimising. A pool that is four times cheaper but fails 70% of the time is not cheaper.
- ISP (static residential) proxies sit between the two families and are the sweet spot for steady, moderate-volume work.
Proxy vendors sell on adjectives — "premium", "clean", "elite", "unlimited". None of those words describe anything measurable. This article replaces them with the four properties that actually determine whether a pool will work for your job: where the address is registered, how many people share it, how long you keep it, and what it costs per successful request.
The four families you can actually buy
Everything on the market is a variation of four underlying supply models. Understanding the supply model tells you more than any feature list.
| Type | Address registered to | Typical price | Trust level | Speed |
|---|---|---|---|---|
| Datacentre | Hosting provider ASN | $0.50–$3 per IP / month | Low | Very high |
| ISP / static residential | Consumer ISP, hosted in a datacentre | $2–$8 per IP / month | High | Very high |
| Rotating residential | Consumer ISP, real end-user lines | $2–$9 per GB | High | Moderate |
| Mobile (4G/5G) | Mobile carrier, CGNAT shared | $8–$30 per GB | Very high | Variable |
Datacentre proxies
These are addresses announced by a hosting company. They are provisioned in seconds, cost almost nothing, and deliver gigabit throughput with 5–20 ms latency. The catch is that anyone can rent them just as cheaply, which is exactly why anti-fraud vendors maintain exhaustive lists of hosting ranges and discount them by default.
They remain the correct choice for a large share of real work: fetching public APIs, monitoring your own infrastructure, crawling documentation and open data, load testing, and any target that does not run commercial bot management. If your success rate on a target is above 95% with datacentre IPs, paying for residential is simply burning money.
ISP (static residential) proxies
An ISP proxy is an address that a consumer ISP has allocated, but which is announced from — and physically served out of — a datacentre. You get residential registration data with datacentre speed and stability. The address is yours for the life of the subscription, so sessions persist for weeks.
This is the most under-used product in the category. For steady, moderate-volume work against a small number of targets — ad verification, dashboard monitoring, managing a handful of accounts — it usually beats rotating residential on both cost and reliability.
Rotating residential proxies
Here the exit is a real consumer line. Traffic is billed per gigabyte and you draw from a pool that may span millions of addresses. Rotation can be per request, per session, or sticky for a fixed window.
The strengths are breadth and trust: you can appear in almost any city, and the addresses carry genuine household reputation. The weaknesses are latency (you are traversing a real home connection, so 200–800 ms is normal), variability (a residential line can vanish mid-request when someone unplugs a router) and cost per gigabyte.
Mobile proxies
Mobile exits sit behind carrier-grade NAT, which means thousands of genuine subscribers share the same public address at any moment. Blocking that address is expensive for the platform, so mobile IPs enjoy the highest tolerance of any category. You pay for that in price and in unpredictable throughput.
Choosing: a decision procedure, not a preference
Work through these questions in order. The first "yes" usually determines your answer.
- Does the target run commercial bot management? Check for Cloudflare, Akamai, DataDome, PerimeterX or hCaptcha in the response headers and challenge pages. If not, use datacentre and stop.
- Does the workflow require a stable identity across steps? Logins, carts, multi-page forms, anything stateful — you need sticky sessions, so ISP or long-sticky residential.
- Do you need many distinct cities or countries? Rotating residential is the only family with real geographic breadth.
- Is the target unusually aggressive — sneaker drops, ticketing, high-value retail? Mobile, accepting the cost.
- Is throughput the binding constraint? ISP proxies give you residential registration without the latency penalty of a real home line.
The only metric that matters: cost per successful request
Headline pricing is deliberately non-comparable — per IP, per GB, per request. Convert everything to cost per successful request and the picture clarifies immediately.
cost_per_success = pool_cost_for_the_run / successful_requests
# Worked example — 100,000 product pages, ~180 KB each (18 GB total)
#
# Datacentre: 50 IPs x $1.50 = $75.00
# success rate 22% = 22,000 pages
# -> $0.0034 per success
#
# Residential: 18 GB x $4.50 = $81.00
# success rate 94% = 94,000 pages
# -> $0.00086 per success
#
# The "expensive" pool is 4x cheaper per usable page.
Two things follow. First, always measure on your real target before buying volume — success rates differ by an order of magnitude between sites. Second, the cheap pool is genuinely cheaper whenever it works, so the optimum is nearly always a cascade rather than a single choice.
The cascade pattern
Send every request to the cheapest pool first and escalate only on failure:
POOLS = [
("datacenter", 0.0004), # cost per request, cheapest first
("isp", 0.0021),
("residential", 0.0090),
]
def fetch(url, budget_tier=2):
for name, _cost in POOLS[:budget_tier + 1]:
resp = request_via(name, url)
if ok(resp): # 200 + expected selector present
record_success(name)
return resp
record_failure(name)
raise Exhausted(url)
In production this routinely cuts proxy spend by 50–70% versus sending everything to residential, because the majority of URLs on most sites are not defended at all. Track the per-pool success rate per domain and let the router learn where to start.
Rotation and sticky sessions
Rotation policy breaks more scrapers than any other setting. Three modes exist:
- Per request. A new exit for every call. Maximum spread, and completely incompatible with anything stateful.
- Sticky window. The same exit for a fixed period, commonly 1, 10 or 30 minutes, selected via the username string or a session parameter.
- Static. The address is yours until you release it. ISP proxies work this way.
Most gateways encode the session in the credentials, which makes it easy to bind a session to a logical worker:
# Typical gateway syntax — check your provider's docs
user-country-us-session-a7f3c1-sesstime-10:[email protected]:7000
# ^ geo ^ sticky key ^ minutes
# In Python: one sticky exit per worker, reused for the whole flow
import requests, uuid
sess_id = uuid.uuid4().hex[:8]
proxy = f"http://user-country-us-session-{sess_id}:[email protected]:7000"
s = requests.Session()
s.proxies = {"http": proxy, "https": proxy}
s.get("https://example.com/login") # same exit
s.post("https://example.com/session") # same exit
s.get("https://example.com/account") # same exit
Geotargeting and how accurate it really is
Country-level targeting is reliable. City-level targeting is best-effort, and the reason is worth understanding: IP geolocation databases are inferences, not ground truth. They are built from registry data, latency triangulation and self-reported feeds, and they disagree with each other constantly.
Practical guidance:
- Verify against the database your target uses, not the one your provider quotes. If a retailer uses MaxMind, check MaxMind.
- Expect 80–95% city accuracy on a good residential pool, and treat outliers as normal rather than as a fault.
- Match everything else to the claimed location:
Accept-Language, browser timezone, locale, and currency. A "Chicago" IP with a Kyiv timezone is a contradiction that costs you more than the geo gained.
SOCKS5 or HTTP?
HTTP(S) proxies understand the request and can therefore cache, filter and rewrite headers. SOCKS5 operates at the transport layer and simply forwards bytes, which makes it protocol-agnostic and slightly lower overhead.
| Situation | Use | Why |
|---|---|---|
| Browser automation, HTTP scraping | HTTP(S) | Better tooling support; header control |
| Non-HTTP protocols, custom clients | SOCKS5 | Works with any TCP traffic |
| DNS must resolve at the exit | SOCKS5h | Prevents local DNS leaking your real resolver |
| Per-request header rewriting | HTTP(S) | The proxy can see and modify the request |
Note the socks5h distinction carefully: plain socks5 resolves the hostname locally and then connects, which sends your DNS queries to your own resolver and leaks the target list. socks5h resolves at the exit. This one letter is a recurring cause of "why is my scraper still being detected?".
Building an honest evaluation harness
Never buy on a vendor's success-rate claim. Spend one day and 5,000 requests on a comparison, using your real targets:
- Pick three representative URLs per target: a listing page, a detail page and something behind a form.
- Run identical request logic through each candidate pool with the same headers, the same concurrency and the same delays.
- Log for every request: pool, status code, byte count, wall-clock duration, whether the expected selector was present, and whether a challenge page was returned.
- Compute success rate, p50/p95 latency, bytes per success and cost per success.
- Repeat at a different hour — residential pool quality varies with the diurnal cycle of the underlying households.
import csv, time, requests
def probe(pool, proxy, url, out):
t0 = time.perf_counter()
try:
r = requests.get(url, proxies={"https": proxy}, timeout=25)
ok = r.status_code == 200 and "product-title" in r.text
out.writerow([pool, url, r.status_code, len(r.content),
round(time.perf_counter() - t0, 3), ok])
except Exception as exc:
out.writerow([pool, url, "ERR", 0,
round(time.perf_counter() - t0, 3), False, type(exc).__name__])
with open("bench.csv", "w", newline="") as fh:
w = csv.writer(fh)
w.writerow(["pool", "url", "status", "bytes", "seconds", "ok", "error"])
# ... loop your pools x urls x repetitions here
Seven expensive mistakes
- Buying residential for undefended targets. Measure first; most URLs need nothing special.
- Rotating inside a stateful flow. Bind one session to one identity for its whole lifetime.
- Leaving DNS on the local resolver. Use
socks5h, or resolve remotely in your HTTP client. - Mismatching headers and geography. Locale, timezone and language must agree with the exit's claimed city.
- Ignoring bytes. On per-GB billing, blocking images and fonts often cuts spend by 60% with no loss of data.
- Treating "unlimited" as unlimited. Read the fair-use clause; the throttle is always in there somewhere.
- No per-domain metrics. Without per-target success rates you cannot route intelligently, and you will overpay forever.
The short version
Start cheap and escalate. Use datacentre proxies wherever they work, ISP proxies for steady stateful work on a handful of targets, rotating residential for breadth and defended targets, and mobile only when nothing else clears the bar. Measure cost per success, per domain, and let that number — not a vendor's adjective — decide where each request goes.
If your work is interactive rather than scripted, a full desktop may serve you better than a proxy: see our guide to residential RDP. If you are building a crawler around these pools, continue to web scraping at scale. Current plans and per-GB rates are on the pricing page.
