Created
February 22, 2026 09:44
-
-
Save omachala/5ea5af4bfa0b194a1d48d6f2eedd6274 to your computer and use it in GitHub Desktop.
Claude Code Usage CLI - check your session, weekly, and extra usage limits from the terminal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Claude Code Usage CLI | |
| # Fetches usage data from Anthropic API and displays it in a readable format | |
| # | |
| # Setup: | |
| # 1. Make sure you're logged into Claude Code (credentials stored in macOS Keychain) | |
| # 2. If you're behind a firewall, configure PROXY_PORT and SSH_HOST below | |
| # 3. chmod +x claude-usage && ./claude-usage | |
| # | |
| # Optional: alias usage="~/path/to/claude-usage" | |
| set -e | |
| # --- Configuration --- | |
| # If you need a SOCKS5 proxy to reach api.anthropic.com, set these. | |
| # Leave SSH_HOST empty to connect directly without a proxy. | |
| PROXY_PORT=1080 | |
| SSH_HOST="" # e.g. "user@your-server.com" | |
| SSH_PORT=22 | |
| USE_PROXY=false | |
| # --------------------- | |
| # Start SSH SOCKS5 proxy if configured and not running | |
| start_proxy() { | |
| if [[ "$USE_PROXY" != "true" ]] || [[ -z "$SSH_HOST" ]]; then | |
| return | |
| fi | |
| if ! nc -z localhost $PROXY_PORT 2>/dev/null; then | |
| ssh -o ConnectTimeout=5 -D $PROXY_PORT -f -N $SSH_HOST -p$SSH_PORT 2>/dev/null || { | |
| echo "Error: Could not establish SSH tunnel to $SSH_HOST" >&2 | |
| exit 1 | |
| } | |
| sleep 1 | |
| fi | |
| } | |
| # Get token from Keychain (macOS) | |
| get_token() { | |
| local creds | |
| creds=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null) || { | |
| echo "Error: Could not retrieve Claude Code credentials from Keychain" >&2 | |
| echo "Make sure you're logged into Claude Code" >&2 | |
| exit 1 | |
| } | |
| echo "$creds" | jq -r '.claudeAiOauth.accessToken' | |
| } | |
| # Fetch usage from API | |
| fetch_usage() { | |
| local token="$1" | |
| local curl_args=(-s) | |
| if [[ "$USE_PROXY" == "true" ]] && [[ -n "$SSH_HOST" ]]; then | |
| curl_args+=(--proxy "socks5h://localhost:$PROXY_PORT") | |
| fi | |
| curl "${curl_args[@]}" "https://api.anthropic.com/api/oauth/usage" \ | |
| -H "Authorization: Bearer $token" \ | |
| -H "anthropic-beta: oauth-2025-04-20" \ | |
| -H "User-Agent: claude-code/2.0.32" | |
| } | |
| # Format ISO date to readable format | |
| format_iso_date() { | |
| local iso_date="$1" | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| date -j -f "%Y-%m-%dT%H:%M:%S" "${iso_date%%.*}" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$iso_date" | |
| else | |
| date -d "$iso_date" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$iso_date" | |
| fi | |
| } | |
| # Convert ISO to epoch | |
| iso_to_epoch() { | |
| local iso_date="$1" | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| date -j -f "%Y-%m-%dT%H:%M:%S" "${iso_date%%.*}" "+%s" 2>/dev/null || echo "0" | |
| else | |
| date -d "$iso_date" "+%s" 2>/dev/null || echo "0" | |
| fi | |
| } | |
| # Calculate week start from reset date (7 days before) | |
| calc_week_start() { | |
| local reset_iso="$1" | |
| if [[ "$OSTYPE" == "darwin"* ]]; then | |
| local reset_epoch | |
| reset_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "${reset_iso%%.*}" "+%s" 2>/dev/null) || { echo "$reset_iso"; return; } | |
| local start_epoch=$((reset_epoch - 604800)) | |
| date -r "$start_epoch" "+%Y-%m-%d %H:%M" | |
| else | |
| date -d "$reset_iso - 7 days" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$reset_iso" | |
| fi | |
| } | |
| # Draw progress bar | |
| progress_bar() { | |
| local pct="$1" | |
| local width=30 | |
| local filled=$(echo "scale=0; $pct * $width / 100" | bc) | |
| [[ $filled -gt $width ]] && filled=$width | |
| local empty=$((width - filled)) | |
| printf "[" | |
| printf "%${filled}s" | tr ' ' '=' | |
| printf "%${empty}s" | tr ' ' ' ' | |
| printf "]" | |
| } | |
| # Main | |
| main() { | |
| local token usage | |
| start_proxy | |
| token=$(get_token) | |
| usage=$(fetch_usage "$token") | |
| if echo "$usage" | jq -e '.error' > /dev/null 2>&1; then | |
| echo "API Error: $(echo "$usage" | jq -r '.error.message // .error')" >&2 | |
| exit 1 | |
| fi | |
| # Extract data | |
| local five_hour_pct five_hour_reset seven_day_pct seven_day_reset | |
| five_hour_pct=$(echo "$usage" | jq -r '.five_hour.utilization // 0') | |
| five_hour_reset=$(echo "$usage" | jq -r '.five_hour.resets_at // ""') | |
| seven_day_pct=$(echo "$usage" | jq -r '.seven_day.utilization // 0') | |
| seven_day_reset=$(echo "$usage" | jq -r '.seven_day.resets_at // ""') | |
| # Extract extra usage (pay-as-you-go) - API returns cents, convert to dollars | |
| local extra_enabled extra_limit_cents extra_used_cents extra_pct extra_limit extra_used | |
| extra_enabled=$(echo "$usage" | jq -r '.extra_usage.is_enabled // false') | |
| extra_limit_cents=$(echo "$usage" | jq -r '.extra_usage.monthly_limit // 0') | |
| extra_used_cents=$(echo "$usage" | jq -r '.extra_usage.used_credits // 0') | |
| extra_pct=$(echo "$usage" | jq -r '.extra_usage.utilization // 0') | |
| extra_limit=$(echo "scale=2; $extra_limit_cents / 100" | bc) | |
| extra_used=$(echo "scale=2; $extra_used_cents / 100" | bc) | |
| # Calculate pace info | |
| local now_epoch reset_epoch start_epoch | |
| now_epoch=$(date +%s) | |
| reset_epoch=$(iso_to_epoch "$seven_day_reset") | |
| start_epoch=$((reset_epoch - 604800)) | |
| local elapsed_seconds time_pct expected_pct diff | |
| elapsed_seconds=$((now_epoch - start_epoch)) | |
| time_pct=$(echo "scale=1; $elapsed_seconds * 100 / 604800" | bc) | |
| expected_pct="$time_pct" | |
| diff=$(echo "scale=1; $seven_day_pct - $expected_pct" | bc) | |
| # Days/hours remaining | |
| local remaining_seconds days_remaining hours_remaining | |
| remaining_seconds=$((reset_epoch - now_epoch)) | |
| days_remaining=$((remaining_seconds / 86400)) | |
| hours_remaining=$(( (remaining_seconds % 86400) / 3600 )) | |
| echo "" | |
| echo "Claude Code Usage" | |
| echo "─────────────────────────────────────────────" | |
| echo "" | |
| # Session (5-hour) | |
| echo "Session (5-hour window)" | |
| printf " Usage: %s%% " "$five_hour_pct" | |
| progress_bar "$five_hour_pct" | |
| echo "" | |
| echo " Resets: $(format_iso_date "$five_hour_reset")" | |
| echo "" | |
| # Weekly (7-day) | |
| echo "Weekly (7-day window)" | |
| printf " Usage: %s%% " "$seven_day_pct" | |
| progress_bar "$seven_day_pct" | |
| echo "" | |
| echo " Period: $(calc_week_start "$seven_day_reset") -> $(format_iso_date "$seven_day_reset")" | |
| echo " Time left: ${days_remaining}d ${hours_remaining}h" | |
| echo "" | |
| # Extra usage (pay-as-you-go) | |
| if [[ "$extra_enabled" == "true" ]]; then | |
| echo "Extra Usage (Monthly)" | |
| printf " Used: \$%.2f / \$%s (%s%%)\n" "$extra_used" "$extra_limit" "$extra_pct" | |
| progress_bar "$extra_pct" | |
| echo "" | |
| echo "" | |
| fi | |
| # Pace status | |
| echo "Pace" | |
| printf " Expected: %s%% (linear) | Actual: %s%%\n" "$expected_pct" "$seven_day_pct" | |
| if (( $(echo "$diff > 5" | bc -l) )); then | |
| printf " Status: AHEAD by %s%% - consider slowing down\n" "$diff" | |
| elif (( $(echo "$diff < -5" | bc -l) )); then | |
| local behind | |
| behind=$(echo "scale=1; $diff * -1" | bc) | |
| printf " Status: BEHIND by %s%% - you have headroom\n" "$behind" | |
| else | |
| echo " Status: ON TRACK" | |
| fi | |
| echo "" | |
| # JSON output option | |
| if [[ "$1" == "--json" ]]; then | |
| echo "Raw API Response:" | |
| echo "$usage" | jq | |
| fi | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://gist.github.com/bdavidzhang/23c1448fa2c798e76d2145a689036c0e
made some quick UI updates and quality of life check that encourages the user to refresh their token if it's not showing up.