Skip to content

Instantly share code, notes, and snippets.

@kvaps
Last active January 20, 2026 15:29
Show Gist options
  • Select an option

  • Save kvaps/84fa5963df1bff9cec65b57afd54e1e4 to your computer and use it in GitHub Desktop.

Select an option

Save kvaps/84fa5963df1bff9cec65b57afd54e1e4 to your computer and use it in GitHub Desktop.
Claude Code statusline with API usage limits (fixed bunx -> ccusage) [forked from https://gist.github.com/lexfrei/b70aaee919bdd7164f2e3027dc8c98de]

Claude Code Statusline with Real Usage Limits

Enhanced statusline for Claude Code that shows real quota usage from Anthropic API instead of estimates.

Features

  • 🟢🟡🟠🔴 Rate indicators — compares usage% with elapsed time%
  • 5-hour window — always shown with time remaining
  • 7-day window — shown only when ≥70% used
  • 60-second cache — avoids excessive API calls
  • Timezone-aware — works correctly in any timezone

Example Output

🤖 Opus 4.5 | 💰 $2.53 session / $46.11 today | 🔥 $7.79/hr | 🧠 112k (56%) | 🟠 7d: 73% (2d 5h) | 🟢 5h: 30% (3h 32m)

Statusline Blocks Explained

Block Meaning
🤖 Opus 4.5 Current model being used
💰 $2.53 session / $46.11 today Estimated cost for current session and total today (based on token prices)
🔥 $7.79/hr Current burn rate — how fast you're spending
🧠 112k (56%) Context window usage — tokens used and percentage of max context
🟠 7d: 73% (2d 5h) 7-day quota usage with rate indicator and time until reset (only shown ≥70%)
🟢 5h: 30% (3h 32m) 5-hour quota usage with rate indicator and time until reset

Rate Indicator Logic

Indicator Meaning
🟢 On track or under (usage ≤ time elapsed)
🟡 Slightly ahead (1-5% over)
🟠 Ahead (6-15% over)
🔴 Way ahead, will likely hit limit (>15% over)

The rate indicator compares your usage percentage with time elapsed percentage in the current window. For example, if 50% of the 5-hour window has passed and you've used 45% of quota — you're on track (🟢). If you've used 70% — you're way ahead (🔴).

Installation

  1. Save statusline-usage.sh to ~/.claude/hooks/:
mkdir -p ~/.claude/hooks
# Download from this gist
chmod +x ~/.claude/hooks/statusline-usage.sh
  1. Add to ~/.claude/settings.json:
{
  "statusLine": {
    "type": "command",
    "command": "$HOME/.claude/hooks/statusline-usage.sh",
    "padding": 0
  }
}
  1. Restart Claude Code session.

Dependencies

  • ccusage — install via npm install -g ccusage or brew install ccusage
  • jq — JSON parsing
  • curl — API requests
  • macOS Keychain with Claude Code credentials

How It Works

  1. Receives JSON input from Claude Code statusline hook
  2. Passes it through ccusage statusline for base info (model, costs, context)
  3. Fetches real usage data from https://api.anthropic.com/api/oauth/usage
  4. Calculates rate indicators by comparing usage% with time elapsed%
  5. Caches API response for 60 seconds

Credits

License

BSD-3-Clause

#!/bin/bash
# Fetch real usage limits from Anthropic API and combine with ccusage statusline
set -euo pipefail
export PATH="/opt/homebrew/bin:$PATH"
# Cache settings
CACHE_FILE="/tmp/claude-usage-cache.json"
CACHE_TTL=60 # seconds
# Get cached or fresh usage data
get_usage() {
local now
now=$(date +%s)
# Check cache
if [[ -f "$CACHE_FILE" ]]; then
local cache_time
cache_time=$(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0)
if (( now - cache_time < CACHE_TTL )); then
cat "$CACHE_FILE"
return
fi
fi
# Get credentials from Keychain
local creds token
creds=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null) || return 1
token=$(echo "$creds" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null) || return 1
if [[ -z "$token" ]]; then
return 1
fi
# Fetch usage from API
local response
response=$(curl --silent --max-time 5 \
--header "Authorization: Bearer $token" \
--header "anthropic-beta: oauth-2025-04-20" \
"https://api.anthropic.com/api/oauth/usage" 2>/dev/null) || return 1
# Cache response
echo "$response" > "$CACHE_FILE"
echo "$response"
}
# Calculate time remaining (in minutes) from ISO timestamp (UTC)
time_remaining_mins() {
local reset_at=$1
local now reset_ts diff
now=$(date +%s)
# Parse ISO timestamp as UTC
# Remove microseconds and timezone suffix, parse in UTC
local ts_clean="${reset_at%%.*}"
ts_clean="${ts_clean//T/ }" # Replace T with space
reset_ts=$(TZ=UTC date -j -f "%Y-%m-%d %H:%M:%S" "$ts_clean" +%s 2>/dev/null) || return 1
diff=$((reset_ts - now))
echo $(( diff / 60 ))
}
# Format minutes to human readable
format_time() {
local mins=$1
if (( mins <= 0 )); then
echo "now"
return
fi
local days hours minutes
days=$((mins / 1440))
hours=$(((mins % 1440) / 60))
minutes=$((mins % 60))
if (( days > 0 )); then
echo "${days}d ${hours}h"
elif (( hours > 0 )); then
echo "${hours}h ${minutes}m"
else
echo "${minutes}m"
fi
}
# Get rate indicator based on usage% vs time elapsed%
# Usage: rate_indicator <usage%> <time_remaining_mins> <total_window_mins>
rate_indicator() {
local usage=$1
local remaining_mins=$2
local total_mins=$3
# Calculate time elapsed percentage
local elapsed_mins=$((total_mins - remaining_mins))
if (( elapsed_mins < 0 )); then elapsed_mins=0; fi
local time_pct
if (( total_mins > 0 )); then
time_pct=$((elapsed_mins * 100 / total_mins))
else
time_pct=0
fi
# Compare usage% with time%
local diff=$((usage - time_pct))
if (( diff <= 0 )); then
echo "🟢" # On track or under
elif (( diff <= 5 )); then
echo "🟡" # Slightly ahead
elif (( diff <= 15 )); then
echo "🟠" # Ahead
else
echo "🔴" # Way ahead, will likely hit limit
fi
}
# Main - receives JSON from Claude Code via stdin
main() {
# Read stdin (Claude Code passes JSON)
local input
input=$(cat)
# Get base statusline from ccusage (pass through the input)
local base
base=$(echo "$input" | ccusage statusline --offline --visual-burn-rate off 2>/dev/null) || base=""
# Remove fields not useful on subscription (ccusage doesn't have options for these)
base=$(echo "$base" | sed -E 's/ *\([0-9]+h [0-9]+m left\)//g; s| */ *\$[0-9]+\.[0-9]+ block||g')
# Get usage data from API
local usage five_hour seven_day
usage=$(get_usage 2>/dev/null) || usage=""
if [[ -n "$usage" ]]; then
five_hour=$(echo "$usage" | jq -r '.five_hour.utilization // empty' 2>/dev/null)
five_hour_resets=$(echo "$usage" | jq -r '.five_hour.resets_at // empty' 2>/dev/null)
seven_day=$(echo "$usage" | jq -r '.seven_day.utilization // empty' 2>/dev/null)
seven_day_resets=$(echo "$usage" | jq -r '.seven_day.resets_at // empty' 2>/dev/null)
local quota_info=""
# 7d only if >= 70%
if [[ -n "$seven_day" ]]; then
local seven_day_int
seven_day_int=$(printf "%.0f" "$seven_day")
if (( seven_day_int >= 70 )); then
local seven_day_remaining_mins seven_day_indicator seven_day_time_str
seven_day_remaining_mins=$(time_remaining_mins "$seven_day_resets" 2>/dev/null) || seven_day_remaining_mins=0
seven_day_indicator=$(rate_indicator "$seven_day_int" "$seven_day_remaining_mins" 10080) # 7d = 10080 mins
seven_day_time_str=$(format_time "$seven_day_remaining_mins")
quota_info+="${seven_day_indicator} 7d: ${seven_day_int}% (${seven_day_time_str})"
fi
fi
# 5h always shown
if [[ -n "$five_hour" ]]; then
local five_hour_int five_hour_remaining_mins five_hour_indicator five_hour_time_str
five_hour_int=$(printf "%.0f" "$five_hour")
five_hour_remaining_mins=$(time_remaining_mins "$five_hour_resets" 2>/dev/null) || five_hour_remaining_mins=0
five_hour_indicator=$(rate_indicator "$five_hour_int" "$five_hour_remaining_mins" 300) # 5h = 300 mins
five_hour_time_str=$(format_time "$five_hour_remaining_mins")
[[ -n "$quota_info" ]] && quota_info+=" | "
quota_info+="${five_hour_indicator} 5h: ${five_hour_int}% (${five_hour_time_str})"
fi
if [[ -n "$quota_info" ]]; then
echo "${base} | ${quota_info}"
else
echo "$base"
fi
else
echo "$base"
fi
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment