Skip to content

Skulldorom/usage-dashboard

Repository files navigation

Usage Dashboard logo

Usage Dashboard

Self-hosted API usage dashboard for Firecrawl, DeepSeek, OpenAI, Anthropic/Claude, OpenRouter, and custom HTTP usage endpoints. It stores provider credentials encrypted at rest, polls usage/balance APIs, renders a dark React/MUI dashboard, and exposes a flat Homepage Dashboard endpoint.

Stack

  • Backend: FastAPI, SQLAlchemy async, asyncpg, Alembic, cryptography/Fernet
  • Frontend: Vite, React, MUI, React Router
  • Runtime: PostgreSQL, nginx-based frontend/proxy image, Docker Compose

Usage Dashboard screenshot

Quick start

cp .env.example .env
openssl rand -base64 32 | tr '+/' '-_'
# paste that value into ENCRYPTION_KEY in .env
# generate an admin token and paste it into ADMIN_TOKEN in .env
# optional if you only expose /api/v1/homepage through HOMEPAGE_ALLOWED_HOSTS
openssl rand -hex 32
docker compose pull
docker compose up -d

Open through the frontend container, which also proxies API traffic to the backend:

Set NGINX_HTTP_PORT in .env to change the external HTTP port. PostgreSQL is intentionally internal-only and is not published on the host. The default Compose stack pulls published images from GitHub Container Registry; set IMAGE_TAG, BACKEND_IMAGE, or FRONTEND_IMAGE to pin or override them.

Configuration

Variable Description
DATABASE_URL Async SQLAlchemy URL. Defaults to the Compose PostgreSQL service.
POSTGRES_IMAGE PostgreSQL image used by Compose. Defaults to postgres:18-alpine; the Compose volume is mounted at /var/lib/postgresql for the 18+ image layout.
ENCRYPTION_KEY Required Fernet key used to encrypt API credentials at rest.
ADMIN_TOKEN Optional bearer token for dashboard config, usage, polling, and homepage API routes. Keep it set for the admin UI; it may be blank only when HOMEPAGE_ALLOWED_HOSTS is used for homepage-only access behind an external auth proxy.
HOMEPAGE_ALLOWED_HOSTS Comma-separated hostnames that may access GET /api/v1/homepage without ADMIN_TOKEN; ports are ignored. All other API routes still require admin auth.
IMAGE_TAG Tag for the default GHCR backend/frontend images. Defaults to latest.
BACKEND_IMAGE Optional full backend image override. Defaults to ghcr.io/skulldorom/usage-dashboard-backend:${IMAGE_TAG}.
FRONTEND_IMAGE Optional full frontend image override. Defaults to ghcr.io/skulldorom/usage-dashboard-frontend:${IMAGE_TAG}.
NGINX_HTTP_PORT Host port published by the frontend/proxy container. Defaults to 3000.
BACKEND_CORS_ORIGINS Comma-separated allowed origins for the FastAPI API.
VITE_API_BASE_URL Frontend API base path baked into the published frontend image. Defaults to /api.
AUTO_POLL_ENABLED Enables background provider polling. Defaults to true.
AUTO_POLL_INTERVAL_MINUTES Minutes between automatic provider polls. Defaults to 60.

Providers

Firecrawl

Uses GET https://api.firecrawl.dev/v2/team/credit-usage for current remaining/plan credits and GET https://api.firecrawl.dev/v2/team/credit-usage/historical for usage consumed in the billing period.

DeepSeek

Uses GET https://api.deepseek.com/user/balance.

OpenAI

Uses GET https://api.openai.com/v1/organization/costs. This endpoint requires an organization admin key

Anthropic / Claude

Uses GET https://api.anthropic.com/v1/organizations/usage_report/messages for the last 24 hours. This requires an Anthropic Admin API key from Console → Settings → Organization → Admin API Keys, not a normal inference key.

OpenRouter

Uses GET https://openrouter.ai/api/v1/key with the same bearer token used for inference and surfaces remaining, daily, weekly, and monthly credit usage.

Custom HTTP

Choose Custom HTTP in Settings, provide a base URL, relative path, auth header template, and one JSON path metric. The encrypted API key is inserted into the auth header template via {api_key}. Do not put secrets in the base URL or path; the backend rejects credential-looking URLs.

