Status line Claude Code : afficher sa consommation API en temps réel
- Publié le
- ·10 min de lecture·Mis à jour le 9 mars 2026
Le problème : combien de quota reste-t-il vraiment ?
Claude Code c'est cool, mais franchement? Aucune visibilité sur ma consommation. Tu lances une session, tu discutes 30 minutes, et puis: DING! "Limite d'utilisation atteinte." Vexant.
Je voulais une vue en temps réel de ma quota. Pas juste un nombre - un vrai dashboard: modèle actuel, contexte utilisé, limites 5h et 7j, temps avant reset. Et tout ça dans une status line élégante en bas du terminal.
Après des semaines de tests, j'ai construit quelque chose qui marche. Voici.
Architecture : une status line dynamique
Claude Code a une feature appelée statusLine qui exécute une commande shell custom. Cette commande:
- Reçoit du JSON en stdin (session, contexte, modèle)
- Peut appeler l'API Anthropic pour la vraie consommation
- Retourne du texte ANSI coloré
Simple, mais puissant.
Configuration : activer la status line
D'abord, dans ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 0
}
}
Ce que ça fait :
type: "command": Exécute un script shell (pas du texte statique)command: Chemin du script (rends-le exécutable:chmod +x)padding: 0: Pas d'espace supplémentaire (j'aime avoir beaucoup de texte)
Le script : ~/.claude/statusline.sh
Voici le script complet. Bash avec du caching intelligent:
#!/bin/bash
# Status line script pour Claude Code
# Affiche : modèle + contexte + API usage en temps réel
# Reçoit les données en JSON sur stdin
CACHE_FILE="$HOME/.claude/usage_cache.json"
CACHE_TTL=600 # 10 minutes (l'API a un rate limit strict)
# Couleurs ANSI
INDIGO="\033[38;5;54m"
CYAN="\033[38;5;51m"
VIOLET="\033[38;5;141m"
MAGENTA="\033[38;5;201m"
RESET="\033[0m"
BOLD="\033[1m"
# Lire les données JSON depuis stdin
input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name // "Unknown"')
context_pct=$(echo "$input" | jq -r '.context_window.used_percentage // 0')
work_dir=$(echo "$input" | jq -r '.workspace.current_dir // "~"' | xargs basename)
# Fonction : retourner la couleur selon le pourcentage
get_color() {
local pct=$1
if (( $(echo "$pct < 50" | bc -l) )); then
echo -e "$CYAN"
elif (( $(echo "$pct < 80" | bc -l) )); then
echo -e "$VIOLET"
else
echo -e "$MAGENTA"
fi
}
# Fonction : afficher une barre de progression (5 blocs)
progress_bar() {
local pct=$1
local blocks=5
local filled=$(( pct / 20 ))
[[ $filled -gt $blocks ]] && filled=$blocks
local bar=""
for (( i=0; i<filled; i++ )); do
bar+="▰"
done
for (( i=filled; i<blocks; i++ )); do
bar+="▱"
done
echo "$bar"
}
# Fonction : formatter l'heure
format_time() {
local seconds=$1
local hours=$(( seconds / 3600 ))
local minutes=$(( (seconds % 3600) / 60 ))
echo "${hours}h${minutes}m"
}
# Fonction : récupérer l'usage API
get_api_usage() {
# Vérifier le cache (basé sur le timestamp dans le JSON)
if [[ -f "$CACHE_FILE" ]]; then
local cache_time=$(jq -r '.timestamp // 0' "$CACHE_FILE" 2>/dev/null)
local cache_age=$(( $(date +%s) - cache_time ))
if [[ $cache_age -lt $CACHE_TTL ]]; then
cat "$CACHE_FILE"
return 0
fi
fi
# Appel API Anthropic
local token=$(jq -r '.claudeAiOauth.accessToken // empty' "$HOME/.claude/.credentials.json" 2>/dev/null)
if [[ -z "$token" ]]; then
echo '{"five_hour": 0, "seven_day": 0, "reset_time": ""}'
return 1
fi
# Vérifier le code HTTP pour détecter les erreurs (429, 401, etc.)
local http_response=$(curl -s -w "\n%{http_code}" --max-time 5 \
-H "Authorization: Bearer $token" \
-H "anthropic-beta: oauth-2025-04-20" \
"https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
local http_code=$(echo "$http_response" | tail -1)
local response=$(echo "$http_response" | sed '$d')
if [[ "$http_code" != "200" ]] || [[ -z "$response" ]]; then
# Erreur API (rate limit, etc.) : réutiliser l'ancien cache
if [[ -f "$CACHE_FILE" ]]; then
# Prolonger le TTL pour éviter de re-tenter trop vite
local old_data=$(cat "$CACHE_FILE")
echo "$old_data" | jq --arg ts "$(date +%s)" '.timestamp = ($ts | tonumber)' > "$CACHE_FILE"
cat "$CACHE_FILE"
else
echo '{"five_hour": 0, "seven_day": 0, "reset_time": ""}'
fi
return 1
fi
# Extraire les valeurs
local five_h=$(echo "$response" | jq -r '.five_hour.utilization // 0')
local seven_d=$(echo "$response" | jq -r '.seven_day.utilization // 0')
local reset_ts=$(echo "$response" | jq -r '.five_hour.resets_at // ""')
local result='{"five_hour": '"$five_h"', "seven_day": '"$seven_d"', "reset_time": "'"$reset_ts"'", "timestamp": '"$(date +%s)"'}'
echo "$result" > "$CACHE_FILE"
echo "$result"
}
# Récupérer l'usage
usage=$(get_api_usage)
five_h=$(echo "$usage" | jq -r '.five_hour // 0')
seven_d=$(echo "$usage" | jq -r '.seven_day // 0')
reset_time=$(echo "$usage" | jq -r '.reset_time // ""')
# Calculer le temps avant reset
time_until_reset=""
if [[ -n "$reset_time" && "$reset_time" != "null" ]]; then
reset_epoch=$(date -d "$reset_time" +%s 2>/dev/null || echo 0)
now_epoch=$(date +%s)
diff=$(( reset_epoch - now_epoch ))
if [[ $diff -gt 0 ]]; then
time_until_reset=$(format_time $diff)
fi
fi
# Construire la status line
status=""
# 1. Modèle (bold indigo)
status+="${BOLD}${INDIGO}◆${RESET} "
status+="${model}"
status+=" │ "
# 2. Contexte utilisé
ctx_color=$(get_color "$context_pct")
ctx_bar=$(progress_bar "$context_pct")
status+="${ctx_color}Ctx: ${ctx_bar} ${context_pct}%${RESET} │ "
# 3. Temps avant reset (si disponible)
if [[ -n "$time_until_reset" ]]; then
status+="${INDIGO}⏱ ${time_until_reset}${RESET} │ "
fi
# 4. Usage 5h
five_color=$(get_color "$five_h")
five_bar=$(progress_bar "$five_h")
status+="${five_color}5h: ${five_bar} ${five_h}%${RESET} │ "
# 5. Usage 7j
seven_color=$(get_color "$seven_d")
seven_bar=$(progress_bar "$seven_d")
status+="${seven_color}7d: ${seven_bar} ${seven_d}%${RESET} │ "
# 6. Répertoire courant
status+="${INDIGO}⌂ ${work_dir}${RESET}"
echo -e "$status"
Comment ça marche : décortiquons le script
Lecture des données
input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name // "Unknown"')
context_pct=$(echo "$input" | jq -r '.context_window.used_percentage // 0')
work_dir=$(echo "$input" | jq -r '.workspace.current_dir // "~"' | xargs basename)
Claude Code envoie un JSON avec le contexte. On extrait:
- Modèle ("Claude Opus 4.6")
- Pourcentage contexte utilisé (0-100)
- Répertoire courant
Appel à l'API Anthropic
get_api_usage() {
local token=$(jq -r '.claudeAiOauth.accessToken // empty' "$HOME/.claude/.credentials.json" 2>/dev/null)
local http_response=$(curl -s -w "\n%{http_code}" --max-time 5 \
-H "Authorization: Bearer $token" \
-H "anthropic-beta: oauth-2025-04-20" \
"https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
local http_code=$(echo "$http_response" | tail -1)
local response=$(echo "$http_response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
# Rate limit ou erreur : réutiliser l'ancien cache
return 1
fi
local five_h=$(echo "$response" | jq -r '.five_hour.utilization // 0')
local seven_d=$(echo "$response" | jq -r '.seven_day.utilization // 0')
local reset_ts=$(echo "$response" | jq -r '.five_hour.resets_at // ""')
}
Quatre choses importantes:
Token d'authentification : stocké dans
~/.claude/.credentials.jsonsous la cléclaudeAiOauth.accessToken(généré par Claude Code à la connexion). OAuth token.Header spécial :
anthropic-beta: oauth-2025-04-20- API "beta" OAuth. Toujours d'actualité avec Claude Code 2.1.x.Vérification du code HTTP :
curl -w "\n%{http_code}"pour récupérer le status. Sans ça,curlretourne exit code 0 même sur un 429 (rate limit) et on finit par cacher des valeurs à 0.Les vraies limites:
five_hour.utilization: pourcentage sur les 5h (0-100)seven_day.utilization: pourcentage sur les 7j (0-100)five_hour.resets_at: timestamp ISO quand la window 5h reset
Caching intelligent
local cache_time=$(jq -r '.timestamp // 0' "$CACHE_FILE" 2>/dev/null)
local cache_age=$(( $(date +%s) - cache_time ))
if [[ $cache_age -lt $CACHE_TTL ]]; then
cat "$CACHE_FILE"
return 0
fi
L'API est appelée une fois toutes les 10 minutes max (CACHE_TTL=600). C'est important: l'endpoint /api/oauth/usage a un rate limit strict. Avec un TTL trop court (60s par exemple), on se fait rate-limit en boucle et on finit avec des valeurs à 0%.
Cache stocké dans ~/.claude/usage_cache.json avec un timestamp dans le JSON. Point crucial: en cas d'erreur API (429, timeout...), on réutilise l'ancien cache au lieu de l'écraser avec des zéros. Sans ça, un seul rate limit suffit à afficher 0% jusqu'au prochain appel réussi.
Barres de progression
progress_bar() {
local pct=$1
local filled=$(( pct / 20 ))
local bar=""
for (( i=0; i<filled; i++ )); do
bar+="▰"
done
for (( i=filled; i<5; i++ )); do
bar+="▱"
done
echo "$bar"
}
Chaque barre = 5 blocs, donc chaque bloc = 20%.
▰▰▱▱▱ = 40%
▰▰▰▱▱ = 60%
▰▰▰▰▰ = 100%
Codage couleur par seuil
get_color() {
local pct=$1
if (( $(echo "$pct < 50" | bc -l) )); then
echo -e "$CYAN" # Vert : safe
elif (( $(echo "$pct < 80" | bc -l) )); then
echo -e "$VIOLET" # Violet : warning
else
echo -e "$MAGENTA" # Rose : danger
fi
}
- < 50% : Cyan (tranquille)
- 50-80% : Violet (commence à être serré)
80% : Magenta (alerte rouge!)
Exemple de sortie
Voici ce qu'on voit au bas du terminal :
◆ Claude Opus 4.6 │ Ctx: ▰▰▱▱▱ 35% │ ⏱ 3h42m │ 5h: ▰▰▰▱▱ 52% │ 7d: ▰▱▱▱▱ 18% │ ⌂ fransys-blog
Décodage:
- ◆ Claude Opus 4.6 : Je suis sur le modèle full Opus
- Ctx: ▰▰▱▱▱ 35% : J'ai utilisé 35% de ma fenêtre de contexte (bon, pas saturé)
- ⏱ 3h42m : Reset dans 3h42m
- 5h: ▰▰▰▱▱ 52% : 52% de ma limite 5h utilisée
- 7d: ▰▱▱▱▱ 18% : 18% de ma limite 7j utilisée (très confortable)
- ⌂ fransys-blog : Je suis dans le répertoire fransys-blog
À ce moment, beaucoup de marge. On continue.
Customisation : adapter les couleurs
Codes de couleur ANSI:
\033[38;5;54m= Indigo (ma couleur primaire)\033[38;5;51m= Cyan\033[38;5;141m= Violet\033[38;5;201m= Magenta
Les 256 couleurs ANSI varient selon le terminal. Teste d'abord:
for i in {0..255}; do echo -e "\033[38;5;${i}m█\033[0m"; done
Tu verras visuellement chaque code. Modifie INDIGO, CYAN, etc. selon tes préférences.
Dépannage courant
La status line n'apparaît pas:
- Vérifie que le script est exécutable:
chmod +x ~/.claude/statusline.sh - Teste à la main:
echo '{"model": {"display_name": "test"}, "context_window": {"used_percentage": 50}}' | ~/.claude/statusline.sh
Les valeurs API sont à 0:
- Vérifie le token:
jq '.claudeAiOauth.accessToken' ~/.claude/.credentials.json - Si vide, réauthentifie:
claude auth login - Test l'API manuellement pour vérifier qu'elle ne rate-limit pas:
TOKEN=$(jq -r '.claudeAiOauth.accessToken' ~/.claude/.credentials.json)
curl -s -w "\nHTTP:%{http_code}" \
-H "Authorization: Bearer $TOKEN" \
-H "anthropic-beta: oauth-2025-04-20" \
"https://api.anthropic.com/api/oauth/usage"
Si tu vois HTTP:429, c'est un rate limit. Attends quelques minutes et augmente CACHE_TTL. Voir la section "Le piège du rate limit" ci-dessous.
Le piège du rate limit (0% permanent):
Le problème le plus courant. L'endpoint /api/oauth/usage a un rate limit strict. Si ton script l'appelle trop souvent:
- L'API retourne HTTP 429 (rate limit)
curlretourne quand même exit code 0 (la connexion a réussi)jqparse la réponse d'erreur,.five_hour.utilizationn'existe pas → fallback à0- Le
0est mis en cache → affiché pendant tout le TTL - Cache expire → on re-tente → encore rate-limited → boucle infinie de 0%
La solution: vérifier le code HTTP (curl -w "%{http_code}"), ne jamais cacher les erreurs, et garder un TTL assez long (600s minimum)
La couleur est pas celle attendue:
- Le terminal n'a peut-être pas 256 couleurs. Test:
echo $TERM. Doit direxterm-256colorou mieux. - Vieux terminal? Remplace les codes
38;5;XXpar les classiques:31(rouge),32(vert), etc.
Cas d'usage avancés
Logger la consommation:
echo "$(date) - 5h: $(echo "$usage" | jq '.five_hour')% | 7d: $(echo "$usage" | jq '.seven_day')%" >> ~/.claude/usage.log
Ajoute ça à ton hook Stop pour un historique.
Alertes:
if [[ $five_h -gt 85 ]]; then
# Envoyer une notification
notify-send "Claude Code" "5h usage at ${five_h}% - slow down!"
fi
Utile si tu es sur une limite serrée.
Plus d'infos:
# Ajouter le modèle des sub-agents
status+=" [subagent: ${CYAN}haiku${RESET}]"
Voilà
Cette status line a transformé ma relation à Claude Code. Plus de peur du vide: je vois exactement où j'en suis, combien de temps avant reset, et je peux décider intelligemment ("10 000 thinking tokens sur ce problème ou juste une réflexion simple?").
Le script n'est pas parfait - pourrait être plus robuste, utiliser un langage plus rapide que bash - mais c'est un bon départ. Copie-le, adapte les couleurs à ton terminal, et ajuste les seuils d'alerte.
Article précédent
← Gérer ses photos avec Immich en auto-hébergé