v1.10.90-0e025b8
Skip to main content
BenchmarksTutorial

How to Test Proxy Speed and Reliability: DIY Benchmark Guide

9 min read

By Hex Proxies Engineering Team

How to Test Proxy Speed and Reliability: DIY Benchmark Guide

Last updated: April 2026 | By Hex Proxies Team

TL;DR: Proxy provider marketing claims are meaningless without independent verification. This guide provides complete, runnable benchmark scripts to test proxy speed, success rates, and reliability against real-world targets. Test any provider -- including Hex Proxies ($1.70/GB residential, $0.83/IP ISP) -- with standardized methodology before committing.

Every proxy provider claims fast speeds, high uptime, and excellent success rates. Very few provide the data to back those claims, and the benchmarks they do publish are run under conditions designed to produce favorable results. The only reliable way to evaluate a proxy provider is to run your own benchmarks against your actual target sites.

This guide provides a complete DIY benchmark framework: what to measure, how to measure it, and how to interpret results. All code examples are production-ready and designed to produce statistically meaningful results.

What to Measure

A comprehensive proxy benchmark covers five metrics:

MetricWhat It Tells YouGood ThresholdWarning Threshold
Latency (median)Typical response time<300ms>500ms
Latency (P95)Worst-case response time<1000ms>2000ms
Success ratePercentage of HTTP 200 responses>90%<80%
ThroughputRequests per second sustainedDepends on plan<50% of expected
IP diversityUnique IPs per 100 requests>90 (rotating)<70 (rotating)

The Benchmark Script

Core Benchmark Framework

#!/usr/bin/env python3
"""Proxy Benchmark Framework

Usage:
    python proxy_benchmark.py \
        --proxy-url http://user:pass@gate.hexproxies.com:8080 \
        --requests 200 \
        --concurrency 10

Requirements:
    pip install aiohttp
"""

import aiohttp
import asyncio
import argparse
import json
import statistics
import time
from dataclasses import dataclass, asdict
from typing import List, Optional
from datetime import datetime, timezone


# Target sites for benchmarking (mix of protection levels)
TARGET_SITES = [
    {"url": "https://httpbin.org/ip", "name": "httpbin (unprotected)"},
    {"url": "https://www.example.com", "name": "example.com (unprotected)"},
    {"url": "https://www.amazon.com", "name": "Amazon (moderate)"},
    {"url": "https://www.google.com/search?q=proxy+test", "name": "Google Search (moderate)"},
    {"url": "https://www.cloudflare.com", "name": "Cloudflare (protected)"},
]


@dataclass(frozen=True)
class RequestResult:
    """Immutable result of a single proxy request."""
    target: str
    status: int
    latency_ms: float
    proxy_ip: str
    content_length: int
    error: Optional[str]
    timestamp: str


@dataclass(frozen=True)
class BenchmarkSummary:
    """Immutable benchmark summary statistics."""
    total_requests: int
    successful_requests: int
    failed_requests: int
    success_rate: float
    latency_median_ms: float
    latency_p95_ms: float
    latency_p99_ms: float
    latency_min_ms: float
    latency_max_ms: float
    unique_ips: int
    total_duration_s: float
    requests_per_second: float