Example metric config generated by the form:

{
  "method": "GET",
  "path": "/v1/billing",
  "auth_header_name": "Authorization",
  "auth_header_template": "Bearer {api_key}",
  "metrics": [
    {"label": "remaining", "path": "$.credits.remaining", "unit": "credits", "maximum_path": "$.credits.limit"}
  ]
}

Homepage Dashboard widget

The Compose stack no longer has a separate nginx service. The frontend service is the nginx-based static frontend and API proxy. Use the URL that matches where Homepage is running:

  • Same Docker Compose project/network: http://frontend/api/v1/homepage
  • Separate Compose project on a shared Docker network: connect Homepage to the Usage Dashboard network and use http://frontend/api/v1/homepage, or add a network alias such as usage-dashboard and use http://usage-dashboard/api/v1/homepage
  • Host/LAN access through the published port: http://<server-ip-or-dns>:${NGINX_HTTP_PORT:-3000}/api/v1/homepage
  • Public reverse-proxy access: https://usage.example.com/api/v1/homepage

If you use HOMEPAGE_ALLOWED_HOSTS, include the hostname that reaches the frontend/proxy and is forwarded to the backend. For internal Docker calls that is usually frontend or your network alias; for public access it is your external hostname.

Two widget formats are supported. Pick one.

Option A -- Block display (scalar fields)

The default block display shows individual fields as labelled rows. Use this for a compact summary tile:

- API Usage:
    icon: mdi-api
    widget:
      type: customapi
      url: http://frontend/api/v1/homepage
      # Optional when HOMEPAGE_ALLOWED_HOSTS includes frontend.
      # headers:
      #   Authorization: Bearer ***
      refreshInterval: 300000
      mappings:
        - field: summary
          label: Providers
        - field: configured_providers
          label: Configured
        - field: healthy_providers
          label: Healthy
        - field: degraded_providers
          label: Degraded

Flattened metrics keys (e.g. firecrawl_main_credits_remaining, deepseek_main_total_balance) are also available as extra field mappings.

Option B -- Dynamic list (one row per provider)

Requires Homepage ≥ 1.1.0. Set display: dynamic-list and use the object-style mappings below. Each enabled provider config becomes a row with its label on the left and usage text on the right.

display: dynamic-list is mandatory -- omitting it causes TypeError: s.slice is not a function because Homepage tries to treat the object-style mappings as a block-display array.

- API Usage:
    icon: mdi-api
    widget:
      type: customapi
      url: http://frontend/api/v1/homepage
      display: dynamic-list
      # Optional when HOMEPAGE_ALLOWED_HOSTS includes frontend.
      # headers:
      #   Authorization: Bearer ***
      refreshInterval: 300000
      mappings:
        items: list
        name: label
        label: value
        format: text

The list array contains one flat object per enabled provider config:

  • label → left side (e.g. deepseek (main))
  • value → right side (prefers remaining credits/usage, then percent-used, then summary fallback)

The existing scalar fields (summary, configured_providers, healthy_providers, degraded_providers) and flattened metrics object remain in the response for use with Option A or extra mappings.

Public homepage behind reverse-proxy auth

Set HOMEPAGE_ALLOWED_HOSTS when a trusted proxy such as Authentik protects the public hostname and you only want the flat homepage payload to be readable without sharing ADMIN_TOKEN:

ADMIN_TOKEN=
HOMEPAGE_ALLOWED_HOSTS=usage.example.com,status.local

Only GET /api/v1/homepage checks this allowlist. /configs, /poll, /usage, and history endpoints still return 401 without a valid admin bearer token. Hostnames are matched case-insensitively and any port suffix is ignored.

Development

cd backend
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
export ENCRYPTION_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
pytest
cd frontend
npm install
npm run dev
npm run build

To build local images instead of using GHCR, run explicit builds with the Dockerfiles:

docker build -t usage-dashboard-backend:local -f backend/Dockerfile .
docker build -t usage-dashboard-frontend:local -f frontend/Dockerfile .
BACKEND_IMAGE=usage-dashboard-backend:local FRONTEND_IMAGE=usage-dashboard-frontend:local docker compose up -d

About

A dashboard that lets users register and see thier current usage for multiple providers at once

Resources

License

Stars

2 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors