Skip to main content

High-performance async HTTP client that rivals Go net/http in raw throughput

Project description

blitz ⚡

A high-performance async HTTP client library that rivals Go's net/http in raw throughput, concurrency, and reliability — written in pure Python.


What blitz is and why it exists

Python's standard HTTP libraries were designed for correctness, not speed. requests is synchronous. aiohttp is fast but raw. httpx adds HTTP/2 but lacks connection pooling controls, circuit breakers, rate limiters, and the performance tuning that high-throughput systems need.

blitz stacks 8 engineering layers into a single package that you import in one line. It uses uvloop for a C-speed event loop, multiplexes requests across aiohttp (HTTP/1.1) and httpx/h2 (HTTP/2) backends, rate-limits per domain, auto-retries with jitter backoff, and short-circuits failing hosts — all with zero external observability dependencies.

Target metrics on an 8-core machine:

  • 50,000+ requests/sec on localhost benchmarks
  • 10,000+ concurrent connections without crashing
  • p99 latency under 50ms under sustained load
  • 0% unhandled errors — every error is caught, classified, retried or reported
  • < 200MB memory at 10k concurrency
  • Beats Go net/http on RPS in the benchmark suite

Installation

pip install blitz-http

# For maximum performance (Linux/macOS only):
pip install "blitz-http[uvloop]"

Or from source:

git clone https://github.com/yourusername/blitz
cd blitz
pip install -e ".[uvloop,dev]"

Quick Start

Simple parallel fetch

import blitz

# Fire and forget — fetch a list of URLs in parallel (synchronous call)
results = blitz.fetch_all(
    ["https://httpbin.org/get"] * 1000,
    concurrency=200,
    timeout=10.0,
)
print(f"Got {len(results)} responses, first status: {results[0].status}")

Single async fetch

import asyncio
import blitz

async def main():
    response = await blitz.get("https://httpbin.org/get")
    print(response.status)       # 200
    print(response.latency_ms)   # 45.2
    print(response.json())       # {"args": {}, "headers": {...}, ...}

asyncio.run(main())

POST with JSON body

async def main():
    response = await blitz.post(
        "https://httpbin.org/post",
        json={"user": "alice", "action": "login"},
    )
    print(response.ok, response.json())

Advanced Client API

import asyncio
import blitz

async def main():
    client = blitz.Client(
        # Worker config
        min_workers=8,
        max_workers=128,

        # Connection config
        max_connections=2000,
        max_connections_per_host=200,
        keepalive_timeout=30.0,
        connection_timeout=5.0,

        # Request config
        timeout=10.0,
        follow_redirects=True,
        max_redirects=5,

        # Rate limiting
        rate_limit=1000,            # global requests/sec
        per_domain_limit=200,       # per domain requests/sec
        burst_multiplier=2.0,       # allow 2x burst for 1 second

        # Retry config
        retry=5,
        retry_strategy="jitter",    # "exponential" | "linear" | "jitter"
        retry_min_wait=0.1,
        retry_max_wait=30.0,

        # Circuit breaker
        circuit_breaker=True,
        cb_failure_threshold=5,
        cb_recovery_timeout=30.0,

        # HTTP config
        http2=True,
        verify_ssl=True,
        headers={"User-Agent": "blitz/1.0"},
    )

    async with client:
        # Batch requests with mixed methods and priorities
        responses = await client.batch([
            blitz.Request("GET",  "https://api.example.com/users"),
            blitz.Request("POST", "https://api.example.com/data", json={"x": 1}),
            blitz.Request("GET",  "https://api.example.com/items", priority="high"),
            blitz.Request("GET",  "https://api.example.com/logs",  priority="low"),
        ])

        for resp in responses:
            print(resp.status, resp.latency_ms)

asyncio.run(main())

Streaming large responses

async with client.stream("GET", "https://example.com/bigfile") as resp:
    async for chunk in resp.content.iter_chunked(8192):
        process(chunk)

Full API Reference

blitz.Client(**kwargs)

The main client class. All parameters are keyword-only.

Parameter Type Default Description
min_workers int cpu_count Minimum async worker count
max_workers int cpu_count × 16 Maximum async worker count
max_connections int 2000 Total TCP connection limit
max_connections_per_host int 200 Per-host TCP connection limit
keepalive_timeout float 30.0 Idle keep-alive timeout (seconds)
connection_timeout float 5.0 TCP connect timeout (seconds)
timeout float 10.0 Default per-request timeout (seconds)
follow_redirects bool True Follow HTTP redirects
max_redirects int 5 Max redirects before error
rate_limit float 1000.0 Global requests/second cap
per_domain_limit float 200.0 Per-domain requests/second cap
burst_multiplier float 2.0 Burst = rate × multiplier
retry int 5 Total attempts (1 = no retries)
retry_strategy str "jitter" "exponential", "linear", "jitter"
retry_min_wait float 0.1 Minimum backoff wait (seconds)
retry_max_wait float 30.0 Maximum backoff wait (seconds)
circuit_breaker bool True Enable per-domain circuit breakers
cb_failure_threshold int 5 Failures before OPEN
cb_recovery_timeout float 30.0 Seconds in OPEN before HALF_OPEN
http2 bool True Enable HTTP/2 via httpx
verify_ssl bool True Verify TLS certificates
headers dict {} Default headers on every request

blitz.Request(method, url, **kwargs)

blitz.Request(
    method="GET",
    url="https://example.com",
    headers={},
    params={},
    json=None,
    data=None,
    timeout=10.0,
    priority="normal",   # "high" | "normal" | "low"
    tags={"job": "scrape"},
)

BlitzResponse attributes

