Max Nardit
← 記事
11 mindev

Claude Code のステータスラインと ccusage:ネイティブのレート制限と、別の時刻に終わる 5 時間ブロック

2 つのツールが同じ問いに別の根拠で答えます。サーバーはリセット時刻を報告し、ログパーサーはそれを推定します。数時間ずれたらサーバーを信じ、パーサーは履歴用に残してください。サブスクリプションでは、ドル金額は為替レートのようなものです。

目次

Claude Code のステータスラインは、以前はモデル名と Git ブランチを表示する場所でした。stdin に rate_limits オブジェクトが届くようになってからは、Pro や Max のサブスクライバーに、作業を止める 2 つの数値も表示できます。5 時間ウィンドウと 7 日間ウィンドウをどれだけ使ったか、そしてそれぞれがいつリセットされるかです。それ以前に書かれたガイドは ccusage を勧めています。ccusage は、ディスク上の JSONL トランスクリプトからコストと独自の 5 時間ブロックを計算します。これは別の測定であって、同じ数値を別の経路で出しているわけではありません。

どちらも役に立ちますが、測っているものは同じではありません。Claude Code が送ってきた内容をすべて記録するステータスラインで対話型のセッションを 1 つ実行し、その入力をそのまま ccusage statusline に渡しました。Claude Code は、5 時間ウィンドウが 04:30 UTC にリセットされると報告しました。ccusage は 03:58 UTC の時点で、自分の 5 時間ブロックは残り 3 時間 1 分だと報告しました。同じマシン、同じ 1 分のなかで、約 2 時間半の差です。以下では、各フィールドが何をカバーするか、使ったスクリプト、そして両者がずれうる理由を説明します。

Claude Code のステータスラインはどんな使用量データを受け取るのか

ラインが更新されるたびに stdin で JSON オブジェクトを受け取ります。使用量に関係するのは rate_limits、context_window、cost、prompt_cache の 4 グループです。プランを表すのは最初の 1 つだけで、残りは現在のセッションを表します。

ステータスラインのドキュメントによると、rate_limits.five_hour と rate_limits.seven_day はそれぞれ used_percentage(0〜100)と、Unix エポック秒の resets_at を持ちます。このセッションでは整数で届きました。context_window はモデルのウィンドウに対する used_percentage(入力トークンだけから計算)と context_window_size を返します。ここでは Haiku 5.5 で 1,000,000 だったので、「7%」は約 65,000 トークンを意味していました。cost.total_cost_usd はセッション単位の推定値です。prompt_cache オブジェクトはヒット率、ミス、キャッシュが温まっているかを報告します。/usage が表示するのと同じデータで、プロンプトキャッシュを壊した原因を探しているときに注目すべきフィールドです。

ラインはセッション開始時に実行され、ほかにもいくつかのタイミングで再実行されます。新しいアシスタントメッセージが届いたとき、/compact の後、直近の入力にある rate_limits のウィンドウが resets_at に達したとき、温まったプロンプトキャッシュが expires_at に達したとき、そして refreshInterval を設定していればその周期ごとです。更新には 300 ms のデバウンスがかかります。2.1.302 の stdin には、ドキュメントの表に載っていないキー scratchpad_dir が 1 つ含まれていました。これに依存するのは避けてください。

Claude Code の rate_limits フィールドが欠けるのはいつか

各セッションの最初の実行時、サブスクリプションも利用上限付きのゲートウェイも使っていない場合、そしてウィンドウごとに、リセット時刻を過ぎたときです。ドキュメントによると、rate_limits が現れるのは「only for claude.ai Pro and Max subscribers, or behind a Claude apps gateway that sets a spend limit for you, and only after the first API response in the session」で、Claude Code は resets_at を過ぎたウィンドウを落とします。

ログにあった起動時の実行は、その説明とぴったり一致していました。

first run:   rate_limits absent, prompt_cache absent, context_window.used_percentage null, cost 0
after reply: rate_limits.five_hour 4% (resets 04:30 UTC), seven_day 45%, context 6%

つまり、rate_limits.five_hour に直接アクセスするスクリプトは起動時にクラッシュし、空のステータスラインしか表示されません。Claude Code は、スクリプトが終了コード 0 で終わったときだけ出力を表示するからです。欠けたり null になったりしうるドキュメント上のフィールドには、すべてフォールバックが必要です。

ネイティブのフィールドから Claude Code のステータスラインを作るには

stdin を 1 回読み、1 行を出力し、使用量のフィールドは省略可能なものとして扱います。ドキュメントのレート制限の例は 2 つのパーセンテージを表示します。ここで使うスクリプトは Python 3 の標準ライブラリだけで書いてあり、各ウィンドウの残り時間とコンテキストの値を加えています。この記事の残りで扱うのは、まさにこれらの数値です。

python
#!/usr/bin/env python3
import json, sys, time
 
data = json.load(sys.stdin)
parts = [(data.get("model") or {}).get("display_name", "?")]
 
ctx = (data.get("context_window") or {}).get("used_percentage")
parts.append(f"ctx {ctx:.0f}%" if ctx is not None else "ctx --")
 
now = time.time()
for key, label in (("five_hour", "5h"), ("seven_day", "7d")):
    win = (data.get("rate_limits") or {}).get(key) or {}
    pct, reset = win.get("used_percentage"), win.get("resets_at")
    if pct is None or reset is None or reset <= now:
        continue
    left = int(reset - now)
    if left >= 86400:
        eta = f"{left // 86400}d{left % 86400 // 3600}h"
    else:
        eta = f"{left // 3600}h{left % 3600 // 60:02d}m"
    parts.append(f"{label} {pct:.0f}% ({eta})")
 
print(" | ".join(parts))

~/.claude/statusline.py として保存し、chmod +x で実行可能にして、~/.claude/settings.json に次の内容をマージします。

json
{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.py",
    "refreshInterval": 60
  }
}

refreshInterval はカウントダウンのためにあります。これがないとラインはイベントのときにしか再実行されず、アイドル状態のセッションは最後のメッセージ時点の残り時間を表示し続けます。記録した 5 つのペイロードのうち最初と最後を 04:07 UTC に再生すると、スクリプトの出力は次のとおりでした。

Haiku 5.5 | ctx --
Haiku 5.5 | ctx 7% | 5h 7% (0h22m) | 7d 45% (3d16h)

信頼する前に、同じ方法でテストしてください。1 セッションの間だけ command を tee -a ~/.claude/statusline-input.jsonl | ~/.claude/statusline.py にして、メッセージをいくつか送り、元のコマンドに戻してから、tail -1 ~/.claude/statusline-input.jsonl | ~/.claude/statusline.py で任意の行を再生します。ここで期限切れのウィンドウを飛ばす処理が効いてきます。再生したペイロードのリセット時刻は過去になっているからです。ドキュメントからもう 1 点。信頼していないフォルダでは、信頼ダイアログを承認するまでステータスラインは空のままです。フックと同じルールでシェルコマンドを実行するためです。

ccusage statusline は Claude Code 自身のフィールドに何を加えるのか

履歴です。ccusage はローカルのトランスクリプトを読み、Claude Code が stdin に載せない 3 つの数値を加えます。全セッションをまたいだ今日の合計、現在の 5 時間ブロックのコストとその残り時間、そしてバーンレートです。03:58 UTC に --cost-source both で表示したラインは次のとおりでした(当日分とブロック分のドル金額は置き換えてあります)。

🤖 Haiku 5.5 (medium) | 💰 ($0.01 cc / $0.01 ccusage) session / $X today / $Y block (3h 1m left) | 🔥 $Z/hr | 🧠 64,531 (6%)

セッションの金額は、デフォルトでは ccusage 自身の計算ではありません。デフォルトの --cost-source auto では Claude Code の cost.total_cost_usd を表示し、そのフィールドがないときだけ自分で計算します。ccusage を指定すると自前の計算を強制し、both は両方を並べて表示します。価格はキャッシュされた LiteLLM の表から取得され(デフォルトはオフライン)、出力のキャッシュはデフォルトで有効、有効期限は 1 秒です。これは Claude Code の refreshInterval とは無関係です。ページではこの機能はベータと表示されています。bun x ccusage@20.0.35 statusline として実行しました。ドキュメントに載っている別の方法は npx -y ccusage statusline です。

ccusage がまったく表示しないのは 7 日間のパーセンテージです。ローカルのログには週の割り当て量を知る手がかりがないので、その分母がありません。

ccusage の 5 時間ブロックは Claude Code の five_hour ウィンドウと一致するのか

今回の確認では一致しませんでした。両者はウィンドウが終わる時刻で食い違っていて、サブスクライバーが計画の基準にするのはまさにその時刻です。

03:58 UTC   Claude Code  rate_limits.five_hour.resets_at = 04:30 UTC  (32 min left)
03:58 UTC   ccusage      block: 1h 58m elapsed, 3h 1m remaining       (ends about 07:00 UTC)

この実行では ccusage のブロックは 02:00 に始まっていて、テストセッションが始まる 2 時間近く前です。つまり、このマシン上の別のアクティビティをすでに数えていたことになります。Blocks レポートのドキュメントは、ブロックを「Triggered by your first message」で「Lasts exactly 5 hours from start time」と説明し、次のように主張しています。

Matches Claude's internal session tracking

なぜずれたのかは追跡していませんし、このテストでは 2 つの候補を切り分けられません。1 つ目は、継続的に使っていると、ある時点を起点にした 5 時間ウィンドウの連なりと、別の時点を起点にした連なりは、両方がすべてのリクエストを見ていても、ずっとずれたままになるという可能性です。2 つ目は、ccusage は自分のデータディレクトリにあるトランスクリプトしか見ないのに対し、サーバーはほかのデバイスや claude.ai からの使用量も含めてアカウントを計測しているという可能性です。いずれにせよ、この実行では、ブロックのドル金額は Anthropic が計測していたのとは別の 5 時間を対象にしていて、その「残り時間」は 2 時間半ずれていました。1 台のマシンでの 1 回の確認では、これがどれくらいの頻度で起きるかはわかりません。それでも、食い違ったときにどちらの数値を信じるべきかはわかります。resets_at はサーバー自身の答えです。

Claude Code と ccusage のセッションコストは一致するのか

近いものの同じではなく、サブスクライバーにとってはどちらも請求額ではありません。同じセッションについて、Claude Code は total_cost_usd を 0.01257 と報告し、ccusage session はトランスクリプトから 0.01149 と計算しました。ccusage のほうが約 9% 低い値です。どちらもライン上では $0.01 と表示されたので、--cost-source both ではまったく差が見えませんでした。

この差は追跡していません。候補は、価格表の違いか、トランスクリプトに残らないリクエストです。コストのページは、プロンプトの提案や会話の要約といったバックグラウンドのリクエストに触れています。ドキュメントがはっきりさせているのは意味のほうです。Claude Code はこの値を「locally from token counts at list price」で計算し、Pro と Max のサブスクライバーについては「the session cost figure isn't relevant for billing purposes」としています。サブスクリプションでは、ドル金額は為替レートのようなもので、API 換算の支出を表しています。セッション同士を比べたり、ある作業が API クレジットでいくらかかるかを知ったりするには役立ちますが、いつ制限で止められるかを知るためのものではありません。

Claude Code の使用量の数値のうち、アカウント全体のものとローカルのものはどれか

アカウント全体を表すのは rate_limits だけです。それ以外はすべて範囲が狭くなります。

