Proxies for Realistic API Load Testing
Load testing from a single IP produces misleading results. Your application's rate limiter, CDN, and WAF all treat single-IP traffic differently than distributed traffic from real users. Proxy infrastructure creates realistic test conditions by distributing load across many source IPs.
Why Single-IP Load Tests Fail
When you run `hey` or `wrk` from one server, several things happen that do not reflect production reality:
- **Rate limiters activate**: Your app blocks the test IP after X requests, producing artificial 429 errors.
- **CDN caching skews results**: CDNs cache responses per edge node. A single-IP test hits one edge node, inflating cache hit rates.
- **WAF interference**: Web application firewalls flag rapid requests from one IP as an attack, returning 403s.
- **Connection pooling artifacts**: HTTP/2 multiplexing over a single connection behaves differently than many separate connections.
Distributed Load Test Architecture
Load Test Runner
├── Worker 1 → Proxy IP A → Your API
├── Worker 2 → Proxy IP B → Your API
├── Worker 3 → Proxy IP C → Your API
└── Worker N → Proxy IP N → Your APIPython Load Test with Proxy Distribution
import asyncio
import aiohttp
import time
from dataclasses import dataclass@dataclass(frozen=True) class LoadTestResult: url: str status: int latency_ms: float proxy_session: str
@dataclass(frozen=True) class LoadTestSummary: total_requests: int successful: int failed: int avg_latency_ms: float p95_latency_ms: float p99_latency_ms: float requests_per_second: float
class ProxiedLoadTester: def __init__(self, username: str, password: str, concurrency: int = 50): self._username = username self._password = password self._concurrency = concurrency
def _get_proxy(self, worker_id: int) -> str: session = f"loadtest-{worker_id}" return f"http://{self._username}-session-{session}:{self._password}@gate.hexproxies.com:8080"
async def run(self, url: str, total_requests: int) -> LoadTestSummary: semaphore = asyncio.Semaphore(self._concurrency) results: list[LoadTestResult] = [] start_time = time.monotonic()
async with aiohttp.ClientSession() as session: tasks = [] for i in range(total_requests): proxy = self._get_proxy(i % self._concurrency) tasks.append(self._send_request(session, url, proxy, f"w{i}", semaphore)) results = await asyncio.gather(*tasks, return_exceptions=True) valid_results = [r for r in results if isinstance(r, LoadTestResult)]
elapsed = time.monotonic() - start_time latencies = sorted([r.latency_ms for r in valid_results]) successful = sum(1 for r in valid_results if 200 <= r.status < 400)
return LoadTestSummary( total_requests=total_requests, successful=successful, failed=total_requests - successful, avg_latency_ms=sum(latencies) / max(len(latencies), 1), p95_latency_ms=latencies[int(len(latencies) * 0.95)] if latencies else 0, p99_latency_ms=latencies[int(len(latencies) * 0.99)] if latencies else 0, requests_per_second=total_requests / max(elapsed, 0.001), )
async def _send_request( self, session: aiohttp.ClientSession, url: str, proxy: str, session_id: str, semaphore: asyncio.Semaphore ) -> LoadTestResult: async with semaphore: start = time.monotonic() try: async with session.get(url, proxy=proxy, timeout=aiohttp.ClientTimeout(total=30)) as resp: await resp.read() latency = (time.monotonic() - start) * 1000 return LoadTestResult(url=url, status=resp.status, latency_ms=latency, proxy_session=session_id) except Exception: latency = (time.monotonic() - start) * 1000 return LoadTestResult(url=url, status=0, latency_ms=latency, proxy_session=session_id) ```
Usage Example
async def main():
tester = ProxiedLoadTester(
username="YOUR_USER",
password="YOUR_PASS",
concurrency=100,
)
summary = tester.run("https://api.yourapp.com/v1/health", total_requests=10000)
result = await summary
print(f"RPS: {result.requests_per_second:.1f}")
print(f"P95 Latency: {result.p95_latency_ms:.1f}ms")asyncio.run(main()) ```
Interpreting Results
Compare proxied load test results against single-IP tests. The differences reveal how much your rate limiting, CDN, and WAF affect real user experience. Proxied tests give you the true performance profile your users experience.
With Hex Proxies' ISP infrastructure delivering sub-50ms proxy latency, the overhead added by the proxy layer is minimal — typically 10-30ms — making your load test results accurate representations of production conditions.