Knowledge

How to Test Residential Proxy Speed, Success Rate, and Location Accuracy

Speed alone is a bad benchmark. How to measure residential proxy success rate, latency percentiles, and geo accuracy properly, with copy-paste Python tests.

Chris Collins

Chris Collins

July 14, 2026 · 9 min read

“How fast is it?” is the wrong first question to ask about a residential proxy. A proxy that answers in 200ms but gets blocked half the time is useless; a slightly slower one that returns the real page every time is exactly what you want. Before you trust a provider, or debug why your scraper is underperforming, you need to measure three things objectively: success rate, latency distribution, and location accuracy. This guide gives you copy-paste tests for each and, just as important, how to read the numbers without fooling yourself.

Everything below runs against the Shifter residential gateway (one endpoint, p.shifter.io:443, targeting encoded in the username), but the tests are provider-agnostic, swap the host and credentials and they work anywhere. Examples are Python; the client setup mirrors residential proxies with Python.

The three metrics that actually matter

Raw speed is the metric people fixate on and the least useful in isolation. Residential IPs route through real consumer devices, so they’re inherently a bit slower than datacenter, that’s expected, and not a defect. What actually determines whether a proxy is good for your job:

  • Success rate — of the requests you send, how many return the real page (not a block, CAPTCHA, or error). This is the number that decides whether your data pipeline is complete.
  • Latency distribution — not the average, the spread. The p95 and the tail tell you how bad your slow requests get, which is what hurts at scale.
  • Location accuracy — when you target a country or city, does the exit IP actually resolve there? For any geo-targeted use case, wrong-location data is silently wrong data.

Measure all three. A single number, especially average speed, hides the problems that matter.

Setup

Keep credentials in environment variables, and define one helper that builds a targeted proxy URL:

import os, time, statistics, requests
USER = os.environ["SHIFTER_USER"]
PASS = os.environ["SHIFTER_PASS"]
GATEWAY = "p.shifter.io:443"
def proxy(country="us", city=None, sid=None, ttl=600):
parts = [USER, "country", country]
if city: parts += ["city", city]
if sid: parts += ["sid", sid, "ttl", str(ttl)]
url = f"http://{'-'.join(parts)}:{PASS}@{GATEWAY}"
return {"http": url, "https": url}

First, sanity-check that you’re actually going through the proxy at all, a surprising number of “the proxy is slow” reports are traffic that never left the local machine:

r = requests.get("https://api.ipify.org?format=json", proxies=proxy("us"), timeout=30)
print(r.json()) # should be a residential IP, not your own

Test 1: Location accuracy

Target a country (and optionally a city), ask a geo-IP endpoint where the exit IP is, and measure the match rate over many samples. Rotate each request (no sid) so you’re sampling the pool, not one IP:

def test_geo(country="us", n=50):
hits = 0
for _ in range(n):
try:
r = requests.get("http://ip-api.com/json", proxies=proxy(country), timeout=30)
data = r.json()
if data.get("countryCode", "").lower() == country.lower():
hits += 1
except requests.RequestException:
pass
print(f"{country}: {hits}/{n} = {hits/n:.0%} country-accurate")
test_geo("us")
test_geo("de")

How to read it. Country accuracy should be very high (think high-90s%). City accuracy is inherently fuzzier, geo-IP databases disagree about which city an IP belongs to, so a “miss” may be a database disagreement, not a proxy sending you to the wrong place. Two caveats that save you from false conclusions: test city accuracy against the same geo-IP source your target site uses if you can, and never trust a single lookup provider as ground truth.

Test 2: Latency, measured as a distribution

Measure total request time over many samples and report percentiles, not the mean. An average smooths over exactly the slow tail that hurts you at scale:

def test_latency(country="us", n=50, url="https://api.ipify.org"):
times = []
for _ in range(n):
start = time.perf_counter()
try:
requests.get(url, proxies=proxy(country), timeout=30).raise_for_status()
times.append((time.perf_counter() - start) * 1000) # ms
except requests.RequestException:
pass
times.sort()
p = lambda q: times[min(len(times)-1, int(q*len(times)))]
print(f"n={len(times)} p50={p(0.5):.0f}ms p95={p(0.95):.0f}ms max={times[-1]:.0f}ms")
test_latency("us")

How to read it. Look at p50 versus p95: a tight spread (p95 close to p50) means predictable performance; a p95 many times the p50 means a heavy tail that will stall concurrent workers. To separate proxy overhead from target slowness, run the same test against a fast, neutral endpoint (like an IP echo) and against your real target, the difference is roughly the target’s own latency. And don’t compare residential p50 to datacenter p50 as if they’re the same product; residential adds a real-device hop by design (see residential vs datacenter proxies).

Test 3: Success rate, the one that matters most

