AgentCap
The API-key guard for unattended Codex CLI and Gemini CLI runs. AgentCap runs
the CLI behind a local proxy on 127.0.0.1, caps spend for the run, and when the
budget is gone it kills the local process and tells you why. No dashboard, no
account, no code changes for tools that honour a base-URL environment variable.
It meters API-key traffic: POST /v1/responses and /v1/chat/completions
(Codex CLI uses the Responses API) plus Gemini generateContent /
streamGenerateContent, by setting OPENAI_BASE_URL and
GOOGLE_GEMINI_BASE_URL for the child. ChatGPT or Google sign-in
(OAuth/subscription) traffic is not metered.
What it is
AgentCap is a small, local, serverless spend-cap proxy. You give it a command and a cap:
agentcap run --cap 2 -- codex exec "fix the failing tests"
Everything the child sends to OpenAI or Gemini is routed through a proxy on
127.0.0.1. AgentCap reserves the worst-case cost of each request before it is
forwarded, settles the real usage afterwards, and terminates the agent's process
group if the cap would be exceeded.
Install
The package is published on PyPI as agentcap-cli (the name agentcap is
taken), but the command it installs is still agentcap.
# install the command globally
pipx install agentcap-cli
# or into the current environment
pip install agentcap-cli
From a checkout:
uv venv .venv
uv pip install -e .
# or install this checkout as the global command
pipx install .
Quickstart
# Codex CLI under a $2 cap (API-key mode: uses OPENAI_API_KEY)
agentcap run --cap 2 -- codex exec "fix the failing tests"
# Gemini CLI under a $2 cap (API-key mode: uses GEMINI_API_KEY)
agentcap run --cap 2 -- gemini -p "refactor utils.py"
# Run your own Python agent (it must honour OPENAI_BASE_URL)
agentcap run --cap 1 -- python my_agent.py
# Keep a proxy open for several terminals
agentcap serve --port 8787 --cap 5
# Inspect spend
agentcap status --port 8787
agentcap status --db
# Inspect the bundled price table
agentcap pricing
agentcap pricing google/gemini-4-argon
Uses your API key (OPENAI_API_KEY / GEMINI_API_KEY). ChatGPT or Google
sign-in (OAuth/subscription) traffic is not metered.
agentcap run accepts --cap (default $5, or $AGENTCAP_CAP),
--hour-cap / --day-cap (Pro), --allow-unmetered, --no-clamp, --port
and --quiet.
Try it without an API key
A local fake upstream is bundled for testing metering and clamping offline.
# Terminal 1: a fake Gemini/OpenAI endpoint on loopback only
python -m agentcap.devtools.fake_upstream --port 9000
# Terminal 2: run a child against it (no API key needed)
AGENTCAP_GEMINI_UPSTREAM=http://127.0.0.1:9000 \
agentcap run --cap 0.05 -- python - <<'PY'
import os, httpx
base = os.environ["GOOGLE_GEMINI_BASE_URL"]
url = base + "/v1beta/models/gemini-4-argon:generateContent"
r = httpx.post(url, json={"contents": [{"parts": [{"text": "hi"}]}]})
print(r.status_code, "output limit:", r.headers.get("x-fake-output-limit"))
PY
The fake returns realistic prompt tokens (ceil(text characters / 4)) and
min(output limit, 2000) output tokens, so the run has a real cost. The
x-fake-output-limit response header echoes the limit the fake actually
received (none if the request had none): with a 5-cent cap the number should
be below the model's 65536-token default, proving AgentCap clamped it. Use
AGENTCAP_OPENAI_UPSTREAM for OpenAI paths.
Overrides must be https://, or http:// on 127.0.0.1, localhost or
::1; anything else is refused with exit code 2. A yellow one-line notice
shows the active override with userinfo and query strings stripped.
How it works
-
agentcap runstarts the proxy on a random free port, bound only to127.0.0.1, and waits for/_agentcap/status. -
The child process is started in its own process group with:
OPENAI_BASE_URL=http://127.0.0.1:PORT/openai/v1 GOOGLE_GEMINI_BASE_URL=http://127.0.0.1:PORT/gemini GEMINI_API_BASE_URL=http://127.0.0.1:PORT/gemini AGENTCAP_RUN_ID=... AGENTCAP_PROXY=http://127.0.0.1:PORT
-
Before forwarding a request, the proxy estimates the prompt size and the maximum output size, then atomically reserves that worst-case USD amount in a local SQLite ledger. The input estimate is padded by a conservative
input_safety_factor(1.25 by default) to cover system prompts, tool schemas, images and tokenizer differences that the local estimate cannot see. Concurrent requests cannot jointly overshoot the cap. -
After the upstream responds, the real usage is parsed and the reservation is settled to the actual cost (rounded up to the micro-dollar). Settlement uses the provider's reported tokens, never the padded estimate. If the provider bills more input tokens than estimated, that one settlement can exceed its reservation; AgentCap emits an
overshootalert and the run's spend may end slightly above the cap. -
If the worst case does not fit the remaining budget but the prompt itself does, the proxy clamps the output limit to exactly what fits (at least
256tokens by default) and reserves only that amount. The agent keeps running instead of dying on the first request; real usage still settles the reservation. Clamping is on by default and can be disabled with--no-clamp. -
If even a minimal clamped request cannot fit, the request is blocked and the whole child process group is terminated (
SIGTERM, thenSIGKILLafter 5s).agentcap runexits with code3. A detected loop exits with4. The kill and its red alert fire at most once per run; later rejected requests just receive429responses. -
Unknown models and unparseable bodies are rejected (fail-closed); the request is never forwarded unmetered.
The proxy makes only two kinds of request-body change: it adds
stream_options.include_usage to OpenAI Chat Completions streams so final
usage is available, and (unless --no-clamp is given) it lowers the request's
output limit when the worst-case reservation would cross the remaining budget.
For OpenAI that is max_completion_tokens (or the legacy max_tokens if the
body already used it) or max_output_tokens for the Responses API; for Gemini
it is generationConfig.maxOutputTokens. An existing smaller limit is never
raised. Everything else is forwarded byte-for-byte, including credentials.
Reasoning/thinking tokens are billed as output and count against the same output limit, so a clamp also caps them.
Supported
| Provider | Endpoints |
|---|---|
| OpenAI | POST /v1/chat/completions, /v1/responses, /v1/completions, /v1/embeddings |
| Gemini | generateContent, streamGenerateContent (other Gemini endpoints pass through) |
Token counting (countTokens) and GET requests are free passthroughs.
Request bodies are billed at price-book rates; embeddings are input-only.
Limitations
- Only tools that honour
OPENAI_BASE_URL/GOOGLE_GEMINI_BASE_URL(and send API keys) can be metered. OAuth or subscription logins are not metered. - Prices are a local table, not a live feed. Check them with
agentcap pricing; override any entry in~/.config/agentcap/pricing.json(or$AGENTCAP_PRICING_FILE). - The proxy rewrites a request only to add
stream_options.include_usageto OpenAI chat streams and, unless--no-clampis set, to lower its output limit to what the remaining budget can afford. Prompts are never changed. - Estimates are deliberately conservative, so a reservation can be larger than
the eventual cost. The cap is enforced before each request with a worst-case
reservation, and concurrent requests cannot jointly overshoot it. This is not
a mathematical guarantee: if a provider bills more input tokens than
estimated (for example server-side system prompts, tool schemas, images or
tokenizer differences), the final spend can exceed the cap by at most that
one request's estimation error. AgentCap emits an
overshootalert when a settlement exceeds its reservation. - The ledger lives at
~/.local/share/agentcap/ledger.sqlite3(override with$AGENTCAP_DB).
Privacy
AgentCap stores no prompts, response contents, API keys or Authorization headers — not in the ledger, not in logs, not in alerts. Only token counts, costs, model names, timestamps and request hashes are recorded. Nothing leaves your machine except your own upstream API calls — and, for Pro users only, the license check described below.
Pro license checks: activating a Pro key (and the periodic re-check) sends
only the license key, the Polar organization id and an anonymous machine
fingerprint hash (SHA-256; it contains no hostname and no username) to Polar's
public license API at api.polar.sh. Free users make no license requests at
all.
Free vs Pro
| Capability | Free | Pro |
|---|---|---|
| Per-run USD cap with worst-case reservation | ✅ | ✅ |
| Output clamping | ✅ | ✅ |
| Kills the local process at the cap | ✅ | ✅ |
| Terminal alert with the reason | ✅ | ✅ |
| Loop breaker (exit code 4) | — | ✅ |
Webhook alerts (AGENTCAP_WEBHOOK_URL) |
— | ✅ |
Telegram alerts (AGENTCAP_TELEGRAM_BOT_TOKEN, AGENTCAP_TELEGRAM_CHAT_ID) |
— | ✅ |
| Hourly / daily caps | — | ✅ |
Free is $0 forever: the basic hard cap — a per-run USD cap with a worst-case reservation, output clamping, the run killed at the cap, and terminal alerts — with no account required.
Pro is $12 one-time — not a subscription — and is centred on the loop
breaker: loop detection that stops a stuck agent and exits with code 4.
It also adds alerts over a webhook and Telegram, plus hourly / daily caps as
an extra, and updates for the 0.x line.
After purchase you receive a license key by email (via Polar). Activate it with
agentcap license activate <KEY>; agentcap license deactivate removes it from
this machine.
How it compares
| Tool | What it does and how it enforces a cap |
|---|---|
| RelayPlane | relayplane.com: free, MIT-licensed local proxy with daily/hourly/per-request/per-run caps, routing and a kill switch. In its source (as of early Oct 2026) we found no inbound OpenAI Responses API (/v1/responses) route and no Gemini-native generateContent route, and on budget breach it blocks or warns at the proxy rather than stopping your agent's process. A good choice for Anthropic/OpenAI chat traffic and broader routing. |
| LoopGuard | tryloopguard.com: free MIT CLI that hooks into Claude Code, Codex CLI and Cursor (no proxy, works with subscriptions) with per-session/daily caps and a no-progress loop killer; paid tier ($9/month) adds phone/Slack alerts and remote pause. A good fit if you use subscriptions or Claude Code. |
| LiteLLM budgets | Budgets per key, user or team in the LiteLLM proxy; budgets need its database. A good fit if you already run a LiteLLM gateway; it is a server you deploy and configure. |
| OpenRouter credit limits / provider spend caps | Account- or key-level and typically monthly (OpenRouter credit limits, OpenAI and Google spend caps). Provider caps are evaluated with a delay — Google has described about 10 minutes for the Gemini API cap — so a fast loop can overshoot. |
| AgentCap | Proxy for API-key traffic from Codex CLI, Gemini CLI or your own script: per-run cap with worst-case reservation and output clamping, kills the process group at the cap and prints why. Pro: loop breaker + Telegram/webhook alerts, $12 one-time. |
Pick what matches your setup — these tools can also be combined.
License
MIT — see LICENSE. Copyright (c) 2026 Kutay Karahan.
The Pro license key is separate from the source license. How it is checked:
- On activation the key is validated against Polar's public license API
(
api.polar.sh). After that it is re-checked at most about once every 7 days. - The result is cached in
~/.config/agentcap/license.json(overridable with$AGENTCAP_LICENSE_FILE). - If you are offline, a previously validated license keeps working for up to 14 days since the last successful check. After that AgentCap falls back to the free tier until the key can be validated again.
AGENTCAP_POLAR_ORG_IDselects the Polar organization the key belongs to.
Development
uv venv .venv
uv pip install -e '.[dev]'
.venv/bin/pytest -q
Tests use mocked upstreams (httpx.MockTransport) and never make real network
calls; no real API keys are required.
Metadata
Release files for agentcap-cli 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| agentcap_cli-0.1.0.tar.gz | 85.6 kB | Without provenance | Details for agentcap_cli-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| agentcap_cli-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for agentcap_cli-0.1.0-py3-none-any.whl |
Total release size: 147.8 kB
Release files / agentcap_cli-0.1.0.tar.gz
| Download URL | agentcap_cli-0.1.0.tar.gz |
|---|---|
| Size | 85.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1539afca989fd5931ac4111e84d643f2c58b74424db498d35d036863be069348
|
|
BLAKE2b-256 checksum How to use checksums |
2b1c4716bf088efaa483402537d27351926725795f7635111588e711c139c02f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / agentcap_cli-0.1.0-py3-none-any.whl
| Download URL | agentcap_cli-0.1.0-py3-none-any.whl |
|---|---|
| Size | 62.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
67537359a6baacf1dceba6de54af064ee00945afe04d62c429ba30313b7c0719
|
|
BLAKE2b-256 checksum How to use checksums |
f580116760ca692e232b472c518891a67bb82d6157d418b923f0c16c8c97e530
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for agentcap-cli 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.