Testing: Extensively tested on ECS Fargate deployment with OpenRouter and AWS Bedrock
Code review: Self-reviewed
Title Prefix:feat:
Changelog Entry
Added
Per-group token usage limits with configurable daily/monthly enforcement
Admin UI for setting token limits, soft limits, and priority per group
Soft limit warning toast and hard limit 429 block
Usage tracking for direct API calls and temporary chats
Usage data preserved when chats are deleted (content stripped, usage metadata retained)
Description
Adds per-group token usage limits that enforce daily or monthly caps on non-admin users. Full design discussion in #23558.
How it works:
Enforcement: Pre-request check in generate_chat_completion queries chat_message for the user's token usage in the current period. If over the group's limit, returns 429. Background tasks (title/tag/follow-up generation) are skipped via metadata.task check.
Usage data preservation: Chat deletion strips message content but preserves usage metadata (user_id, model_id, tokens, timestamps). Prevents users from resetting usage by deleting chats.
Temp chats & API calls: Since chat_message has a FK to chat (which doesn't exist for these), usage is written to a small usage_ledger table. Enforcement sums both tables.
Configuration: Stored in the existing group permissions JSON field. Admin UI added to the Permissions tab in group settings.
Soft/hard limits: Configurable soft limit shows a warning toast; hard limit blocks with 429.
Priority: When a user belongs to multiple groups, the limit from the highest-priority group (lowest priority number) applies.
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
Token counts verified to match analytics dashboard
Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via pg_constraint query)
Soft limit warning toasts and hard limit 429 blocks working
Admin users bypass all limits
Direct API calls (POST /api/chat/completions with JWT, no chat_id, stream: false) — both enforced and tracked
Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
Automated code review via pr-validator-bot — both findings addressed and retested
Breaking Changes
Chat message content is now stripped (set to NULL) on deletion instead of row deletion. Usage metadata (user_id, model_id, input_tokens, output_tokens, timestamps) is preserved. This changes the behavior of delete_chat_by_id, delete_chat_by_id_and_user_id, delete_chats_by_user_id, and delete_chats_by_user_id_and_folder_id.
New Alembic migration adds usage_ledger table (revision c3d4e5f6a7b8, depends on b7c8d9e0f1a2)
Contributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/open-webui/open-webui/pull/23600
**Author:** [@smorello87](https://github.com/smorello87)
**Created:** 4/11/2026
**Status:** ❌ Closed
**Base:** `dev` ← **Head:** `feat/usage-limits`
---
### 📝 Commits (1)
- [`3e4197b`](https://github.com/open-webui/open-webui/commit/3e4197b15484425e777ce106440799d80113f9a4) feat: per-group token usage limits with enforcement
### 📊 Changes
**13 files changed** (+760 additions, -12 deletions)
<details>
<summary>View changed files</summary>
📝 `backend/open_webui/main.py` (+2 -0)
➕ `backend/open_webui/migrations/versions/c3d4e5f6a7b8_add_usage_ledger_table.py` (+102 -0)
📝 `backend/open_webui/models/chat_messages.py` (+1 -1)
📝 `backend/open_webui/models/chats.py` (+22 -8)
📝 `backend/open_webui/models/groups.py` (+20 -0)
➕ `backend/open_webui/models/usage_ledger.py` (+144 -0)
➕ `backend/open_webui/routers/usage_limits.py` (+151 -0)
📝 `backend/open_webui/utils/chat.py` (+17 -1)
📝 `backend/open_webui/utils/middleware.py` (+60 -0)
➕ `backend/open_webui/utils/usage_limits.py` (+153 -0)
📝 `src/lib/components/admin/Users/Groups/EditGroupModal.svelte` (+2 -1)
📝 `src/lib/components/admin/Users/Groups/Permissions.svelte` (+81 -0)
📝 `src/lib/components/chat/Chat.svelte` (+5 -1)
</details>
### 📄 Description
<!--
⚠️ CRITICAL CHECKS FOR CONTRIBUTORS (READ, DON'T DELETE) ⚠️
1. Target the `dev` branch. PRs targeting `main` will be automatically closed.
2. Do NOT delete the CLA section at the bottom. It is required for the bot to accept your PR.
-->
- [x] **Target branch:** `dev`
- [x] **Description:** See below
- [x] **Testing:** Extensively tested on ECS Fargate deployment with OpenRouter and AWS Bedrock
- [x] **Code review:** Self-reviewed
- [x] **Title Prefix:** `feat:`
# Changelog Entry
### Added
- Per-group token usage limits with configurable daily/monthly enforcement
- Admin UI for setting token limits, soft limits, and priority per group
- Soft limit warning toast and hard limit 429 block
- Usage tracking for direct API calls and temporary chats
- Usage data preserved when chats are deleted (content stripped, usage metadata retained)
---
### Description
Adds per-group token usage limits that enforce daily or monthly caps on non-admin users. Full design discussion in #23558.
**How it works:**
- **Enforcement**: Pre-request check in `generate_chat_completion` queries `chat_message` for the user's token usage in the current period. If over the group's limit, returns 429. Background tasks (title/tag/follow-up generation) are skipped via `metadata.task` check.
- **Usage data preservation**: Chat deletion strips message content but preserves usage metadata (user_id, model_id, tokens, timestamps). Prevents users from resetting usage by deleting chats.
- **Temp chats & API calls**: Since `chat_message` has a FK to `chat` (which doesn't exist for these), usage is written to a small `usage_ledger` table. Enforcement sums both tables.
- **Configuration**: Stored in the existing group `permissions` JSON field. Admin UI added to the Permissions tab in group settings.
- **Soft/hard limits**: Configurable soft limit shows a warning toast; hard limit blocks with 429.
- **Priority**: When a user belongs to multiple groups, the limit from the highest-priority group (lowest priority number) applies.
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
- OpenRouter (Gemma 4 31B) and AWS Bedrock (Nova Pro, Nova Premier)
- Token counts verified to match analytics dashboard
- Background tasks (title/tag/follow-up gen) excluded from counting and enforcement — verified via CloudWatch logs
- Chat deletion confirmed to preserve usage counts (FK changed to SET NULL, verified via `pg_constraint` query)
- Soft limit warning toasts and hard limit 429 blocks working
- Admin users bypass all limits
- Direct API calls (`POST /api/chat/completions` with JWT, no `chat_id`, `stream: false`) — both enforced and tracked
- Alembic migration tested on PostgreSQL (uses raw SQL for FK alteration, batch mode for SQLite)
- Automated code review via pr-validator-bot — both findings addressed and retested
### Breaking Changes
- Chat message content is now stripped (set to NULL) on deletion instead of row deletion. Usage metadata (user_id, model_id, input_tokens, output_tokens, timestamps) is preserved. This changes the behavior of `delete_chat_by_id`, `delete_chat_by_id_and_user_id`, `delete_chats_by_user_id`, and `delete_chats_by_user_id_and_folder_id`.
### Additional Information
- Discussion: #23558
- Related: #21675 (analytics enhancements), #23323 (original feature request)
- New Alembic migration adds `usage_ledger` table (revision `c3d4e5f6a7b8`, depends on `b7c8d9e0f1a2`)
### Contributor License Agreement
- [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/23600
Author: @smorello87
Created: 4/11/2026
Status: ❌ Closed
Base:
dev← Head:feat/usage-limits📝 Commits (1)
3e4197bfeat: per-group token usage limits with enforcement📊 Changes
13 files changed (+760 additions, -12 deletions)
View changed files
📝
backend/open_webui/main.py(+2 -0)➕
backend/open_webui/migrations/versions/c3d4e5f6a7b8_add_usage_ledger_table.py(+102 -0)📝
backend/open_webui/models/chat_messages.py(+1 -1)📝
backend/open_webui/models/chats.py(+22 -8)📝
backend/open_webui/models/groups.py(+20 -0)➕
backend/open_webui/models/usage_ledger.py(+144 -0)➕
backend/open_webui/routers/usage_limits.py(+151 -0)📝
backend/open_webui/utils/chat.py(+17 -1)📝
backend/open_webui/utils/middleware.py(+60 -0)➕
backend/open_webui/utils/usage_limits.py(+153 -0)📝
src/lib/components/admin/Users/Groups/EditGroupModal.svelte(+2 -1)📝
src/lib/components/admin/Users/Groups/Permissions.svelte(+81 -0)📝
src/lib/components/chat/Chat.svelte(+5 -1)📄 Description
devfeat:Changelog Entry
Added
Description
Adds per-group token usage limits that enforce daily or monthly caps on non-admin users. Full design discussion in #23558.
How it works:
generate_chat_completionquerieschat_messagefor the user's token usage in the current period. If over the group's limit, returns 429. Background tasks (title/tag/follow-up generation) are skipped viametadata.taskcheck.chat_messagehas a FK tochat(which doesn't exist for these), usage is written to a smallusage_ledgertable. Enforcement sums both tables.permissionsJSON field. Admin UI added to the Permissions tab in group settings.Testing
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedTesting
Tested on an ECS Fargate deployment (CUNY AI Lab, PostgreSQL 16.3) with:
pg_constraintquery)POST /api/chat/completionswith JWT, nochat_id,stream: false) — both enforced and trackedBreaking Changes
delete_chat_by_id,delete_chat_by_id_and_user_id,delete_chats_by_user_id, anddelete_chats_by_user_id_and_folder_id.Additional Information
usage_ledgertable (revisionc3d4e5f6a7b8, depends onb7c8d9e0f1a2)Contributor License Agreement
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.