async def single_request(
    session: aiohttp.ClientSession,
    url: str,
    proxy_url: str,
    target_name: str
) -> RequestResult:
    """Execute a single request through the proxy and measure performance."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/124.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept-Encoding": "gzip, deflate, br"
    }
    
    start = time.monotonic()
    proxy_ip = "unknown"
    
    try:
        async with session.get(
            url,
            proxy=proxy_url,
            headers=headers,
            timeout=aiohttp.ClientTimeout(total=30),
            ssl=False  # Skip SSL verification for benchmark
        ) as response:
            content = await response.read()
            latency = (time.monotonic() - start) * 1000
            
            # Try to extract proxy IP from httpbin response
            if "httpbin" in url:
                try:
                    data = json.loads(content)
                    proxy_ip = data.get("origin", "unknown")
                except (json.JSONDecodeError, UnicodeDecodeError):
                    pass
            
            return RequestResult(
                target=target_name,
                status=response.status,
                latency_ms=round(latency, 2),
                proxy_ip=proxy_ip,
                content_length=len(content),
                error=None,
                timestamp=datetime.now(timezone.utc).isoformat()
            )
    except Exception as e:
        latency = (time.monotonic() - start) * 1000
        return RequestResult(
            target=target_name,
            status=0,
            latency_ms=round(latency, 2),
            proxy_ip="unknown",
            content_length=0,
            error=str(e),
            timestamp=datetime.now(timezone.utc).isoformat()
        )


async def run_benchmark(
    proxy_url: str,
    num_requests: int = 200,
    concurrency: int = 10
) -> tuple:
    """Run the full benchmark suite. Returns (results, summary)."""
    
    semaphore = asyncio.Semaphore(concurrency)
    results: List[RequestResult] = []
    
    async def bounded_request(session, url, name):
        async with semaphore:
            result = await single_request(session, url, proxy_url, name)
            return result
    
    connector = aiohttp.TCPConnector(limit=concurrency * 2, ssl=False)
    async with aiohttp.ClientSession(connector=connector) as session:
        # Build request list: distribute evenly across targets
        tasks = []
        for i in range(num_requests):
            target = TARGET_SITES[i % len(TARGET_SITES)]
            tasks.append(
                bounded_request(session, target["url"], target["name"])
            )
        
        start_time = time.monotonic()
        results = list(await asyncio.gather(*tasks))
        total_duration = time.monotonic() - start_time
    
    # Calculate summary statistics
    successful = [r for r in results if r.status == 200]
    latencies = [r.latency_ms for r in successful]
    unique_ips = len(set(
        r.proxy_ip for r in results
        if r.proxy_ip != "unknown"
    ))
    
    if latencies:
        sorted_latencies = sorted(latencies)
        p95_idx = int(len(sorted_latencies) * 0.95)
        p99_idx = int(len(sorted_latencies) * 0.99)
        
        summary = BenchmarkSummary(
            total_requests=len(results),
            successful_requests=len(successful),
            failed_requests=len(results) - len(successful),
            success_rate=round(len(successful) / len(results) * 100, 2),
            latency_median_ms=round(statistics.median(latencies), 2),
            latency_p95_ms=round(sorted_latencies[p95_idx], 2),
            latency_p99_ms=round(sorted_latencies[p99_idx], 2),
            latency_min_ms=round(min(latencies), 2),
            latency_max_ms=round(max(latencies), 2),
            unique_ips=unique_ips,
            total_duration_s=round(total_duration, 2),
            requests_per_second=round(len(results) / total_duration, 2)
        )
    else:
        summary = BenchmarkSummary(
            total_requests=len(results),
            successful_requests=0,
            failed_requests=len(results),
            success_rate=0.0,
            latency_median_ms=0.0,
            latency_p95_ms=0.0,
            latency_p99_ms=0.0,
            latency_min_ms=0.0,
            latency_max_ms=0.0,
            unique_ips=0,
            total_duration_s=round(total_duration, 2),
            requests_per_second=0.0
        )
    
    return tuple(results), summary


def print_report(summary: BenchmarkSummary, results: tuple):
    """Print a formatted benchmark report."""
    print("\n" + "=" * 60)
    print("PROXY BENCHMARK REPORT")
    print("=" * 60)
    print(f"\nTotal Requests:     {summary.total_requests}")
    print(f"Successful:         {summary.successful_requests}")
    print(f"Failed:             {summary.failed_requests}")
    print(f"Success Rate:       {summary.success_rate}%")
    print(f"\nLatency (median):   {summary.latency_median_ms}ms")
    print(f"Latency (P95):      {summary.latency_p95_ms}ms")
    print(f"Latency (P99):      {summary.latency_p99_ms}ms")
    print(f"Latency (min):      {summary.latency_min_ms}ms")
    print(f"Latency (max):      {summary.latency_max_ms}ms")
    print(f"\nUnique IPs:         {summary.unique_ips}")
    print(f"Duration:           {summary.total_duration_s}s")
    print(f"Throughput:         {summary.requests_per_second} req/s")
    
    # Per-target breakdown
    print("\n" + "-" * 60)
    print("PER-TARGET BREAKDOWN")
    print("-" * 60)
    
    targets = set(r.target for r in results)
    for target in sorted(targets):
        target_results = [r for r in results if r.target == target]
        target_success = [r for r in target_results if r.status == 200]
        target_latencies = [r.latency_ms for r in target_success]
        
        rate = (
            len(target_success) / len(target_results) * 100
            if target_results else 0
        )
        median = (
            statistics.median(target_latencies)
            if target_latencies else 0
        )
        
        print(f"\n  {target}")
        print(f"    Success: {rate:.1f}% ({len(target_success)}/{len(target_results)})")
        print(f"    Median latency: {median:.0f}ms")
    
    print("\n" + "=" * 60)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Proxy Benchmark Tool")
    parser.add_argument(
        "--proxy-url",
        required=True,
        help="Proxy URL (http://user:pass@host:port)"
    )
    parser.add_argument(
        "--requests",
        type=int,
        default=200,
        help="Total number of requests (default: 200)"
    )
    parser.add_argument(
        "--concurrency",
        type=int,
        default=10,
        help="Concurrent requests (default: 10)"
    )
    parser.add_argument(
        "--output",
        help="Save results to JSON file"
    )
    
    args = parser.parse_args()
    
    print(f"Starting benchmark: {args.requests} requests, "
          f"{args.concurrency} concurrent")
    print(f"Proxy: {args.proxy_url.split('@')[1] if '@' in args.proxy_url else args.proxy_url}")
    
    results, summary = asyncio.run(
        run_benchmark(args.proxy_url, args.requests, args.concurrency)
    )
    
    print_report(summary, results)
    
    if args.output:
        output_data = {
            "summary": asdict(summary),
            "results": [asdict(r) for r in results]
        }
        with open(args.output, "w") as f:
            json.dump(output_data, f, indent=2)
        print(f"\nResults saved to {args.output}")

Running the Benchmark

Basic Usage

# Install dependency
pip install aiohttp

# Test Hex Proxies residential
python proxy_benchmark.py \
    --proxy-url "http://your_user:your_pass@gate.hexproxies.com:8080" \
    --requests 200 \
    --concurrency 10 \
    --output results_hex_residential.json

# Test another provider for comparison
python proxy_benchmark.py \
    --proxy-url "http://user:pass@other-provider.com:8080" \
    --requests 200 \
    --concurrency 10 \
    --output results_other.json

Sample Output

============================================================
PROXY BENCHMARK REPORT
============================================================

Total Requests:     200
Successful:         186
Failed:             14
Success Rate:       93.0%

Latency (median):   247.31ms
Latency (P95):      892.55ms
Latency (P99):      1432.18ms
Latency (min):      89.22ms
Latency (max):      2891.44ms

Unique IPs:         38
Duration:           62.41s
Throughput:         3.21 req/s

------------------------------------------------------------
PER-TARGET BREAKDOWN
------------------------------------------------------------

  Amazon (moderate)
    Success: 88.0% (35/40)
    Median latency: 312ms

  Cloudflare (protected)
    Success: 82.5% (33/40)
    Median latency: 445ms

  Google Search (moderate)
    Success: 92.5% (37/40)
    Median latency: 289ms

  example.com (unprotected)
    Success: 100.0% (40/40)
    Median latency: 142ms

  httpbin (unprotected)
    Success: 100.0% (40/40)
    Median latency: 167ms

============================================================

Interpreting Results

Success Rate Analysis

Success RateAssessmentAction
95-100%ExcellentProvider works well for this target
85-95%GoodAcceptable with retry logic
70-85%MarginalConsider different proxy type or provider
<70%PoorNot suitable for this target

Latency Analysis

Focus on median and P95, not average. Averages are skewed by outliers. The median tells you what most requests experience; the P95 tells you what your slowest requests look like:

  • Median <300ms: Good for most use cases
  • Median 300-500ms: Acceptable for scraping, slow for interactive use
  • Median >500ms: Only suitable for background data collection
  • P95 >3x median: Inconsistent proxy performance, investigate further

IP Diversity Analysis

For rotating residential proxies, check how many unique IPs appear in your benchmark:

  • Unique IPs = total requests: True per-request rotation (ideal)
  • Unique IPs > 80% of requests: Good rotation with some reuse
  • Unique IPs < 50% of requests: Pool is small or rotation is sticky -- investigate

Advanced Benchmark: Sustained Load Test

async def sustained_load_test(
    proxy_url: str,
    duration_minutes: int = 30,
    requests_per_minute: int = 60
):
    """Run a sustained load test over a longer period.
    
    Measures how proxy performance degrades (or doesn't)
    over time. Useful for detecting IP pool exhaustion
    or rate limiting by the proxy provider.
    """
    interval = 60 / requests_per_minute
    end_time = time.monotonic() + (duration_minutes * 60)
    
    minute_results = {}  # minute_number -> list of results
    current_minute = 0
    
    connector = aiohttp.TCPConnector(limit=20, ssl=False)
    async with aiohttp.ClientSession(connector=connector) as session:
        while time.monotonic() < end_time:
            minute = int((time.monotonic() - (end_time - duration_minutes * 60)) / 60)
            if minute not in minute_results:
                minute_results[minute] = []
            
            target = TARGET_SITES[len(minute_results[minute]) % len(TARGET_SITES)]
            result = await single_request(
                session, target["url"], proxy_url, target["name"]
            )
            minute_results[minute].append(result)
            
            await asyncio.sleep(interval)
    
    # Report per-minute statistics
    print("\nSUSTAINED LOAD TEST RESULTS")
    print("-" * 60)
    print(f"{'Minute':<8} {'Requests':<10} {'Success %':<12} {'Median ms':<12}")
    print("-" * 60)
    
    for minute in sorted(minute_results.keys()):
        results = minute_results[minute]
        successful = [r for r in results if r.status == 200]
        latencies = [r.latency_ms for r in successful]
        rate = len(successful) / len(results) * 100 if results else 0
        median = statistics.median(latencies) if latencies else 0
        
        print(f"{minute:<8} {len(results):<10} {rate:<12.1f} {median:<12.0f}")
    
    return minute_results

Comparing Providers Side by Side

Run the benchmark against multiple providers and compare results. Key comparison points:

# Run against 3 providers
python proxy_benchmark.py --proxy-url "http://user:pass@provider1.com:8080" \
    --requests 500 --output provider1.json

python proxy_benchmark.py --proxy-url "http://user:pass@gate.hexproxies.com:8080" \
    --requests 500 --output hexproxies.json

python proxy_benchmark.py --proxy-url "http://user:pass@provider3.com:8080" \
    --requests 500 --output provider3.json

# Compare results
python compare_benchmarks.py provider1.json hexproxies.json provider3.json

Comparison Script

#!/usr/bin/env python3
"""Compare benchmark results from multiple providers."""

import json
import sys


def load_summary(filepath):
    """Load benchmark summary from JSON file."""
    with open(filepath) as f:
        data = json.load(f)
    return data["summary"]


def compare(files):
    """Print side-by-side comparison table."""
    summaries = {}
    for f in files:
        name = f.replace(".json", "")
        summaries[name] = load_summary(f)
    
    # Header
    names = list(summaries.keys())
    header = f"{'Metric':<25}" + "".join(f"{n:<20}" for n in names)
    print(header)
    print("-" * len(header))
    
    # Metrics
    metrics = [
        ("Success Rate", "success_rate", "%"),
        ("Median Latency", "latency_median_ms", "ms"),
        ("P95 Latency", "latency_p95_ms", "ms"),
        ("Unique IPs", "unique_ips", ""),
        ("Throughput", "requests_per_second", "req/s"),
    ]
    
    for label, key, unit in metrics:
        row = f"{label:<25}"
        for name in names:
            val = summaries[name].get(key, "N/A")
            row += f"{val}{unit:<{20 - len(str(val)) - len(unit)}}"
        print(row)


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python compare_benchmarks.py file1.json file2.json [file3.json ...]")
        sys.exit(1)
    compare(sys.argv[1:])

Common Benchmarking Mistakes

  1. Testing too few requests. 10 requests is not statistically significant. Use at least 200 requests per provider for reliable results. For production decisions, use 500-1,000.
  2. Testing only unprotected targets. Every proxy works against httpbin. Test against your actual target sites and similar protection levels.
  3. Ignoring P95 latency. A provider with great median latency but terrible P95 means 1 in 20 requests is unacceptably slow. This matters for user-facing applications.
  4. Not testing during peak hours. Proxy performance can vary by time of day due to shared infrastructure load. Run benchmarks during your actual usage hours.
  5. Comparing different proxy types. Comparing residential rotating proxies to ISP static proxies is meaningless -- they serve different purposes. Compare like for like.

Benchmark Checklist

StepActionWhy
1Define target sites matching your use caseResults are only meaningful for your targets
2Run 200+ requests per providerStatistical significance
3Test at multiple times of dayDetect time-dependent performance variations
4Compare median AND P95 latencyMedian alone hides tail latency issues
5Check IP diversity (for rotating proxies)Verify actual pool size vs marketing claims
6Run sustained load test (30+ min)Detect performance degradation over time
7Calculate true cost per successful requestFailed requests waste budget

Frequently Asked Questions

How many requests do I need for a reliable benchmark?

Minimum 200 requests per provider for basic comparison. For production decisions with confidence, use 500-1,000 requests spread across multiple time windows. The benchmark script above defaults to 200 for quick comparisons.

Should I test against my actual target sites?

Yes, absolutely. Protection levels vary dramatically between sites. A proxy that achieves 98% success on Google might achieve 75% on Amazon. Add your target URLs to the TARGET_SITES list in the benchmark script for the most relevant results.

How often should I re-benchmark proxy providers?

Quarterly for ongoing provider evaluation. Additionally, re-benchmark when: (1) you notice success rate drops in production, (2) your target sites update their anti-bot protection, or (3) your provider announces infrastructure changes.

What if my success rates are low with all providers?

Low success rates across all providers indicate the target's protection requires more than just proxy IPs. You may need: browser automation (Playwright/Puppeteer), TLS fingerprint impersonation, behavioral simulation, or a dedicated scraping API for those specific targets. See our guide on anti-bot detection in 2026 for details.

Can I benchmark Hex Proxies before purchasing?

Yes. Hex Proxies plans have no minimum commitment. Purchase the smallest available plan, run this benchmark against your targets, and evaluate the results before scaling up. The benchmark framework above works with any provider that supports HTTP proxy authentication.


Independent benchmarking is the only way to make informed proxy provider decisions. The scripts in this guide give you a standardized, reproducible framework for evaluating any provider. Test Hex Proxies residential ($1.70/GB) and ISP ($0.83/IP) against your actual targets using the benchmark above, and compare the results against any competing provider. View plans and start benchmarking.