Run requests against your actual target (success is target-specific) and classify each outcome. The trap here is trusting the HTTP status: a 200 OK can still be a soft block, a CAPTCHA or “please verify” page served with a 200. So validate the content, not just the code:

def looks_like_real_page(html):
# Tune these to your target: presence of expected markup, absence of block markers.
block_markers = ("captcha", "are you a robot", "access denied", "unusual traffic")
low = html.lower()
return len(html) > 1000 and not any(m in low for m in block_markers)
def test_success(url, country="us", n=100):
ok, soft_block, errors = 0, 0, 0
for _ in range(n):
try:
r = requests.get(url, proxies=proxy(country), timeout=30)
if r.status_code == 200 and looks_like_real_page(r.text):
ok += 1
else:
soft_block += 1 # 200-but-block, 403/429/503, etc.
except requests.RequestException:
errors += 1 # timeouts, connection failures
print(f"success={ok/n:.0%} blocked={soft_block/n:.0%} errors={errors/n:.0%}")
test_success("https://your-target.example/page", country="us")

How to read it. Success rate is the headline metric for scraping, it’s the fraction of requests that returned usable data. Break failures into blocked (the target refused you) versus errors (network/timeout), because they have different fixes: consistent blocks point to IP quality or request behavior (why scrapers get blocked), while errors point to timeouts or over-tight geo filters. Note that per-request rotation and a retry loop raise your effective success rate, so measure both raw (single-shot) and with-retry if that’s how you’ll run in production.

Putting it together

A minimal harness runs all three and prints one report:

if __name__ == "__main__":
for cc in ("us", "de", "gb"):
print(f"\n== {cc.upper()} ==")
test_geo(cc, n=50)
test_latency(cc, n=50)
test_success("https://your-target.example/page", country=cc, n=100)

Read it in this order: success rate first (is the data even coming back?), then the latency tail (will it keep up under concurrency?), then geo accuracy (is it the right data?). A provider that wins on p50 but loses on success rate is the wrong choice for scraping.

Common pitfalls that produce misleading numbers

  • Reporting the mean latency. Use percentiles, the tail is what breaks concurrent jobs.
  • Trusting status 200. Soft blocks return 200 with a CAPTCHA page. Validate content.
  • Too-small samples. 5 requests tell you nothing; use enough (50 to 100+) to see the distribution and the tail.
  • Wrong geo ground truth. Different geo-IP databases disagree, especially at city level. Test against the source your target uses, and treat city accuracy as inherently fuzzier than country.
  • Comparing residential to datacenter on raw speed. Different products; residential trades some latency for real-user trust and a higher success rate on defended targets (which proxies are better for scraping).
  • Not confirming you’re on the proxy. Verify the exit IP changed before benchmarking anything.

A note on doing it responsibly

Benchmark against endpoints you’re allowed to hit, public IP-echo services for speed and geo, and your own authorized targets for success rate. Keep sample sizes reasonable, don’t hammer a site to measure it, and honor rate limits. Our acceptable use policy is the source of truth for what’s allowed on Shifter.

FAQ

What’s a good success rate for residential proxies? It depends entirely on the target, a well-defended site will block more than an open one. What matters is measuring it against your target and comparing providers on the same target. Break failures into blocks versus errors to know what to fix.

Why is my residential proxy slower than a datacenter one? Because it routes through a real consumer device, that hop adds latency by design. That’s the tradeoff for real-user trust and higher success rates on sites that block datacenter IPs. Judge residential on success rate and the latency distribution, not raw p50 versus datacenter.

How do I test city-level location accuracy? Target the city and check the exit IP against a geo-IP source, ideally the same one your target uses. Expect country accuracy to be very high and city accuracy to be fuzzier, since geo-IP databases disagree on city boundaries. See when city-level targeting matters.

Why does a 200 response still count as a failure? Because sites serve soft blocks, CAPTCHA or “verify you’re human” pages, with a 200 status. If you only check the status code, you’ll record blocks as successes. Validate the content matches what a real page looks like.

Should I test with rotating or sticky sessions? Both, depending on your workload. Rotating (no sid) samples the pool and suits broad scraping; sticky (sticky vs rotating) tests multi-step flows. Measure the mode you’ll actually run in production.

The bottom line

Testing a residential proxy well means refusing to reduce it to a single speed number. Measure success rate against your real target with content validation, latency as a distribution with a look at the p95 tail, and location accuracy with a sensible geo-IP ground truth, and you’ll know whether a provider actually fits your job instead of guessing from a marketing figure. Pool quality is what moves all three, so understanding IP reputation helps you interpret the results.

If you’re benchmarking providers or debugging your own success rate, run these tests against the residential gateway and your target of choice. The pricing page has per-GB plans to trial it against the markets and sites that matter to you.

Ready to get started?

Try Shifter's residential proxies, 205M+ IPs, 195+ countries, from $0.75/GB.

Get Started