Tollgate is a self-hosted gateway for Claude Code and any agent that uses the Anthropic or OpenAI API.
It refuses requests that would go over a budget, fails over when a provider goes down, and prices every request.
Two environment variables. No code changes. One Go binary.
Quick start · How it works · See it work · Features · Documentation · FAQ
Coding agents do not spend money like chat apps. One task can send hundreds of large requests and run for an hour while nobody watches the meter. One bad minute at the provider can stop the task.
| Without Tollgate | With Tollgate |
|---|---|
| An agent stuck in a loop spends the month's budget overnight. | Each session and each month has a hard budget. Tollgate reserves the estimated cost before it sends a request, and refuses with HTTP 402 when the money is gone. |
The provider returns 529 Overloaded, and an hour-long task stops. |
Tollgate fails over to the next target, such as a second account or Claude on Vertex AI, before the first byte reaches the agent. The agent gets a normal response. |
| The invoice goes up, and nobody knows which task spent the money. | Tollgate prices every request and tags it with its tenant, key, and session. The dashboard and tollgate report show where the money went. |
| Ten agents share one provider key, with no limits. | Each person or agent gets its own Tollgate key, with its own models, rate limits, and budget. The provider key stays on the gateway. |
Prerequisites: Git, GNU Make, Node.js, and the Go version in go.mod.
git clone https://github.com/ykakade/tollgate.git && cd tollgate
make dashboard-demoOpen http://127.0.0.1:9090. Tollgate runs with fake providers, 7 days of synthetic history, and live agent-like sessions. One fake provider always fails, so you can watch failover and circuit breakers at work. Stop the demo with Ctrl-C.
-
Install Tollgate:
go install github.com/ykakade/tollgate/cmd/tollgate@latest
-
Download the Claude Code example configuration, and make a key:
curl -fsSLO https://raw.githubusercontent.com/ykakade/tollgate/main/examples/claude-code/tollgate.yaml tollgate keys create -id laptop
In
tollgate.yaml, remove[]afterkeys:and add the two lines that the command shows. Keep the key that starts withtg_. -
Start the gateway with your Anthropic API key:
export ANTHROPIC_API_KEY='YOUR_ANTHROPIC_API_KEY' tollgate serve -config tollgate.yaml
-
In a second terminal, start Claude Code with the Tollgate address, the Tollgate key, and a session name:
export ANTHROPIC_BASE_URL=http://127.0.0.1:8080 export ANTHROPIC_AUTH_TOKEN='YOUR_TOLLGATE_KEY' export ANTHROPIC_CUSTOM_HEADERS='x-tollgate-session: my-task' claude
Result: Claude Code works as usual. The task my-task has a $5 session budget, the month has a $50 budget, and each request writes its cost to data/usage.jsonl. Open http://127.0.0.1:9090 to see the spend.
Other ways to install
- Release binaries: Linux archives for amd64 and arm64 are on the releases page. Each release has checksums, a keyless cosign signature, and GitHub build provenance. The installation guide shows how to verify them.
- From source: clone the repository and run
make build. The binaries are inbin/. - Full demo stack: with Docker Compose,
make demostarts Tollgate with fake providers, a load generator, Prometheus, Grafana, and Jaeger. See observability.
Note
Tollgate is alpha software (v0.1.0). The configuration format can change before v1.0. Read the budget tolerances before you depend on a budget.
Clients keep their native API and change only the base URL and the key. For each request, Tollgate:
- Authenticates the Tollgate key and finds its tenant, models, and limits.
- Estimates the input tokens and reads
max_tokensfrom the request. - Reserves the rate limit and the estimated cost. If the tenant is over a limit, the request stops here with 429 or 402.
- Checks the opt-in response cache. A hit costs no provider tokens.
- Forwards the request to the first healthy target. If that target fails before the first response byte, Tollgate tries the next one.
- Settles the real cost from the reported usage, and writes one usage event.
Streams pass through byte for byte, unless redaction or a budget cutoff changes them. One process keeps its state in memory. Several copies share limits and budgets through Redis.
The 81-second tour. Watch it in 1080p, or read how it is made.
This is real output from a local run with two fake providers. The primary always returns 529, and the session budget is $0.01.
The primary fails, so Tollgate sends the request to the backup. The response headers tell you what happened:
HTTP/1.1 200 OK
X-Tollgate-Attempts: 2
X-Tollgate-Cache: bypass
X-Tollgate-Model: claude-sonnet-5-5
X-Tollgate-Overhead-Ms: 0.068
X-Tollgate-Provider: backup
X-Tollgate-Session-Remaining-Usd: 0.00794469 requests later, the session budget is spent. Tollgate refuses request 70 with 402 Payment Required, in the error format of the caller's API, and the provider never sees it:
{
"type": "error",
"error": {
"type": "budget_exceeded",
"message": "The session \"fix-flaky-test\" does not have sufficient budget for this request. The session budget is 0.01 USD, and the spend is 0.008004 USD. Start a new session, or increase the limit_usd of the session budget."
}
}The usage file has the whole story:
$ tollgate report -file data/usage.jsonl -group-by session
SESSION REQUESTS FALLBACKS SWITCHES INPUT CACHE READ CACHE WRITE OUTPUT COST USD CACHE LOSS USD
personal/fix-flaky-test 70 1 0 1932 0 0 414 0.008004 0.00The dashboard is built into the binary. Select a tenant and a time range, then open a session to see its budget and its requests.
Show agent sessions and provider health
The screenshots use synthetic data from make dashboard-demo. See the admin API and dashboard guide.
| Capability | What you get |
|---|---|
| Budgets that hold | Month and session budgets for each tenant. A hard budget reserves the estimated cost first, refuses with HTTP 402, and cuts off a stream at the limit. A soft budget only warns. |
| Failover before the first byte | Up to 10 ordered targets for each route, bounded retries, a retry budget, circuit breakers, and separate connect, first-token, idle, and total timeouts. |
| Agent sessions | Session IDs from a header or from a hash of the prompt prefix. Sticky routing keeps a session on the target with the warm prompt cache. Projections show the requests and minutes left. |
| Traffic controls | Requests and tokens per minute for each tenant, with ratelimit-* and retry-after headers. Several copies share the counters through Redis. |
| Privacy | Opt-in redaction of emails, card numbers, IP addresses, US SSNs, and your own patterns, with placeholders restored in the response. Logs and usage events hold no prompt text. |
| Exact response cache | Opt-in for each route, and scoped by tenant. A hit costs no provider tokens. |
| Native APIs | Anthropic /v1/messages and OpenAI /v1/chat/completions, with and without streaming. Targets: Anthropic, OpenAI, Claude on Vertex AI, and Ollama. |
| Visibility | An embedded dashboard, a JSONL usage file, tollgate report, Prometheus metrics, and OpenTelemetry traces with one span for each upstream attempt. |
The agent features and reliability guides give the exact behavior.
250 tests · 90.8% statement coverage · 4 fuzz targets · 33 architecture decision records · signed releases
- Byte-exact streams. Golden tests compare recorded provider streams byte for byte. A client disconnect cancels the upstream request, and a graceful shutdown drains open streams.
- Real clients. Integration tests run the official Anthropic and OpenAI SDKs against the gateway.
- Shared state under load. Cluster tests run three gateway copies on one real Redis. They check the rate limit tolerance and the worst-case budget overspend.
- Fuzzing. Fuzz targets cover the SSE parser, request preflight, configuration loading, and redaction.
- Decisions on record. 33 ADRs explain the store, retries, budgets, sessions, cache, and dashboard, with the alternatives that were rejected.
- Supply chain. CI runs the race detector, static analysis,
govulncheck, a link check, and a demo-stack check. Releases have checksums, a keyless cosign signature, and build provenance.
Measured: sticky routing cost 25% less in a simulated 6-minute outage
A synthetic agent session sends 40 growing requests through two fake providers with a simulated 5-minute prompt cache. Without an outage, sticky routing changes nothing. During a long outage, it stops the session from moving back to the recovered primary with a cold cache. Run make replay to reproduce the numbers, and read the replay results for the method and its limits.
How is Tollgate different from LiteLLM or Portkey?
Those are general-purpose LLM gateways with many more providers and features. Tollgate does less, on purpose. It is built for coding agents: budgets for each agent session, sticky routing that keeps the prompt cache warm, streams that pass through byte for byte, and one static binary with no database. If you need dozens of providers or a hosted service, one of those is a better fit.
How much latency does Tollgate add?
Measure it on your own traffic. Each response has an x-tollgate-overhead-ms header, and each log line has overhead_us: the time that Tollgate spends before it sends the first upstream request. In the local runs for this README, it was between 0.02 and 0.13 ms. That is an example, not a benchmark. See observability.
Does Tollgate store my prompts?
No. Logs and usage events hold IDs, token counts, timings, and costs, not prompt text. A derived session ID is a SHA-256 hash. The opt-in response cache keeps responses in memory only. Opt-in redaction can mask personal data before a request leaves the gateway.
Which clients and providers work?
Any client that sends the Anthropic Messages API or the OpenAI Chat Completions API and lets you set the base URL. Claude Code and the official Anthropic and OpenAI SDKs are tested. The OpenAI Responses API is not supported.
Anthropic routes send to Anthropic or Claude on Vertex AI. OpenAI routes send to OpenAI or Ollama. Tollgate does not translate between the two formats. See configuration.
Can I run more than one copy?
Yes. Set store.kind: redis, and all copies share their rate limits and budgets. One Lua script checks and reserves all the limits of a request in one Redis round trip. If Redis fails, tenants with a hard budget get status 503, and other tenants continue on local rate limits. See limits and budgets.
How exact are the budgets?
Tollgate reserves the estimated cost before it sends a request, then settles at the real cost. A budget can still go over its limit by a bounded amount, because a response can be longer than its reservation. The limits guide gives the formula, and a cluster test checks it. To make the bound smaller, set reservation.output_tokens to the max_tokens of your clients.
The documentation site has search and dark mode. The same guides are in docs/.
| Guide | Covers |
|---|---|
| Quick start | Installation, a first request, Claude Code, and the demos |
| Configuration | Providers, routes, tenants, keys, and every YAML field |
| Limits and budgets | Reservations, Redis behavior, and overspend bounds |
| Reliability | Fallback rules, retries, circuit breakers, and timeouts |
| Agent features | Sessions, sticky routing, caching, redaction, and reports |
| Admin API | Dashboard access, endpoints, authentication, and data limits |
| Observability | Prometheus, OpenTelemetry, Grafana, and the Docker demo |
| Operations | Health checks, logs, shutdown, and deployment |
Tollgate is a young project, and early feedback shapes it.
- Found a bug, or want a feature? Open an issue.
- Want to send a change? Read CONTRIBUTING.md.
make checkruns the same checks as CI. - Found a security issue? Use private vulnerability reporting. See SECURITY.md.
If Tollgate saves you from one surprise bill, give it a star.
Stars help other people find the project.
Licensed under Apache-2.0.
