Know Exactly Where Your Tokens Go
When you’re running an Open WebUI instance for a team, a community, or a client-facing product, one question comes up fast: who’s using how many tokens, and how do you set limits?
Open WebUI doesn’t ship with built-in per-user token quotas. That means without a solution in place, a single user can burn through your entire API budget in an afternoon. The Usage Tracking Filter solves this by adding group-based token limits, real-time usage visibility, and graceful blocking — all backed by PostgreSQL and with zero PII stored.
How It Works
The filter hooks into Open WebUI’s inlet/outlet pipeline — the same mechanism used by safety filters and memory systems. Every request passes through two stages:
Inlet: Check Before Processing
Before the LLM ever sees a message, the inlet checks the user’s current token usage against their group’s limits. If they’re over their daily or monthly quota, the request is blocked with a friendly message explaining what happened and when their limit resets.
If they’re approaching their limit (configurable, default 80%), a warning is displayed but the request still goes through.
Outlet: Record After Response
After the LLM responds, the outlet extracts token counts from the response and records them to PostgreSQL. It also checks whether the user has now crossed a threshold and appends a usage warning directly to the chat response if needed.
This two-stage approach means usage checks are fast (a single database call before processing) and recording is non-blocking (happens after the user already has their response).
Group-Based Tiers
Instead of setting limits per user, the filter uses a group-based model. Users are assigned to a group, and each group defines its own daily and monthly token limits:
| Group | Daily Limit | Monthly Limit |
|---|---|---|
| Freemium | 50,000 tokens | 1,000,000 tokens |
| Pro | 500,000 tokens | 10,000,000 tokens |
| Enterprise | Unlimited | Unlimited |
These are the defaults — you can customize them or add entirely new tiers by updating a single row in the usage_limits table. Moving a user between tiers is one SQL statement:
INSERT INTO user_groups (user_id, group_name, assigned_by)
VALUES ('user-uuid-here', 'pro', 'admin')
ON CONFLICT (user_id) DO UPDATE SET
group_name = EXCLUDED.group_name,
assigned_at = NOW();
Users who haven’t been explicitly assigned default to the freemium tier automatically.
Auto-Schema Creation
One of the smoothest parts of this filter: you don’t need to run any SQL manually. On first request, the filter detects that the required tables don’t exist and creates the entire schema automatically — tables, indexes, views, and helper functions.
If you’re already running PostgreSQL for another filter (like the LangGraph Memory Filter), just point this filter at the same database. The schemas are independent and won’t conflict.
What Gets Stored
Privacy was a core design constraint. The filter stores only:
- User UUIDs — the opaque identifiers Open WebUI assigns internally. No emails, names, or any other personal information.
- Token counts — prompt tokens, completion tokens, and totals per request.
- Metadata — model ID, chat ID, and timestamps.
No conversation content is ever logged or persisted. The database contains nothing that could identify a real person without access to Open WebUI’s own user database.
Built-In Analytics
The schema includes several PostgreSQL views for querying usage patterns without writing complex SQL:
usage_daily— Token totals per user per dayusage_monthly— Token totals per user per monthusage_summary— Current daily and monthly usage with percentage calculationsusers_near_limit— All users currently above 80% of any limit
Check a specific user’s status:
SELECT * FROM usage_summary WHERE user_id = 'user-uuid-here';
See everyone approaching their limits:
SELECT * FROM users_near_limit;
A cleanup function is also included to purge records older than a configurable retention period (default 90 days):
SELECT cleanup_old_usage_records(90);
User Experience
The filter is designed to be transparent, not punitive. Users see their usage in real time via Open WebUI’s status bar:
- 📊 Usage: 12.5K/50.0K today (25%) • 45.2K/1000.0K month (5%) — Normal usage
- ⚠️ Usage: 42.0K/50.0K today (84%) • … — Approaching limit warning
- ❌ Daily limit reached — Blocked, with a clear explanation and reset time
When a user is blocked, the response explains which limit was hit, how many tokens were used, and when the limit resets. No cryptic error codes.
Admin users can optionally bypass all limits (enabled by default) so you never accidentally lock yourself out while testing.
Configuration
All settings are exposed as Open WebUI Valves — no code changes needed:
| Valve | Default | Description |
|---|---|---|
priority | 5 | Filter execution order (lower = runs first) |
postgres_host | langgraph-postgres | PostgreSQL host |
postgres_port | 5432 | PostgreSQL port |
postgres_database | langgraph_memory | Database name |
enable_blocking | true | Block requests when over limit |
show_usage_status | true | Show usage in status bar |
warn_at_percent | 80 | Warning threshold percentage |
admin_bypass | true | Let admins bypass limits |
default_group | freemium | Group for unassigned users |
Getting Started
- Go to Admin Panel → Functions in Open WebUI
- Click ”+ Add Function” and paste the filter code
- Save, enable, and set priority to 5 (so it runs before other filters)
- Configure your PostgreSQL connection in the Valves
- Send a message — the schema auto-creates and tracking begins immediately
If you need a dedicated PostgreSQL instance, a Docker Compose file is included in the repo to spin one up in seconds.
Open Source
The Usage Tracking Filter is MIT licensed and available on GitHub. Contributions, issues, and feature requests are welcome.