Fransys

Blog technique — Architecture, Cloud & DevOps

BlogServicesContactÀ propos

Suivez-moi

githubGitHublinkedinLinkedinmailMail

© 2026 Fransys • Fransys

Fransys

Catégories

  • Tous les articles
  • Tags
  • productivite10
  • nas10
  • ia8
  • securite7
  • linux6
  • claude-code6
  • auto-hebergement6
  • neovim5
  • docker5
  • editeur4
  • mcp3
  • vpn3
  • reseau3
  • lua2
  • terminal2
claude-codeiaproductivitebash

Status line Claude Code : afficher sa consommation API en temps réel

Publié le
27 février 2026·10 min de lecture·Mis à jour le 9 mars 2026
Avatar François GUERLEZFrançois GUERLEZ

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:

  1. Reçoit du JSON en stdin (session, contexte, modèle)
  2. Peut appeler l'API Anthropic pour la vraie consommation
  3. 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:

  1. Token d'authentification : stocké dans ~/.claude/.credentials.json sous la clé claudeAiOauth.accessToken (généré par Claude Code à la connexion). OAuth token.

  2. Header spécial : anthropic-beta: oauth-2025-04-20 - API "beta" OAuth. Toujours d'actualité avec Claude Code 2.1.x.

  3. Vérification du code HTTP : curl -w "\n%{http_code}" pour récupérer le status. Sans ça, curl retourne exit code 0 même sur un 429 (rate limit) et on finit par cacher des valeurs à 0.

  4. 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:

  1. L'API retourne HTTP 429 (rate limit)
  2. curl retourne quand même exit code 0 (la connexion a réussi)
  3. jq parse la réponse d'erreur, .five_hour.utilization n'existe pas → fallback à 0
  4. Le 0 est mis en cache → affiché pendant tout le TTL
  5. 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 dire xterm-256color ou mieux.
  • Vieux terminal? Remplace les codes 38;5;XX par 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é

Article suivant

Monitoring NAS : surveiller disques, services et logs en un coup d'oeil→
← Retour au blog

Sommaire

  • Le problème : combien de quota reste-t-il vraiment ?
  • Architecture : une status line dynamique
  • Configuration : activer la status line
  • Le script : ~/.claude/statusline.sh
  • Comment ça marche : décortiquons le script
  • Lecture des données
  • Appel à l'API Anthropic
  • Caching intelligent
  • Barres de progression
  • Codage couleur par seuil
  • Exemple de sortie
  • Customisation : adapter les couleurs
  • Dépannage courant
  • Cas d'usage avancés
  • Voilà