A webapp that provides real-time visibility into the current usage of one or more Claude.ai accounts, and can provide alerts when the five-hour or weekly limits are being reached.
  • Python 60.6%
  • Vue 28.5%
  • TypeScript 9.3%
  • Dockerfile 0.8%
  • Mako 0.4%
  • Other 0.4%
Find a file
2026-04-06 14:28:10 -05:00
backend Fix polling reliability and alert threshold resets 2026-04-06 14:28:10 -05:00
frontend fix: alert once per threshold crossing; show hours and minutes on reset countdown 2026-03-27 18:13:23 -05:00
.env.example feat: make HTTP port configurable via HTTP_PORT env var 2026-03-26 12:38:01 -05:00
.gitignore feat: initial release of Claude Usage Monitor v1.0.0 2026-03-26 12:16:50 -05:00
CHANGELOG.md feat: make HTTP port configurable via HTTP_PORT env var 2026-03-26 12:38:01 -05:00
CLAUDE.md Created CLAUDE.md 2026-03-26 16:05:25 -05:00
docker-compose.yml feat: make HTTP port configurable via HTTP_PORT env var 2026-03-26 12:38:01 -05:00
README.md fix: support sk-ant-sid02- (sessionKey) cookie format 2026-03-26 13:23:44 -05:00

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 levelsreadonly (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 -d gets 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

  1. Create an OAuth2/OpenID Connect provider in Authentik.
  2. Set the redirect URI to http://your-host/api/auth/callback.
  3. Under Advanced protocol settings, ensure the groups scope is included in the userinfo response — this is how roles are assigned.
  4. Create two groups:
    • claude-monitor-admins → maps to the admin role
    • claude-monitor-users → maps to the user role
    • Any user in neither group receives the readonly role
  5. 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

  1. Log in with an admin account.
  2. Navigate to Accounts → Add Account.
  3. Paste the sk-ant-sid01=… session cookie value.

How to find your session cookie:

  • Open https://claude.ai in your browser and log in.
  • Open DevTools (F12) → Application tab → Cookieshttps://claude.ai.
  • Copy the value of the cookie named sessionKey (newer accounts — value starts with sk-ant-sid02-) or sk-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