A 2-line wrapper around your OpenAI or Anthropic client that blocks an over-budget API call before it happens.
Project description
SpendGuard
A 2-line wrapper around your OpenAI or Anthropic client that blocks an over-budget API call before it happens — no surprises at the end of the month.
from spendguard import SpendGuard
guard = SpendGuard(workspace="my-app", ceiling_usd=20.0)
client = guard.wrap_openai(OpenAI()) # or wrap_anthropic(Anthropic())
# Call the client exactly as normal — SpendGuard intercepts transparently.
# If the estimated cost would push cumulative spend past 25% of the $20 ceiling,
# it raises BudgetExceededError before the API call is made.
response = client.chat.completions.create(model="gpt-4o", messages=[...])
Install
pip install spendguard
For more accurate pre-call token counting on OpenAI models:
pip install spendguard[tiktoken]
How it works
SpendGuard wraps your existing client object. Every call goes through two steps:
- Pre-call estimate — approximates the input token count and adds the max output tokens × the model's per-token rate. If
cumulative_spend + estimate > ceiling × threshold_pct, it raisesBudgetExceededErrorbefore the network call. - Post-call commit — reads the provider's actual usage numbers from the response and records the real cost.
The default threshold is 25% of the ceiling (threshold_pct=0.25). This means a single call can consume at most 25% of your monthly budget — it is a guardrail against a single runaway call, not a hard cap at 100%.
Supported providers and models
| Provider | Client wrapper | Models gated by default |
|---|---|---|
| OpenAI | wrap_openai() |
gpt-4o, gpt-4o-mini, and all models in the pricing config |
| Anthropic | wrap_anthropic() |
claude-3-5-sonnet, claude-3-opus, haiku, and all models in the pricing config |
Usage
Basic setup
from openai import OpenAI
from spendguard import SpendGuard
guard = SpendGuard(workspace="my-product", ceiling_usd=20.0)
client = guard.wrap_openai(OpenAI())
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=512,
)
except BudgetExceededError as e:
print(f"Blocked: {e}")
Anthropic
from anthropic import Anthropic
from spendguard import SpendGuard
guard = SpendGuard(workspace="my-product", ceiling_usd=20.0)
client = guard.wrap_anthropic(Anthropic())
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
Overriding a block on purpose
When you explicitly want to allow a call that would be blocked (e.g., a one-time large batch job), use track() with override=True:
with guard.track(override=True):
response = client.chat.completions.create(...) # never blocked
The override only applies inside the with block and does not persist.
Inspecting current spend
summary = guard.get_summary()
# {"ceiling_usd": 20.0, "spent_usd": 1.23, "reserved_usd": 0.0, "threshold_pct": 0.25}
Workspace isolation
Each SpendGuard instance is scoped to a workspace string. When you run multiple products or feature flags, give each its own workspace so their budgets are tracked independently.
Out of scope for v0.1
- Streaming calls (
stream=True) — explicitly rejected with a clear error. - Embeddings, images, audio, and other non-chat/messages endpoints.
- Persistent spend across process restarts (resets on
SpendGuard()construction).
Persistence and streaming support are planned for v1.0.
Feedback
Found a bug or have a feature request? Open an issue — all feedback welcome.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spendguard-0.1.0.tar.gz.
File metadata
- Download URL: spendguard-0.1.0.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e1edd8e95a6f2a3648d13dc97cff6071329a5c6b6c8619fbb59fecdedfdb601
|
|
| MD5 |
6f6b7b85667b5ebc5e7da57adc420e11
|
|
| BLAKE2b-256 |
21d9f6a0d064dc3be78710d63d8483474f035a71fdb7976ec73087ed834e15c1
|
Provenance
The following attestation bundles were made for spendguard-0.1.0.tar.gz:
Publisher:
publish-spendguard.yml on Rahul-git23/spendguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spendguard-0.1.0.tar.gz -
Subject digest:
2e1edd8e95a6f2a3648d13dc97cff6071329a5c6b6c8619fbb59fecdedfdb601 - Sigstore transparency entry: 1994851824
- Sigstore integration time:
-
Permalink:
Rahul-git23/spendguard@2639162d73b7e05edbd7be2c7fdf79afc9237a7b -
Branch / Tag:
refs/tags/spendguard-v0.1.0 - Owner: https://github.com/Rahul-git23
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-spendguard.yml@2639162d73b7e05edbd7be2c7fdf79afc9237a7b -
Trigger Event:
release
-
Statement type:
File details
Details for the file spendguard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: spendguard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90526c95400e1cfde27a4561e587153f92bd9093de5f1b9e97345a8f0ed3d9c1
|
|
| MD5 |
1cf56f83f5e1cb6b746c61bc864776c9
|
|
| BLAKE2b-256 |
cdd0a0d8429f34da94de815843764a7c1925531e5cac7dbdd5b4b9565325cdb2
|
Provenance
The following attestation bundles were made for spendguard-0.1.0-py3-none-any.whl:
Publisher:
publish-spendguard.yml on Rahul-git23/spendguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spendguard-0.1.0-py3-none-any.whl -
Subject digest:
90526c95400e1cfde27a4561e587153f92bd9093de5f1b9e97345a8f0ed3d9c1 - Sigstore transparency entry: 1994852209
- Sigstore integration time:
-
Permalink:
Rahul-git23/spendguard@2639162d73b7e05edbd7be2c7fdf79afc9237a7b -
Branch / Tag:
refs/tags/spendguard-v0.1.0 - Owner: https://github.com/Rahul-git23
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-spendguard.yml@2639162d73b7e05edbd7be2c7fdf79afc9237a7b -
Trigger Event:
release
-
Statement type: