If you're running any proxy-dependent workflow at scale — scraping, multi-account management, automation — proxy speed and bandwidth accuracy are two of the most consequential variables in your stack. Yet most people only think about them after something breaks: a scraping job that runs 3x slower than expected, a billing discrepancy that's hard to explain, a provider whose latency spikes under load.
This guide covers the practical methods for measuring both throughput and latency from the command line and from Python, explains what the numbers actually mean, and includes a benchmark comparison across proxy types so you have a reference point before you start testing.
What You're Actually Measuring
It's worth separating two things that often get conflated:
Latency is the time from sending a request to receiving the first byte of the response. For proxy connections this includes DNS resolution (if the proxy doesn't do remote DNS), TCP handshake to the proxy, authentication, the proxy's own request to the target, and the target's response time. For scraping and automation, latency determines how many requests per minute you can push per concurrent connection.
Throughput is how many bytes actually move through the tunnel per second once the connection is established. For bandwidth-billed proxies, this is what determines your cost per operation. A provider that rounds up to the nearest MB per request, counts TLS handshake bytes as metered traffic, or charges for failed requests will show higher consumption in your dashboard than what you actually received.
Both numbers matter, but they fail in different ways. A proxy with great throughput but high latency is expensive for high-frequency request patterns. A proxy with great latency but padded bandwidth billing will quietly cost you more than expected at scale.
CLI Testing: curl and wget
The fastest way to get a proxy speed reading without writing any code is curl. It supports HTTP and SOCKS5 proxies natively and can output precise timing breakdowns.
Basic throughput test via HTTP proxy:
curl -x http://user:pass@host:port \
-o /dev/null \
-w "time_connect: %{time_connect}s\ntime_starttransfer: %{time_starttransfer}s\ntime_total: %{time_total}s\nspeed_download: %{speed_download} B/s\nsize_download: %{size_download} B\n" \
https://httpbin.org/bytes/10485760
The -w format string extracts the timing breakdown you actually need:
time_connect — time to establish the TCP connection to the proxy
time_starttransfer — time to first byte (TTFB), includes proxy overhead
time_total — total transfer time
speed_download — average throughput in bytes/second
size_download — bytes actually received (compare to expected payload size)
For SOCKS5 proxies:
curl -x socks5h://user:pass@host:port \
-o /dev/null \
-w "time_total: %{time_total}s\nspeed_download: %{speed_download} B/s\n" \
https://httpbin.org/bytes/10485760
The socks5h scheme tells curl to do DNS resolution through the proxy rather than locally — important for accurate latency measurement and for preventing DNS leaks.
Batch testing a proxy list with a shell loop:
!/bin/bash
PROXIES=(
"http://user:pass@host1:port"
"http://user:pass@host2:port"
"http://user:pass@host3:port"
)
PAYLOAD_MB=10
PAYLOAD_BYTES=$((PAYLOAD_MB * 1024 * 1024))
URL="https://httpbin.org/bytes/${PAYLOAD_BYTES}"
echo "proxy,time_total_s,speed_bps,bytes_received"
for PROXY in "${PROXIES[@]}"; do
RESULT=$(curl -x "$PROXY" -o /dev/null --max-time 30 \
-w "%{time_total},%{speed_download},%{size_download}" \
-s "$URL" 2>/dev/null)
echo "$PROXY,$RESULT"
done
This produces a CSV you can pipe into any analysis tool. Add -s to suppress progress output and --max-time 30 to timeout slow proxies cleanly.
Python Testing: requests and aiohttp
For integration into existing workflows, Python gives you more control over what you measure and how you aggregate it.
Single proxy benchmark with requests:
import requests
import time
def benchmark_proxy(proxy_url: str, payload_mb: int = 10) -> dict:
payload_bytes = payload_mb * 1024 * 1024
proxies = {"http": proxy_url, "https": proxy_url}
url = f"https://httpbin.org/bytes/{payload_bytes}"
start = time.perf_counter()
try:
resp = requests.get(url, proxies=proxies, stream=True, timeout=30)
ttfb = time.perf_counter() - start # time to first byte (approx)
bytes_received = 0
for chunk in resp.iter_content(chunk_size=65536):
bytes_received += len(chunk)
elapsed = time.perf_counter() - start
throughput_mbps = (bytes_received * 8) / (elapsed * 1_000_000)
return {
"proxy": proxy_url,
"status": resp.status_code,
"ttfb_s": round(ttfb, 3),
"elapsed_s": round(elapsed, 3),
"bytes_received": bytes_received,
"expected_bytes": payload_bytes,
"throughput_mbps": round(throughput_mbps, 2),
"billing_delta_pct": round(
(payload_bytes - bytes_received) / payload_bytes * 100, 2
),
}
except Exception as e:
return {"proxy": proxy_url, "error": str(e)}
result = benchmark_proxy("http://user:pass@host:port", payload_mb=50)
print(result)
The billing_delta_pct field is the key metric for billing verification: if you're pulling a 50 MB payload and receiving 49.2 MB, your provider's dashboard should not show 52 MB consumed.
Concurrent benchmark across a proxy list with asyncio + aiohttp:
import asyncio
import aiohttp
import time
from typing import List
async def benchmark_single(
session: aiohttp.ClientSession,
proxy: str,
payload_bytes: int,
url: str
) -> dict:
start = time.perf_counter()
try:
async with session.get(url, proxy=proxy, timeout=aiohttp.ClientTimeout(total=30)) as resp:
ttfb = time.perf_counter() - start
data = await resp.read()
elapsed = time.perf_counter() - start
throughput_mbps = (len(data) * 8) / (elapsed * 1_000_000)
return {
"proxy": proxy,
"status": resp.status,
"ttfb_s": round(ttfb, 3),
"elapsed_s": round(elapsed, 3),
"bytes_received": len(data),
"expected_bytes": payload_bytes,
"throughput_mbps": round(throughput_mbps, 2),
}
except Exception as e:
return {"proxy": proxy, "error": str(e)}
async def benchmark_batch(proxies: List[str], payload_mb: int = 10):
payload_bytes = payload_mb * 1024 * 1024
url = f"https://httpbin.org/bytes/{payload_bytes}"
async with aiohttp.ClientSession() as session:
tasks = [
benchmark_single(session, proxy, payload_bytes, url)
for proxy in proxies
]
results = await asyncio.gather(*tasks)
return results
proxies = [
"http://user:pass@host1:port",
"http://user:pass@host2:port",
"http://user:pass@host3:port",
]
results = asyncio.run(benchmark_batch(proxies, payload_mb=20))
for r in results:
print(r)
Running benchmarks concurrently is important for residential and mobile proxies, where individual IPs vary significantly. A single test on a single IP is not representative — you want to sample 5–10 IPs from a rotating pool and average the results.
Benchmark Reference Table
The numbers below are representative of what you should expect from different proxy types in 2026 on a mid-tier scraping target (a static file endpoint with no bot detection). Your results will vary based on your origin location, the target region, and network conditions.
Proxy Type
Avg Latency (TTFB)
Avg Throughput
Success Rate
Notes
Residential rotating
0.4–0.9s
3–8 Mbps
95–98%
Varies by IP; some slow outliers
Mobile (4G/LTE)
0.5–1.2s
4–12 Mbps
97–99%
Higher trust, slightly higher latency
ISP (static residential)
0.2–0.5s
8–25 Mbps
99%+
Fastest and most consistent
Datacenter
0.05–0.2s
50–200 Mbps
60–85%*
Fast, but high block rate on guarded targets
*Success rate for datacenter proxies depends heavily on target. On unprotected endpoints they work fine; on platforms with IP reputation checks, block rates are high.
For bandwidth-billed residential proxies, the more meaningful number is success rate combined with bytes-received accuracy. A provider whose IPs fail 10% of requests but still bill you for those attempts costs more than the per-GB rate suggests.
Integrating Speed Tests into Your Proxy Workflow
The practical use of these benchmarks isn't running them once and filing the results. It's making speed testing a routine step in your proxy workflow so you can catch degradation early.
A minimal pre-flight check before any production scraping run:
import requests
import time
def proxy_health_check(proxy_url: str, threshold_ttfb: float = 2.0) -> bool:
"""Returns True if proxy meets latency threshold."""
proxies = {"http": proxy_url, "https": proxy_url}
start = time.perf_counter()
try:
resp = requests.get(
"https://httpbin.org/status/200",
proxies=proxies,
timeout=threshold_ttfb + 1
)
ttfb = time.perf_counter() - start
return resp.status_code == 200 and ttfb <= threshold_ttfb
except Exception:
return False
For bandwidth billing verification at the end of a session, compare your bytes_received total from the script against what your provider's dashboard shows for the same session window. Any consistent gap of more than a few percent is worth raising with support.
Using the NodeMaven Proxy Bandwidth Checker
For a no-code version of the same test, the NodeMaven Proxy Bandwidth Checker runs a payload transfer of your choice (10 MB to 500 MB) through your proxy tunnel and reports HTTP status, elapsed time, bytes received, and expected bandwidth side by side. Credentials are used only for the duration of the test and never stored.
The tool works with any HTTP or SOCKS5 proxy, not just NodeMaven proxies — which makes it useful as a standalone verification step in any proxy workflow. If your provider reports 15% more bandwidth consumed than the checker measures on the same payload size, that's concrete evidence of billing inflation worth investigating.
Run it after initial setup to baseline your proxy's performance, and again any time your pipeline's success rate or throughput degrades unexpectedly. Having a reference measurement from a known-good state makes it much easier to diagnose whether a slowdown is coming from the proxy, the target, or your own infrastructure.
What Good Numbers Look Like
For scraping and automation workflows, here's a practical reference:
A residential proxy with TTFB under 1 second and throughput above 3 Mbps is healthy for most tasks. Anything consistently above 2 seconds TTFB suggests the IP is either geographically far from your target, congested, or of lower quality. Throughput below 1 Mbps on a residential proxy usually means you're hitting an IP that's throttled or being rate-limited by the target.
For mobile proxies on high-trust platforms like Instagram or TikTok, the TTFB metric matters less than the success rate. An IP that takes 1.1 seconds to respond but succeeds 99% of the time is more valuable than one that responds in 0.4 seconds but triggers a checkpoint every third request.
ISP proxies should consistently hit TTFB under 500ms. If they're performing like residential rotating proxies, something is wrong with the IP assignment or the routing.
Run these tests from the same origin server or VPS that your actual scraping jobs run from. Testing from your laptop in one country against proxies routed to another will give you numbers that don't reflect production performance.
For further actions, you may consider blocking this person and/or reporting abuse