rate_limits.five_hour / seven_day   the account, as the server sees it
cost.total_cost_usd                  this session, list price, reset by /clear
context_window                       this session's last API response
ccusage today / block / burn rate    local transcripts, filtered to the day or block
/usage plan breakdown                local session history on this machine

コストのページによると、/usage の内訳は「computed from local session history on this machine, so usage from other devices or claude.ai is not included」です。ccusage も構造上ローカルです。この区別が最も効いてくるのは、多数のセッションを同時に動かしているときです。各セッションに届く rate_limits のパーセンテージは、すべて同じ割り当て量を表しています。セッションごとのコストとコンテキストはそのセッション 1 つについて教えてくれるだけで、アカウントが上限にどれだけ近いかについては何も教えてくれません。Anthropic のヘルプセンターによると、Pro と Max の制限は Claude と Claude Code で共有されています。チャットの使用量も rate_limits を動かしますが、ccusage も /usage の内訳もそれを見ていません。

ネイティブの stdin には、モデル別のウィンドウもありません。プランに Sonnet 専用の週次制限があっても、ステータスラインには表示できません。そのすき間をブラウザ側で埋めるのが Claude Usage Deck です。claude.ai の使用量制限を読み取り、セッション、週次、Sonnet 週次のメーターを表示します。

Pro や Max プランで Claude Code のステータスラインに何を表示すべきか

rate_limits の 2 つのパーセンテージとそのリセット時刻、そしてコンテキストです。次に何をするかを左右するのはこれらの数値です。長いタスクを今始めるか 04:30 まで待つか、いつ /compact するか。ドル金額をラインに載せるのは、トークン単位で支払っている場合だけにしてください。その場合でも推定値で、リスト価格か、管理者が modelPricing を設定していれば契約価格に基づきます。

ccusage は残しておきましょう。ただし得意なことに使います。ccusage claude weekly --last 1 --mode calculate は「このマシンの Claude Code トランスクリプトが今週 API 価格でどれだけ使ったか」に答えます(--start-of-week と --timezone を渡さない限り、週はシステムのタイムゾーンで日曜日に始まります)。ある作業をそもそもサブスクリプションで回すべきかを判断するときには、これは本当に必要な問いです。Anthropic がプログラムからの利用を別に計測する計画の背景にあったのも同じ問いでした。ログにできないのは、サーバーのウィンドウがどこで終わるかを教えることです。その答えは、すでに stdin に届いています。

01 / ディスカッション

コメント欄はありません。議論は X で行っています。

Max Nardit@mnardit

02 / ほかの記事

  1. claude -p での Claude Code フォークサブエージェント:2.1.302 がヘッドレス実行で変えたこと

    提供されることと使われることは別です。ヘッドレス実行のモデルはフォークを一度も選ばないかもしれず、公式ドキュメントはまだ古いデフォルトを説明しています。パイプラインがどのサブエージェントを使ったかの確認方法、バックグラウンドのフォークがパーサーに2つの回答を渡す理由、そして無効にする価値がある場面を解説します。

  2. Claude Code vs Cursor: どちらのツールも 1 つの利用環境に縛られなくなった今、開発者が仕事を分ける 7 つの方法

    エディタ対ターミナルという古い境界線は、両ベンダーがそれぞれの側から消しました。それでも 2 つを分けているものは、もっと目立たないところにあります。Cursor は Claude Code の設定フォルダを毎回そのまま読み、Claude Code は Cursor のルールを一度だけ取り込みます。そして権限の仕組みは、互いの deny ルールを無視します。

  3. AI エージェントにとっての MCP と API:API カタログはディレクトリであり、選択を決めるのは間に誰が入るか

    API カタログは MCP サーバーと競合しません。MCP サーバーを掲載できる側です。そこを押さえると、MCP か OpenAPI で記述した HTTP API かの選択は、ホストアプリケーションがモデルのためにサービスを組み込むかどうか、そして誰かがすでにそのサービスを組み込むと決めているかどうかにかかってきます。