7.1 KiB
Budget Code Matrix
What LiteLLM actually implements for budgets: every entity that can carry a dollar
budget, how the limit is enforced, and where in the code it happens. This is the
"what we support" reference; the companion BUDGET_TEST_COVERAGE_MATRIX.md maps
each row to its tests and the e2e gaps.
Over-budget surfaces as a budget_exceeded error (the live suite
tests/otel_tests/test_e2e_budgeting.py asserts type == "budget_exceeded",
code == "429"); the underlying BudgetExceededError is defined in
litellm/exceptions.py (status_code=400). Enforcement runs in common_checks()
/ auth_checks.py at auth time, plus pre-call reservation in
budget_reservation.py.
Legend for "Enforced": block = request rejected; filter = router skips the deployment; alert = notify only, request proceeds.
1. Per-entity dollar budgets
| Entity | Budget stored | Hard max_budget |
Soft budget | Per-window | Model budget | Reset by budget_duration |
|---|---|---|---|---|---|---|
| API key | LiteLLM_VerificationToken (direct cols + budget_id FK) |
block (_virtual_key_max_budget_check) |
alert (_virtual_key_soft_budget_check) + 80% alert |
block (_virtual_key_multi_budget_check) |
block (model_max_budget_limiter.is_key_within_model_budget) |
keys reset job |
| Internal user | LiteLLM_UserTable (direct cols) |
block (common_checks, only when not on a team) |
- | - | via model_max_budget json |
users reset job |
| Team | LiteLLM_TeamTable (direct cols) |
block (_team_max_budget_check) |
alert (_team_soft_budget_check) |
block (_team_multi_budget_check) |
via model_max_budget |
teams reset job |
| Team member | LiteLLM_TeamMembership -> LiteLLM_BudgetTable |
block (_check_team_member_budget) |
- | - | - | budget-table reset job |
| End-user / customer | LiteLLM_EndUserTable -> LiteLLM_BudgetTable |
block (_check_end_user_budget) |
- | - | block (is_end_user_within_model_budget) |
budget-table reset job |
| Organization | LiteLLM_OrganizationTable -> LiteLLM_BudgetTable |
block (_organization_max_budget_check) |
- | - | via budget-table | budget-table reset job |
| Tag | LiteLLM_TagTable -> LiteLLM_BudgetTable |
block (_tag_max_budget_check) |
- | - | via budget-table | budget-table reset job |
| Project | LiteLLM_ProjectTable -> LiteLLM_BudgetTable |
block (_project_max_budget_check) |
alert (_project_soft_budget_check) |
- | - | budget-table reset job |
| Provider (router) | config provider_budget_config (in-memory) |
filter (router_strategy/budget_limiter) |
- | yes (time window) | - | window TTL |
| Global proxy | litellm.max_budget (config) |
block (_global_proxy_budget_check) |
- | - | - | - |
Notes / flags from the code:
- User budget only enforced off-team:
common_checksskips the personal-user budget when the key belongs to a team (team budget governs instead). - Comparison operators are inconsistent: key/user use
>=, team/end-user main budget use>. Spend exactly atmax_budgetblocks a key but not a team. - Provider budgets are filter-only: an over-budget provider is removed from
routing; if all are over budget the router raises
no_deployments_with_provider_budget_routing(not a per-entity block). - Enforcement timing differs by entity: key / user / org / team-member / tag /
model enforce off real-time reservation counters (block within ~2 calls);
end-user enforcement reads
EndUserTable.spend, which only updates on theproxy_batch_write_atflush, so it lags by that interval (verified live).
2. Budget mechanisms
| Mechanism | What it does | Code |
|---|---|---|
| Pre-call reservation | Estimates max request cost, atomically reserves against redis spend counters for key/team/user/end_user/tag/team_member/org before the call; blocks if a counter would exceed | spend_tracking/budget_reservation.py |
| Post-call reconciliation | Adjusts the reservation to the actual cost once known | reconcile_budget_reservation |
| Read-time enforcement | Auth-time check of current spend vs max_budget |
auth_checks.common_checks + per-entity _*_max_budget_check |
| Soft budget / alerts | At soft_budget (or 80% of max) fire Slack/email alert, do not block |
_virtual_key_soft_budget_check, _team_soft_budget_check, budget_alerts |
| Multi-window budgets | budget_limits list of {budget_duration, max_budget}; each window enforced + reset independently |
_virtual_key_multi_budget_check, reset_budget_windows |
| Model-level budgets | model_max_budget dict (per model: budget_limit + time_period) on key/user/end_user |
hooks/model_max_budget_limiter.py |
| Reset by duration | Job zeros spend, recomputes budget_reset_at = now + duration_in_seconds(budget_duration), invalidates redis counters |
common_utils/reset_budget_job.py, duration_parser.duration_in_seconds |
| Zero-cost bypass | Models with no configured price bypass budget reservation | budget_reservation zero-cost path |
3. Budget management surface (endpoints)
| Action | Endpoint | Handler |
|---|---|---|
| Create budget | POST /budget/new |
new_budget |
| Update budget | POST /budget/update |
update_budget |
| Budget info | POST /budget/info ({"budgets": [id]}) |
info_budget |
| Budget settings | GET /budget/settings |
budget_settings |
| List budgets | GET /budget/list |
list_budget |
| Delete budget | POST /budget/delete ({"id": id}) |
delete_budget |
| Set on key | POST /key/generate, /key/update (max_budget, soft_budget, budget_duration, model_max_budget, budget_id) |
key mgmt |
| Set on user | POST /user/new (max_budget, budget_duration) |
internal user |
| Set on team | POST /team/new (max_budget, soft_budget, team_member_budget) |
team |
| Set on team member | POST /team/member_add (max_budget_in_team) |
team |
| Set on org | POST /organization/new (max_budget, soft_budget, model_max_budget) |
org |
| Set on customer | POST /customer/new, /customer/update (max_budget, budget_id) |
customer |
| Set on tag | POST /tag/new, /tag/update (max_budget) |
tag mgmt |
| Read budget+spend | /key/info, /user/info, /team/info, /organization/info, /customer/info, /budget/info |
per-entity info |
Endpoint method/shape gotchas verified live: /organization/delete is DELETE
with {"organization_ids": [id]}; /budget/info takes {"budgets": [id]};
model_max_budget entries use {"budget_limit", "time_period"}.
4. Config knobs
| Setting | Effect |
|---|---|
litellm.max_budget |
proxy-wide hard cap (global proxy budget) |
max_internal_user_budget / default_max_internal_user_budget |
default max_budget for internal users |
internal_user_budget_duration |
default reset duration for internal users |
max_end_user_budget / max_end_user_budget_id |
default budget for end-users |
default_team_params |
default max_budget / budget_duration / limits for teams |
provider_budget_config (router) |
per-provider spend caps + windows |