pven

Homelab notes on Claude, automation, and self-hosted tools.

If you run Claude Code heavily, you've probably stared at the usage bar wondering how much of your 5-hour block is gone — and whether your extra credits are quietly draining. I built a small bash script to stop guessing and start logging.

The problem

Claude Pro has three usage dimensions worth tracking:

  • 5-hour block utilisation — the rolling window that gates your active sessions
  • 7-day utilisation — the broader weekly cap
  • Extra credits — the paid overflow, billed in euros

None of this is exposed in a machine-readable way by default. It turns out there is an undocumented OAuth endpoint that exposes exactly that data.

The endpoint

Claude Code stores an OAuth access token in ~/.claude/.credentials.json. The same token works against the usage API:

GET https://api.anthropic.com/api/oauth/usage
Authorization: Bearer <token>
anthropic-beta: oauth-2025-04-20

The response looks like this:

{
  "five_hour": { "utilization": 7.0, "resets_at": "..." },
  "seven_day": { "utilization": 19.0, "resets_at": "..." },
  "extra_usage": {
    "is_enabled": true,
    "monthly_limit": 1700,
    "used_credits": 190.0,
    "utilization": 11.18,
    "currency": "EUR"
  }
}

Note: monthly_limit and used_credits are in eurocents. Divide by 100 for the actual euro amounts.

The script

The script reads the token, hits the endpoint, optionally queries ccusage blocks for remaining block time, and appends a single JSON line to a log file — ready for Loki to scrape and Grafana to visualise.

#!/usr/bin/env bash
# Dependencies: jq, curl, ccusage (npm install -g ccusage)

LOGFILE="/var/log/ccusage.log"
CREDENTIALS_FILE="${HOME}/.claude/.credentials.json"
ANTHROPIC_USAGE_URL="https://api.anthropic.com/api/oauth/usage"

log() { echo "[$(date '+%d-%b-%Y %H:%M:%S')] $*" >&2; }

TOKEN=$(jq -r '.claudeAiOauth.accessToken // empty' "$CREDENTIALS_FILE" 2>/dev/null)
if [[ -z "$TOKEN" ]]; then
    log "No credentials available, aborting"
    exit 1
fi

USAGE=$(curl -s \
    -H "Authorization: Bearer $TOKEN" \
    -H "anthropic-beta: oauth-2025-04-20" \
    "$ANTHROPIC_USAGE_URL")

if ! echo "$USAGE" | jq -e '.five_hour' > /dev/null 2>&1; then
    log "Invalid API response"
    exit 1
fi

BLOK=$(echo "$USAGE"         | jq '(.five_hour.utilization   // 0) | round')
WEEK=$(echo "$USAGE"         | jq '(.seven_day.utilization   // 0) | round')
CREDITS_PCT=$(echo "$USAGE"  | jq '(.extra_usage.utilization // 0) * 100 | round / 100')
CREDITS_USED=$(echo "$USAGE" | jq '(.extra_usage.used_credits  // 0) / 100')
CREDITS_MAX=$(echo "$USAGE"  | jq '(.extra_usage.monthly_limit // 0) / 100')

REMAINING=$(ccusage blocks --json 2>/dev/null | jq '
    if (.blocks // []) | length > 0
       and (.blocks[-1].projection.remainingMinutes // null) != null
    then (.blocks[-1].projection.remainingMinutes / 60 * 10 | round / 10)
    else 0
    end' 2>/dev/null || echo 0)

printf '{"timestamp":"%s","blok_pct":%s,"week_pct":%s,"credits_pct":%s,"credits_used":%s,"credits_max":%s,"blok_remaining_hours":%s}\n' \
    "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
    "$BLOK" "$WEEK" "$CREDITS_PCT" "$CREDITS_USED" "$CREDITS_MAX" "$REMAINING" \
    >> "$LOGFILE"

log "ccusage logged: block=${BLOK}% week=${WEEK}% credits=${CREDITS_PCT}%"

Install

Dependencies:

apt install jq curl
npm install -g ccusage

Deploy:

cp ccusage_log.sh /usr/local/sbin/ccusage_log
chmod +x /usr/local/sbin/ccusage_log

Cron — every 5 minutes:

*/5 * * * * /usr/local/sbin/ccusage_log

Output

Each run appends one line to /var/log/ccusage.log:

{"timestamp":"2026-05-23T19:26:17Z","blok_pct":7,"week_pct":19,"credits_pct":11.18,"credits_used":1.9,"credits_max":17,"blok_remaining_hours":0}

Point a Loki scrape job at that file and you have time-series data for all six metrics. Setting up Loki and Grafana for this is a topic for a separate post.

Notes

  • Tested on Claude Pro with Claude Code. May work with other Claude subscriptions, but I haven't verified.
  • The OAuth endpoint is undocumented and could change without notice.
  • blok_remaining_hours is 0 when no active session block is running — that's expected.
  • The full script with the latest version is available on GitHub: pven/scripts/claude

Written with assistance from Claude.


Want to reach out? Contact me.