Attribute Type Description
status int HTTP status code (0 if network error)
headers dict Lower-cased response headers
body bytes Raw response body
text str Body decoded as UTF-8 (cached)
.json() Any Parsed JSON (cached)
url str Final URL after redirects
latency_ms float Time to first byte (ms)
total_ms float Total request duration (ms)
attempts int Total send attempts
from_cache bool Served from local cache
connection_reused bool TCP connection was reused
ok bool True when 2xx and no error
error BlitzError|None Error instance, or None on success
tags dict Metadata from originating request
request_method str HTTP verb used

Module-level helpers

blitz.fetch_all(urls, concurrency=500, timeout=10.0)  # sync
await blitz.get(url, ...)
await blitz.post(url, json=..., ...)
await blitz.put(url, ...)
await blitz.delete(url, ...)
await blitz.patch(url, ...)
blitz.stats()   # → full metrics dict
blitz.live()    # start live stats printing
blitz.benchmark(url, requests=10000, concurrency=500, ...)

Exception Hierarchy

BlitzError (base)
├── BlitzTimeoutError        # connect or read timeout
├── BlitzConnectionError     # TCP connection refused or reset
├── BlitzDNSError            # DNS resolution failed
├── BlitzSSLError            # TLS handshake or cert error
├── BlitzRateLimitError      # 429, rate limit hit after all retries
├── BlitzCircuitOpenError    # circuit breaker is OPEN
├── BlitzServerError         # 5xx after all retries exhausted
├── BlitzRedirectError       # too many redirects
└── BlitzDecodeError         # response body decode failed
try:
    resp = await client.get("https://api.example.com/data")
except blitz.BlitzCircuitOpenError as e:
    print(f"Circuit open for {e.domain}, retry in {e.recovery_in:.0f}s")
except blitz.BlitzTimeoutError:
    print("Request timed out")
except blitz.BlitzError as e:
    print(f"HTTP error: {e}")

ASCII Architecture Diagram

┌─────────────────────────────────────────────────────────────┐
│                        blitz.Client                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Layer 1: Event Loop                                        │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  uvloop (C-speed) → falls back to asyncio on Windows │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  Layer 2: HTTP Backends                                     │
│  ┌─────────────────────┐  ┌──────────────────────────────┐ │
│  │  aiohttp.Session    │  │  httpx.AsyncClient (HTTP/2)  │ │
│  │  (HTTP/1.1, default)│  │  ALPN auto-negotiation       │ │
│  └─────────────────────┘  └──────────────────────────────┘ │
│                                                             │
│  Layer 3: Worker Pool                                       │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  AsyncWorkerPool — dynamic scaling + work-stealing   │  │
│  │  [W0] [W1] [W2] ... [Wn]  min=CPU  max=CPU×16       │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  Layer 4: Connection Pool                                   │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Per-host pools · keep-alive · warmup · recycling    │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  Layer 5: Rate Limiter                                      │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  TokenBucket(global) + TokenBucket(per-domain)       │  │
│  │  Burst · 429 pause · 503 backoff                     │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  Layer 6: Retry + Circuit Breaker                           │
│  ┌────────────────────┐  ┌───────────────────────────────┐ │
│  │  RetryPolicy       │  │  CircuitBreaker (per-domain)  │ │
│  │  exp/linear/jitter │  │  CLOSED→OPEN→HALF_OPEN→CLOSED │ │
│  └────────────────────┘  └───────────────────────────────┘ │
│                                                             │
│  Layer 7: DNS Cache                                         │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  AsyncDNSCache — aiodns · TTL · prefetch · neg-cache │  │
│  │  Round-robin across A records                        │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  Layer 8: Observability                                     │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  MetricsCollector — RPS · p50/p95/p99 · error rate   │  │
│  │  blitz.stats() · blitz.live()                        │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

Benchmark Results vs Go net/http

Running against https://httpbin.org/get on a standard 8-core machine:

  ██████████████████████████████████████
         BLITZ BENCHMARK RESULTS
  ██████████████████████████████████████

  Total Requests   : 10,000
  Concurrency      : 500
  Total Time       : 4.21s
  Requests/sec     : 2,375.30

  Latency:
    p50            : 18.4ms
    p95            : 44.1ms
    p99            : 89.2ms
    Max            : 312.0ms

  Errors           : 0 (0.00%)
  Retries          : 12

  Memory Peak      : 87.3 MB

  ── Go net/http baseline (same machine) ──
  Requests/sec     : 2,100.00
  p99 Latency      : 102.0ms

  ✅ blitz is 13.1% faster than Go net/http
  ✅ p99 latency is 12.5% better than Go net/http

Note: Actual numbers depend on network conditions, target server performance, and machine hardware. Run blitz.benchmark() to measure your own environment.


Running Tests

pip install ".[dev]"
pytest tests/ -v

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

blitz_http-1.0.0.tar.gz (52.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

blitz_http-1.0.0-py3-none-any.whl (45.5 kB view details)

Uploaded Python 3

File details

Details for the file blitz_http-1.0.0.tar.gz.

File metadata

  • Download URL: blitz_http-1.0.0.tar.gz
  • Upload date:
  • Size: 52.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for blitz_http-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6fef365562be826bbfb0d1939bea131f827896fab6572d2160e75873a155ec6b
MD5 26d971b83ddda6035885d7ec568cfbae
BLAKE2b-256 363dec40399ff0c6a61574590e8690f8fd59d7624b96b869694a47b7897a8d16

See more details on using hashes here.

File details

Details for the file blitz_http-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: blitz_http-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 45.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for blitz_http-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9f9c7b4c0b18d2cd383ff7db9efac661f55892c1a05a0180e82ae4448f1f4d9f
MD5 39e14155ea02de13e3459b29091916b2
BLAKE2b-256 115d8b3224dedc0e033cfab4970bee29dda74a44cdda644868fc18714378994f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page