- Python 60.6%
- Vue 28.5%
- TypeScript 9.3%
- Dockerfile 0.8%
- Mako 0.4%
- Other 0.4%
| backend | ||
| frontend | ||
| .env.example | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| README.md | ||
Claude Usage Monitor
A self-hosted web application for real-time visibility into one or more Claude.ai consumer account usage limits, with configurable threshold alerts delivered via Discord, Matrix, Pushover, or generic webhooks.
Features
- Live dashboard — semi-circle gauges show 5-hour rolling and weekly usage per account, updated in real time via Server-Sent Events
- Multi-account — monitor as many Claude.ai Pro/Max accounts as you like from a single interface
- Threshold alerts — configure per-user alert subscriptions at any percentage threshold (default 75 %, 90 %, 95 %) for session and/or weekly limits
- Four notification channels — Discord webhooks, generic HTTP webhooks, Pushover, and Matrix
- Authentik OAuth2 login — no local password management; roles are mapped from your Authentik groups
- Three access levels —
readonly(view only),user(view + manage own alerts),admin(full access) - Zero-restart config — all runtime settings (poll interval, cooldown, thresholds) are stored in the database and take effect immediately
- Docker deployment — single
docker compose up -dgets everything running
Screenshots
Dashboard with live usage gauges, SSE connection indicator, and per-account status dots.
╔══════════════════════════════════╗ ╔══════════════════════════════════╗
║ ● work@example.com 3m ago ║ ║ ● personal@example.com 1m ago ║
║ ║ ║ ║
║ 62.4% 44.1% ║ ║ 91.2% 78.5% ║
║ 5-hour Weekly ║ ║ 5-hour Weekly ║
║ 312/500 882/2000 ║ ║ 456/500 1570/2000 ║
╚══════════════════════════════════╝ ╚══════════════════════════════════╝
How it works
Claude.ai consumer plans (Pro, Max 5×, Max 20×) enforce a 5-hour rolling message window and a weekly limit shared across all Claude surfaces. This app polls the unofficial Claude.ai internal API every 5 minutes (configurable) using your session cookies, stores the results in SQLite, and pushes updates to all connected browsers over SSE.
Important: Claude.ai session cookies (
sk-ant-sid) expire approximately every 28 days. The dashboard will display a warning when a cookie is approaching expiry; you update it in the Accounts section without restarting the app.
Requirements
- Docker and Docker Compose
- An Authentik instance (for OAuth2/OIDC login)
- One or more Claude.ai Pro or Max accounts
Quick start
1. Clone and configure
git clone https://git.rhoving.com/rbrooks/ClaudeUsageMonitoring.git
cd ClaudeUsageMonitoring
cp .env.example .env
Edit .env and fill in every value:
# Generate a Fernet key:
# python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
FERNET_KEY=
# Generate a random secret:
# python -c "import secrets; print(secrets.token_hex(32))"
SESSION_SECRET_KEY=
OIDC_CLIENT_ID=claude-monitor
OIDC_CLIENT_SECRET=<from Authentik>
OIDC_DISCOVERY_URL=https://auth.example.com/application/o/claude-monitor/.well-known/openid-configuration
APP_BASE_URL=http://your-host-or-ip
2. Configure Authentik
- Create an OAuth2/OpenID Connect provider in Authentik.
- Set the redirect URI to
http://your-host/api/auth/callback. - Under Advanced protocol settings, ensure the
groupsscope is included in the userinfo response — this is how roles are assigned. - Create two groups:
claude-monitor-admins→ maps to theadminroleclaude-monitor-users→ maps to theuserrole- Any user in neither group receives the
readonlyrole
- Copy the Client ID and Client Secret into your
.env.
The group names are configurable via ADMIN_GROUP_NAME and USER_GROUP_NAME in .env if you prefer different names.
3. Start
docker compose up -d
On first run, Alembic applies the database schema and the app seeds the default settings. Open http://your-host in a browser.
4. Add a Claude.ai account
- Log in with an admin account.
- Navigate to Accounts → Add Account.
- Paste the
sk-ant-sid01=…session cookie value.
How to find your session cookie:
- Open
https://claude.aiin your browser and log in. - Open DevTools (
F12) → Application tab → Cookies →https://claude.ai. - Copy the value of the cookie named
sessionKey(newer accounts — value starts withsk-ant-sid02-) orsk-ant-sid01(older accounts). Paste the value only, not the name.
The app polls immediately after the account is added.
Notification channels
Navigate to Alerts → Add Channel to configure one or more delivery targets.
| Channel | Required config |
|---|---|
| Discord | Webhook URL |
| HTTP webhook | URL, optional method (POST/GET) and JSON body template |
| Pushover | Application API token + user key |
| Matrix | Homeserver URL, access token, room ID |
HTTP webhook body templates support {title}, {message}, {account}, {pct}, and {limit_type} placeholders, making it compatible with Slack incoming webhooks, n8n, Zapier, and similar tools.
After adding a channel, use the Test button to verify delivery before creating subscriptions.
Alert subscriptions
Once you have at least one channel, go to Alerts → Subscribe to tie an account, a channel, and a set of thresholds together. Each subscription is independent — you can have one subscription sending a quiet Pushover at 75 % and a louder Discord alert at 95 %.
A 4-hour cooldown (configurable) prevents repeated alerts while usage stays near a threshold. Alerts re-arm automatically once usage drops more than 5 % below the threshold.
Runtime settings
Admins can adjust all settings at Settings without restarting the app:
| Key | Default | Description |
|---|---|---|
poll_interval_seconds |
300 | How often to poll Claude.ai (seconds) |
alert_cooldown_hours |
4 | Minimum hours between repeated alerts for the same threshold |
session_threshold_defaults |
[75,90,95] | Default session thresholds for new subscriptions |
weekly_threshold_defaults |
[75,90,95] | Default weekly thresholds for new subscriptions |
sse_keepalive_seconds |
30 | SSE heartbeat interval |
max_snapshots_per_account |
2016 | Usage history retained per account (7 days at 5-minute intervals) |
cookie_expiry_warn_days |
7 | Days before cookie expiry to begin showing warnings |
Architecture
browser ──SSE──► nginx ──proxy──► FastAPI (uvicorn, 1 worker)
│
APScheduler ──► claude.ai API
│
SQLite (WAL)
- Backend: Python 3.12, FastAPI, SQLAlchemy 2 (async), Alembic, APScheduler 3, Authlib, itsdangerous, httpx, cryptography
- Frontend: Vue 3, TypeScript, Vite, Pinia, Vue Router 4, Chart.js
- Database: SQLite with WAL mode (volume-mounted at
/app/data/claude_monitor.db) - Auth: Authentik OIDC → Authlib → itsdangerous-signed HttpOnly session cookie
Single-worker constraint: The SSE broadcaster uses in-process asyncio queues. The backend must run with
--workers 1(already set in the Dockerfile). If you need horizontal scaling, the broadcaster would need to be replaced with Redis pub/sub.
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
FERNET_KEY |
Yes | — | Fernet key for encrypting session cookies and channel configs |
SESSION_SECRET_KEY |
Yes | — | Key for signing browser session cookies |
OIDC_CLIENT_ID |
Yes | — | Authentik application client ID |
OIDC_CLIENT_SECRET |
Yes | — | Authentik application client secret |
OIDC_DISCOVERY_URL |
Yes | — | Authentik OIDC discovery endpoint URL |
APP_BASE_URL |
Yes | http://localhost |
Public base URL used to build the OIDC redirect URI |
HTTP_PORT |
No | 80 |
Host port that nginx binds to (change to avoid conflicts, e.g. 8080) |
DATABASE_URL |
No | sqlite+aiosqlite:///./data/claude_monitor.db |
SQLAlchemy database URL |
ADMIN_GROUP_NAME |
No | claude-monitor-admins |
Authentik group name that maps to the admin role |
USER_GROUP_NAME |
No | claude-monitor-users |
Authentik group name that maps to the user role |
Development
Backend
cd backend
python -m venv .venv && source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
cp ../.env.example ../.env # fill in values
mkdir -p data
alembic upgrade head
uvicorn app.main:app --reload --port 8000
Frontend
cd frontend
npm install
npm run dev # proxies /api/ to localhost:8000
Regenerating the Fernet key
If you rotate FERNET_KEY, all stored encrypted values (cookies and channel configs) become unreadable. Re-enter all account cookies and channel configs after rotating the key.
Changelog
See CHANGELOG.md.
License
MIT