The idea
A lightweight local reverse proxy that sits between your application and any third-party HTTP API, enforcing configurable rate limits on outbound requests before they leave your machine. You point your app at localhost:8080/github instead of api.github.com, and the proxy queues or rejects requests that exceed the configured limit — preventing 429s from reaching your process in the first place.
Config is a single YAML file: list your routes, their target base URLs, and the rate you want enforced. The proxy handles the rest.
Why build this
Hitting third-party rate limits in staging or CI is a solved problem in theory but a persistent pain in practice. Most teams discover limits the hard way — a batch job runs too fast, a test suite hammers an endpoint, or a misconfigured retry loop burns through a daily quota in minutes. Client-side rate-limiting libraries exist per language but require instrumenting every call site and keeping limits in sync across services.
A proxy handles all outbound calls from any process, any language, zero code changes.
The rise of AI APIs with strict per-minute token and request tiers sharpens this problem further. A single runaway batch job can exhaust an OpenAI or Anthropic key in seconds and break everything else sharing it. A proxy gives you a single enforcement point regardless of how many services share a credential.
Stack sketch
- Runtime: Go — single static binary, ships as a release artifact with no runtime dependencies
- Config: YAML file listing named routes: target URL, rate limit (e.g.,
60/minute), and burst allowance - Rate limiter: token bucket via
golang.org/x/time/rate; sliding window per route - Queuing: in-process channel per route; requests wait up to a configurable timeout before the proxy returns a synthetic 429 with a
Retry-Afterheader - Logging: structured JSON to stdout — method, route, status, queue wait time, whether throttled
- Stats endpoint:
GET /_proxy/statsreturns a JSON snapshot of per-route counters (requests, throttled, queued, queue depth) - TLS: HTTP-only by default (app talks HTTPS directly to the real host); optional MITM mode with a generated local root cert for full inspection
Scope for v1
- YAML config with named routes and per-route token-bucket limits
- In-process request queue with max-wait timeout
- Structured stdout logging
/_proxy/statsJSON endpoint--dry-runflag that logs what would have been throttled without actually blocking- Single static binary for Linux, macOS, and Windows via GitHub Actions release
- Out of scope: response caching, credential injection, retry logic, a UI, persistent metrics storage
Where it could go
The most natural first extension after v1 is a minimal HTML dashboard at /_proxy/ that plots request volume and throttle events per route over the last hour using a small in-memory ring buffer. This turns the tool from a black-box enforcer into something you can use to tune limits — you see your real traffic shape before committing to a number.
A second direction is replay mode: the proxy records real responses to disk in a structured format, then serves them back in tests without hitting the real API. The transport layer is already in place; recording adds a small write step. Teams already use tools like VCR and WireMock for this, but a proxy-level solution requires no test framework integration.
Watch out for
HTTPS interception requires installing a self-generated root certificate into the OS trust store, which corporate security tooling often flags or blocks — defaulting to HTTP-only proxy mode sidesteps this entirely and covers most development and CI use cases. Token-bucket and sliding-window rate limiting behave differently at burst edges; document which semantics the proxy uses so users don't miscalibrate limits against an API that counts differently.