明日(6/15)から、headless の claude -p や Claude Agent SDK、GitHub Actions 経由の実行が、サブスクの上限とは別枠の月次クレジットへ移ります。私が一番困ったのは「新しいプランで足りるのか」を判断する材料が手元になかったことでした。Pro の月次クレジットで収まるのか、Max まで必要なのか、感覚では分かりません。残り1日では正確な見積もりは無理でも、せめて自分の無人実行が「だいたい何トークン使っているのか」だけは数字にしておきたい。個人開発で無人実行をいくつも抱えている私自身にとって、ここはきちんと数字にしておきたいところでした。そう思って、この1週間ぶんの消費をログに残してみました。
結論から言うと、計測してみて初めて「思っていたより夜間バッチが重く、昼の単発実行はほぼ誤差」だと分かりました。プラン選びは、この内訳が見えてからのほうが圧倒的に楽です。
なぜ「合計トークン」だけでは判断を誤るのか
課金の話になると、つい「今月いくら使ったか」の総額だけを見がちです。ですが月次クレジットへ移ると、効いてくるのは総額よりも どの工程がクレジットを食っているかの内訳 です。総額が同じでも、「重い実行が少数」なのか「軽い実行が多数」なのかで、削るべき場所がまったく変わります。
特に headless 実行は、出力トークンと、プロンプトキャッシュの読み書きが見えにくいところで効いてきます。Claude の usage には input_tokens / output_tokens のほかに cache_creation_input_tokens と cache_read_input_tokens が分かれて入っており、キャッシュ読み取りは割安、キャッシュ作成は割高という非対称があります。総額だけ見ているとこの構造を見落とすので、私は工程ごと・種別ごとに分けて記録することにしました。
実行をラップして usage を1行ずつ残す
やったことはとても素朴で、headless 実行を薄いラッパーで包み、終わったら usage を JSONL に1行追記するだけです。Agent SDK を使っている工程はこのラッパー経由に差し替えました。
// usage-logger.mjs
import { appendFileSync } from "node:fs";
import { query } from "@anthropic-ai/claude-agent-sdk";
const LOG_PATH = process.env.USAGE_LOG ?? "./headless-usage.jsonl";
// job: この実行が何の工程かを表すラベル("nightly-build" など)
export async function runWithUsage(job, prompt, options = {}) {
const startedAt = new Date().toISOString();
let usage = null;
for await (const message of query({ prompt, options })) {
// SDK は最後に usage を含む result メッセージを返す
if (message.type === "result" && message.usage) {
usage = message.usage;
}
}
if (usage) {
const row = {
job,
startedAt,
finishedAt: new Date().toISOString(),
input: usage.input_tokens ?? 0,
output: usage.output_tokens ?? 0,
cacheCreate: usage.cache_creation_input_tokens ?? 0,
cacheRead: usage.cache_read_input_tokens ?? 0,
};
appendFileSync(LOG_PATH, JSON.stringify(row) + "\n");
}
return usage;
}ポイントは2つあります。1つは、job というラベルを必ず付けること。これがないと後で内訳に割れません。私は工程名(夜間ビルド・整合性チェック・記事生成など)をそのまま入れました。もう1つは、result メッセージの usage を拾うこと。ストリーミングの途中にも usage 様の情報は流れますが、確定値は最後の result に入ります。途中の値を足し込むと二重計上になるので、最後の1つだけを記録します。
JSONL(1行1レコード)にしているのは、追記が安全で壊れにくいからです。複数の cron が同時に書いても行単位なら混ざりにくく、後段の集計も for await で1行ずつ読めます。CSV だと途中で列がずれたとき全体が読めなくなりますが、JSONL は壊れた行だけ捨てれば残りは生きます。
1週間ぶんを工程別に畳む
1週間ためたら、工程ごとに合計し、推定コストまで出します。価格は MTok 単位なので、トークン数を 100 万で割って掛けるだけです(下の単価は API の入力 $10 / 出力 $50 を例にした概算で、実際の課金は契約プランのクレジット換算に従います)。
// summarize-usage.mjs
import { readFileSync } from "node:fs";
const PRICE = { input: 10, output: 50, cacheCreate: 12.5, cacheRead: 1 }; // $/MTok(例)
const rows = readFileSync(process.env.USAGE_LOG ?? "./headless-usage.jsonl", "utf8")
.split("\n")
.filter(Boolean)
.map((line) => JSON.parse(line));
const byJob = {};
for (const r of rows) {
const j = (byJob[r.job] ??= { runs: 0, input: 0, output: 0, cacheCreate: 0, cacheRead: 0 });
j.runs += 1;
j.input += r.input;
j.output += r.output;
j.cacheCreate += r.cacheCreate;
j.cacheRead += r.cacheRead;
}
const cost = (t) =>
(t.input * PRICE.input +
t.output * PRICE.output +
t.cacheCreate * PRICE.cacheCreate +
t.cacheRead * PRICE.cacheRead) /
1_000_000;
const table = Object.entries(byJob)
.map(([job, t]) => ({ job, runs: t.runs, weekUSD: +cost(t).toFixed(2), monthUSD: +(cost(t) * 30 / 7).toFixed(2) }))
.sort((a, b) => b.monthUSD - a.monthUSD);
console.table(table);
console.log("月額合計(概算) $", table.reduce((s, r) => s + r.monthUSD, 0).toFixed(2));monthUSD は「週の実績を 30/7 倍した概算」です。厳密ではありませんが、プランの上限と並べて「収まる/収まらない」を一目で判断するにはこれで十分でした。出力を console.table にすると、どの工程が重いかが表で並ぶので、削る順番がそのまま見えます。
計測して初めて見えたこと
私の場合、夜間にまとめて走る重いバッチが月額の大半を占め、昼にぽつぽつ叩く単発実行は合計しても誤差の範囲でした。感覚では「回数が多い昼の実行のほうが効いている」と思い込んでいたので、ここは完全に逆でした。頻度ではなく1回あたりの重さが効く という、言われれば当たり前のことが、自分の数字で見えたのは収穫でした。
もう1つ意外だったのは、cacheRead(キャッシュ読み取り)のトークン数が input の何倍にもなっていた工程があったことです。同じ大きな前提(リポジトリの規約やテンプレート)を毎回読み込ませている工程で、キャッシュが効いているおかげで割安に収まっていました。逆に言えば、キャッシュが効かない作り方に変えると、ここが一気に跳ねるということでもあります。プラン移行のついでにプロンプト構造をいじるなら、この欄を必ず横目で見ておきたいところです。
残り時間で何を決めたか
1週間ぶんとはいえ、内訳が数字で出れば判断はあっけないものでした。私は「重い夜間バッチを2本に絞り、頻度の高い軽い実行は当面そのまま」という線で月次クレジットの想定内に収め、過負荷時に止まらないよう Claude Code の fallbackModel を三段構成にする 設計と組み合わせることにしました。プランそのものの仕様は 6/15 の Claude Code 課金変更で、headless 実行はどう変わるのか に、月初データから先を読む手法は Claude API のトークンコストを月初3日間データから月末予測する にまとめてあります。
明日からの課金を前にできることは限られていますが、まず手元の実行に usage-logger.mjs を1枚かぶせて、今夜のバッチ1回ぶんだけでもログを取ってみてください。1行のレコードが残るだけで、プラン選びの会話は「たぶん」から「この工程が月 $◯◯」に変わります。お読みいただきありがとうございました。