Skip to content

Instantly share code, notes, and snippets.

@mvandermeulen
Forked from solrz/fetch-claude-usage.swift
Created March 19, 2026 16:51
Show Gist options
  • Select an option

  • Save mvandermeulen/f6c21d4d04d5ad0a5f83831c720a9219 to your computer and use it in GitHub Desktop.

Select an option

Save mvandermeulen/f6c21d4d04d5ad0a5f83831c720a9219 to your computer and use it in GitHub Desktop.
Claude Code Statusline - Shows context usage (C:), 5-hour API usage (S:), and weekly usage remaining (W:) with color gradients
#!/usr/bin/env swift
import Foundation
func readSessionKey() -> String? {
// TODO: Replace with your session key from claude.ai cookies
let injectedKey = "YOUR_SESSION_KEY_HERE"
let trimmedKey = injectedKey.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmedKey.isEmpty ? nil : trimmedKey
}
func readOrganizationId() -> String? {
// TODO: Replace with your organization ID from claude.ai
let injectedOrgId = "YOUR_ORG_ID_HERE"
let trimmedOrgId = injectedOrgId.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmedOrgId.isEmpty ? nil : trimmedOrgId
}
func fetchUsageData(sessionKey: String, orgId: String) async throws -> (utilization: Int, resetsAt: String?, weeklyUtilization: Int?, weeklyResetsAt: String?) {
// Build URL safely - validate orgId doesn't contain path traversal
guard !orgId.contains(".."), !orgId.contains("/") else {
throw NSError(domain: "ClaudeAPI", code: 5, userInfo: [NSLocalizedDescriptionKey: "Invalid organization ID"])
}
guard let url = URL(string: "https://claude.ai/api/organizations/\(orgId)/usage") else {
throw NSError(domain: "ClaudeAPI", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
}
var request = URLRequest(url: url)
request.setValue("sessionKey=\(sessionKey)", forHTTPHeaderField: "Cookie")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.httpMethod = "GET"
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NSError(domain: "ClaudeAPI", code: 3, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch usage"])
}
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let fiveHour = json["five_hour"] as? [String: Any],
let utilization = fiveHour["utilization"] as? Double {
let resetsAt = fiveHour["resets_at"] as? String
// Extract seven_day (weekly) data if available
var weeklyUtilization: Int? = nil
var weeklyResetsAt: String? = nil
if let sevenDay = json["seven_day"] as? [String: Any] {
if let weeklyUtil = sevenDay["utilization"] as? Double {
weeklyUtilization = Int(weeklyUtil)
}
weeklyResetsAt = sevenDay["resets_at"] as? String
}
return (Int(utilization), resetsAt, weeklyUtilization, weeklyResetsAt)
}
throw NSError(domain: "ClaudeAPI", code: 4, userInfo: [NSLocalizedDescriptionKey: "Invalid response format"])
}
// Main execution
// Use Task to run async code, RunLoop keeps script alive until exit() is called
Task {
guard let sessionKey = readSessionKey() else {
print("ERROR:NO_SESSION_KEY")
exit(1)
}
guard let orgId = readOrganizationId() else {
print("ERROR:NO_ORG_CONFIGURED")
exit(1)
}
do {
let (utilization, resetsAt, weeklyUtilization, weeklyResetsAt) = try await fetchUsageData(sessionKey: sessionKey, orgId: orgId)
// Output format: UTILIZATION|RESETS_AT|WEEKLY_UTILIZATION|WEEKLY_RESETS_AT
let resets = resetsAt ?? ""
let weeklyUtil = weeklyUtilization.map { String($0) } ?? ""
let weeklyResets = weeklyResetsAt ?? ""
print("\(utilization)|\(resets)|\(weeklyUtil)|\(weeklyResets)")
exit(0)
} catch {
print("ERROR:\(error.localizedDescription)")
exit(1)
}
}
// Keep script alive while async Task executes
RunLoop.main.run()
#!/bin/bash
config_file="$HOME/.claude/statusline-config.txt"
if [ -f "$config_file" ]; then
source "$config_file"
show_dir=$SHOW_DIRECTORY
show_branch=$SHOW_BRANCH
show_usage=$SHOW_USAGE
show_bar=$SHOW_PROGRESS_BAR
show_reset=$SHOW_RESET_TIME
show_context=$SHOW_CONTEXT
else
show_dir=1
show_branch=1
show_usage=1
show_bar=1
show_reset=1
show_context=1
fi
input=$(cat)
current_dir_path=$(echo "$input" | grep -o '"current_dir":"[^"]*"' | sed 's/"current_dir":"//;s/"$//')
current_dir=$(basename "$current_dir_path")
BLUE=$'\033[0;34m'
GREEN=$'\033[0;32m'
GRAY=$'\033[0;90m'
YELLOW=$'\033[0;33m'
RESET=$'\033[0m'
# 10-level gradient: dark green → deep red
LEVEL_1=$'\033[38;5;22m' # dark green
LEVEL_2=$'\033[38;5;28m' # soft green
LEVEL_3=$'\033[38;5;34m' # medium green
LEVEL_4=$'\033[38;5;100m' # green-yellowish dark
LEVEL_5=$'\033[38;5;142m' # olive/yellow-green dark
LEVEL_6=$'\033[38;5;178m' # muted yellow
LEVEL_7=$'\033[38;5;172m' # muted yellow-orange
LEVEL_8=$'\033[38;5;166m' # darker orange
LEVEL_9=$'\033[38;5;160m' # dark red
LEVEL_10=$'\033[38;5;124m' # deep red
# Build components (without separators)
dir_text=""
if [ "$show_dir" = "1" ]; then
dir_text="${BLUE}${current_dir}${RESET}"
fi
branch_text=""
if [ "$show_branch" = "1" ]; then
if git rev-parse --git-dir > /dev/null 2>&1; then
branch=$(git branch --show-current 2>/dev/null)
[ -n "$branch" ] && branch_text="${GREEN}${branch}${RESET}"
fi
fi
context_text=""
if [ "$show_context" = "1" ]; then
# Parse context_window.used_percentage from JSON input
context_used=$(echo "$input" | grep -o '"used_percentage":[0-9.]*' | sed 's/"used_percentage"://')
if [ -n "$context_used" ]; then
# Convert to integer for comparison (truncate decimal)
context_raw=${context_used%.*}
[ -z "$context_raw" ] && context_raw=0
# Scale to 100% (context full at 85%)
context_int=$(( context_raw * 100 / 85 ))
[ "$context_int" -gt 100 ] && context_int=100
# Select color based on usage level (same gradient as API usage)
if [ "$context_int" -le 10 ]; then
context_color="$LEVEL_1"
elif [ "$context_int" -le 20 ]; then
context_color="$LEVEL_2"
elif [ "$context_int" -le 30 ]; then
context_color="$LEVEL_3"
elif [ "$context_int" -le 40 ]; then
context_color="$LEVEL_4"
elif [ "$context_int" -le 50 ]; then
context_color="$LEVEL_5"
elif [ "$context_int" -le 60 ]; then
context_color="$LEVEL_6"
elif [ "$context_int" -le 70 ]; then
context_color="$LEVEL_7"
elif [ "$context_int" -le 80 ]; then
context_color="$LEVEL_8"
elif [ "$context_int" -le 90 ]; then
context_color="$LEVEL_9"
else
context_color="$LEVEL_10"
fi
if [ "$show_bar" = "1" ]; then
if [ "$context_int" -eq 0 ]; then
ctx_filled=0
elif [ "$context_int" -ge 100 ]; then
ctx_filled=10
else
ctx_filled=$(( (context_int * 10 + 50) / 100 ))
fi
[ "$ctx_filled" -lt 0 ] && ctx_filled=0
[ "$ctx_filled" -gt 10 ] && ctx_filled=10
ctx_empty=$((10 - ctx_filled))
ctx_bar=" "
i=0
while [ $i -lt $ctx_filled ]; do
ctx_bar="${ctx_bar}"
i=$((i + 1))
done
i=0
while [ $i -lt $ctx_empty ]; do
ctx_bar="${ctx_bar}"
i=$((i + 1))
done
else
ctx_bar=""
fi
context_text="${context_color}C: ${context_int}%${ctx_bar}${RESET}"
fi
fi
# Parse token usage and calculate cost
token_text=""
total_input=$(echo "$input" | grep -o '"total_input_tokens":[0-9]*' | sed 's/"total_input_tokens"://')
total_output=$(echo "$input" | grep -o '"total_output_tokens":[0-9]*' | sed 's/"total_output_tokens"://')
if [ -n "$total_input" ] && [ -n "$total_output" ]; then
# Format as K (thousands)
input_k=$((total_input / 1000))
output_k=$((total_output / 1000))
# Calculate cost: input $5/1M + output $25/1M
# Using cents: input * 500 / 1000000 + output * 2500 / 1000000
cost_cents=$(( (total_input * 500 + total_output * 2500) / 1000000 ))
cost_dollars=$((cost_cents / 100))
cost_remainder=$((cost_cents % 100))
cost_display=$(printf "\$%d.%02d" "$cost_dollars" "$cost_remainder")
token_text="${GRAY}${input_k}K/${output_k}K ${cost_display}${RESET}"
fi
usage_text=""
weekly_text=""
if [ "$show_usage" = "1" ]; then
swift_result=$(swift "$HOME/.claude/fetch-claude-usage.swift" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$swift_result" ]; then
utilization=$(echo "$swift_result" | cut -d'|' -f1)
resets_at=$(echo "$swift_result" | cut -d'|' -f2)
weekly_util=$(echo "$swift_result" | cut -d'|' -f3)
weekly_resets=$(echo "$swift_result" | cut -d'|' -f4)
if [ -n "$utilization" ] && [ "$utilization" != "ERROR" ]; then
if [ "$utilization" -le 10 ]; then
usage_color="$LEVEL_1"
elif [ "$utilization" -le 20 ]; then
usage_color="$LEVEL_2"
elif [ "$utilization" -le 30 ]; then
usage_color="$LEVEL_3"
elif [ "$utilization" -le 40 ]; then
usage_color="$LEVEL_4"
elif [ "$utilization" -le 50 ]; then
usage_color="$LEVEL_5"
elif [ "$utilization" -le 60 ]; then
usage_color="$LEVEL_6"
elif [ "$utilization" -le 70 ]; then
usage_color="$LEVEL_7"
elif [ "$utilization" -le 80 ]; then
usage_color="$LEVEL_8"
elif [ "$utilization" -le 90 ]; then
usage_color="$LEVEL_9"
else
usage_color="$LEVEL_10"
fi
if [ "$show_bar" = "1" ]; then
if [ "$utilization" -eq 0 ]; then
filled_blocks=0
elif [ "$utilization" -eq 100 ]; then
filled_blocks=10
else
filled_blocks=$(( (utilization * 10 + 50) / 100 ))
fi
[ "$filled_blocks" -lt 0 ] && filled_blocks=0
[ "$filled_blocks" -gt 10 ] && filled_blocks=10
empty_blocks=$((10 - filled_blocks))
# Build progress bar safely without seq
progress_bar=" "
i=0
while [ $i -lt $filled_blocks ]; do
progress_bar="${progress_bar}"
i=$((i + 1))
done
i=0
while [ $i -lt $empty_blocks ]; do
progress_bar="${progress_bar}"
i=$((i + 1))
done
else
progress_bar=""
fi
reset_time_display=""
if [ "$show_reset" = "1" ] && [ -n "$resets_at" ] && [ "$resets_at" != "null" ]; then
# Strip milliseconds and timezone (handles both Z and +00:00 formats)
iso_time=$(echo "$resets_at" | sed 's/\.[0-9]*//; s/Z$//; s/[+-][0-9][0-9]:[0-9][0-9]$//')
epoch=$(date -ju -f "%Y-%m-%dT%H:%M:%S" "$iso_time" "+%s" 2>/dev/null)
if [ -n "$epoch" ]; then
# Detect system time format (12h vs 24h) from macOS locale preferences
time_format=$(defaults read -g AppleICUForce24HourTime 2>/dev/null)
if [ "$time_format" = "1" ]; then
# 24-hour format
reset_time=$(date -r "$epoch" "+%H:%M" 2>/dev/null)
else
# 12-hour format (default)
reset_time=$(date -r "$epoch" "+%I:%M %p" 2>/dev/null)
fi
[ -n "$reset_time" ] && reset_time_display=$(printf " → Reset: %s" "$reset_time")
fi
fi
usage_text="${usage_color}S: ${utilization}%${progress_bar}${reset_time_display}${RESET}"
# Calculate weekly display if available
if [ -n "$weekly_util" ] && [ "$weekly_util" != "" ]; then
# weekly_left = 8 * (100 - weekly_util) => 800% when 0%, 0% when 100%
weekly_left=$(( 8 * (100 - weekly_util) ))
# Calculate time remaining until weekly reset
time_pct=0
if [ -n "$weekly_resets" ] && [ "$weekly_resets" != "" ]; then
# Strip milliseconds and timezone (handles both Z and +00:00 formats)
weekly_iso=$(echo "$weekly_resets" | sed 's/\.[0-9]*//; s/Z$//; s/[+-][0-9][0-9]:[0-9][0-9]$//')
weekly_epoch=$(date -ju -f "%Y-%m-%dT%H:%M:%S" "$weekly_iso" "+%s" 2>/dev/null)
if [ -n "$weekly_epoch" ]; then
now_epoch=$(date "+%s")
seconds_left=$((weekly_epoch - now_epoch))
[ "$seconds_left" -lt 0 ] && seconds_left=0
minutes_left=$((seconds_left / 60))
# time_pct = minutes_left * 800 / 10080 (1440 * 7 = 10080 minutes per week)
time_pct=$((minutes_left * 800 / 10080))
[ "$time_pct" -gt 800 ] && time_pct=800
fi
fi
# Color based on weekly_left (0-800 scale, map to 0-100 for color selection)
weekly_color_level=$((weekly_left / 8))
if [ "$weekly_color_level" -ge 90 ]; then
weekly_color="$LEVEL_1"
elif [ "$weekly_color_level" -ge 80 ]; then
weekly_color="$LEVEL_2"
elif [ "$weekly_color_level" -ge 70 ]; then
weekly_color="$LEVEL_3"
elif [ "$weekly_color_level" -ge 60 ]; then
weekly_color="$LEVEL_4"
elif [ "$weekly_color_level" -ge 50 ]; then
weekly_color="$LEVEL_5"
elif [ "$weekly_color_level" -ge 40 ]; then
weekly_color="$LEVEL_6"
elif [ "$weekly_color_level" -ge 30 ]; then
weekly_color="$LEVEL_7"
elif [ "$weekly_color_level" -ge 20 ]; then
weekly_color="$LEVEL_8"
elif [ "$weekly_color_level" -ge 10 ]; then
weekly_color="$LEVEL_9"
else
weekly_color="$LEVEL_10"
fi
weekly_text="${weekly_color}W: ${weekly_left}%:${time_pct}%${RESET}"
fi
else
usage_text="${YELLOW}S: ~${RESET}"
fi
else
usage_text="${YELLOW}S: ~${RESET}"
fi
fi
output=""
separator="${GRAY}${RESET}"
[ -n "$dir_text" ] && output="${dir_text}"
if [ -n "$branch_text" ]; then
[ -n "$output" ] && output="${output}${separator}"
output="${output}${branch_text}"
fi
if [ -n "$context_text" ]; then
[ -n "$output" ] && output="${output}${separator}"
output="${output}${context_text}"
fi
if [ -n "$token_text" ]; then
[ -n "$output" ] && output="${output}${separator}"
output="${output}${token_text}"
fi
if [ -n "$usage_text" ]; then
[ -n "$output" ] && output="${output}${separator}"
output="${output}${usage_text}"
fi
if [ -n "$weekly_text" ]; then
[ -n "$output" ] && output="${output}${separator}"
output="${output}${weekly_text}"
fi
printf "%s\n" "$output"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment