手帳の8月31日のところに丸をつけていました。Sonnet 5 の導入価格が終わる日、という意味です。
9月からの請求見込みを立て直すつもりで、直近の usage を並べ、単価を 3ドル / 15ドル に置き換えた表を作っていました。個人開発の規模では月あたり数十ドルの差でも無視できません。数字が思ったより大きかったので、念のため一次情報を見に行ったのです。
公式の価格ページには、こう書かれていました。導入価格として案内していた 2ドル / 10ドル が、そのまま標準価格になった。9月1日に予定していた引き上げは行われない。
私は7月に、この引き上げを前提にした記事を書いています。前提そのものが消えたわけです。作り直した表は使えなくなりましたが、価格表を一行ずつ読み直したおかげで、それまで見落としていた注記に気づきました。今回はその話をします。
いま有効な単価を、自分の目で確認する
まず事実関係です。2026年8月23日時点の公式価格表は次のとおりです。単位はすべて100万トークンあたりの米ドルです。
| モデル | 入力 | 5分キャッシュ書き込み | キャッシュ読み取り | 出力 |
|---|---|---|---|---|
| Claude Fable 5 | $10 | $12.50 | $1 | $50 |
| Claude Opus 5 | $5 | $6.25 | $0.50 | $25 |
| Claude Sonnet 5 | $2 | $2.50 | $0.20 | $10 |
| Claude Sonnet 4.6 | $3 | $3.75 | $0.30 | $15 |
| Claude Haiku 4.5 | $1 | $1.25 | $0.10 | $5 |
キャッシュの単価は入力単価から機械的に決まります。5分キャッシュの書き込みが入力の1.25倍、1時間キャッシュの書き込みが2倍、読み取り(ヒット)が0.1倍です。Batch API は入力と出力が半額になり、inference_geo を "us" に固定した場合は全カテゴリが1.1倍になります。
数字は変わりますので、判断の前には必ず Claude Platform の Pricing ページ を開いてください。私自身、この確認を月次の作業に入れていませんでした。7月の見込みを外したのは、そこが原因です。
手元の usage から実効コストを出す
価格表を眺めるより、自分の usage を入れて計算したほうが早いです。API のレスポンスに入っている usage は、次の4つの数字を返します。
input_tokens— キャッシュに載らなかった入力cache_creation_input_tokens— キャッシュへ書き込んだ入力cache_read_input_tokens— キャッシュから読んだ入力output_tokens— 生成された出力
この4つを別々に集計しておくと、あとで効きます。次のスクリプトは、その4つから内訳つきで費用を出します。
M = 1_000_000
# tokenizer: "new" = Claude 4.7 以降、"old" = Claude Sonnet 4.6 以前
RATES = {
"sonnet-5": {"in": 2.0, "write5m": 2.50, "read": 0.20, "out": 10.0, "tokenizer": "new"},
"opus-5": {"in": 5.0, "write5m": 6.25, "read": 0.50, "out": 25.0, "tokenizer": "new"},
"sonnet-4-6": {"in": 3.0, "write5m": 3.75, "read": 0.30, "out": 15.0, "tokenizer": "old"},
"haiku-4-5": {"in": 1.0, "write5m": 1.25, "read": 0.10, "out": 5.0, "tokenizer": "old"},
}
def breakdown(model, usage, batch=False, us_only=False):
"""内訳つきで費用を返す。usage は API レスポンスの usage と同じキー名。"""
r = RATES[model]
mult = (0.5 if batch else 1.0) * (1.1 if us_only else 1.0)
items = {
"uncached_input": usage.get("input_tokens", 0) * r["in"],
"cache_write": usage.get("cache_creation_input_tokens", 0) * r["write5m"],
"cache_read": usage.get("cache_read_input_tokens", 0) * r["read"],
"output": usage.get("output_tokens", 0) * r["out"],
}
items = {k: v * mult / M for k, v in items.items()}
items["total"] = sum(items.values())
return items
if __name__ == "__main__":
daily = { # 自分の usage 合計に置き換えてください
"input_tokens": 120_000,
"cache_creation_input_tokens": 800_000,
"cache_read_input_tokens": 4_200_000,
"output_tokens": 260_000,
}
b = breakdown("sonnet-5", daily)
for k in ("uncached_input", "cache_write", "cache_read", "output", "total"):
print(f" {k:<15} ${b[k]:.4f} / 日 ${b[k] * 30:7.2f} / 30日")手元で実行すると、こう出ます。
uncached_input $0.2400 / 日 $ 7.20 / 30日
cache_write $2.0000 / 日 $ 60.00 / 30日
cache_read $0.8400 / 日 $ 25.20 / 30日
output $2.6000 / 日 $ 78.00 / 30日
total $5.6800 / 日 $ 170.40 / 30日
合計だけを出さずに内訳を分けているのには理由があります。打ち手が完全に別だからです。
出力が最大なら、削るべきは max_tokens や返答フォーマットです。キャッシュ書き込みが最大なら、キャッシュの区切り位置か、リクエストの間隔が5分の有効期限を超えている可能性を疑います。キャッシュ読み取りが最大なら、それはむしろ健全な形で、単価の0.1倍が効いている証拠です。合計だけを見ていると、この三つが区別できません。
ちなみに同じ数字で 3ドル / 15ドル を仮定すると、30日で 255.60ドル でした。引き上げが行われないことで、この例では月あたり 85.20ドル の差になります。
単価表だけでモデルを並べると、見込みが外れます
価格表を読み直していて足が止まったのは、表の下にある短い注記でした。
Claude 4.7 以降のモデルは新しいトークナイザを使っており、同じ文章でもおよそ30%多いトークンになる、という内容です。Claude Sonnet 4.6 以前は従来のトークナイザのままです。
つまり Sonnet 5 と Opus 5 は新しい数え方、Sonnet 4.6 と Haiku 4.5 は従来の数え方をしています。この二つを 100万トークンあたりの単価だけで並べると、物差しが違うまま比べていることになります。
載せ替えを計算するときは、トークン数のほうも換算する必要があります。
def swap_model(src, dst, usage, tokenizer_ratio=1.30):
"""src で計測した usage を dst に載せ替えたときの費用。"""
a, b = RATES[src]["tokenizer"], RATES[dst]["tokenizer"]
scale = 1.0 if a == b else (1 / tokenizer_ratio if a == "new" else tokenizer_ratio)
scaled = {k: v * scale for k, v in usage.items()}
return breakdown(dst, scaled)["total"], scale先ほどの daily をそのまま載せ替えると、こうなります。
haiku-4-5:
単価だけ差し替え $ 85.20 / 30日 (Sonnet 5 比 0.50x)
トークン換算 0.77 込み $ 65.54 / 30日 (Sonnet 5 比 0.38x)
opus-5:
単価だけ差し替え $ 426.00 / 30日 (Sonnet 5 比 2.50x)
トークン換算 1.00 込み $ 426.00 / 30日 (Sonnet 5 比 2.50x)
Haiku 4.5 は「単価が半分だから半額」ではありませんでした。この例では0.38倍です。トークナイザが同じ Opus 5 との比較では、換算は入らず2.5倍のままです。
差が出るのは費用だけではありません。同じ文章が1.3倍のトークンになるということは、同じコンテキスト窓に入る分量がその分だけ減るということでもあります。窓の上限に近い使い方をしているなら、こちらのほうが先に効いてくるはずです。
「およそ30%」を自分の文章で確かめる
ここで一度、正直に線を引かせてください。1.30 という比は公式が示している目安であり、私が測った値ではありません。実際の比は、書かれている内容によって動きます。日本語と英語でも、コードと散文でも変わります。
自分の入力で確かめるほうが確実です。token counting のエンドポイントは課金されませんので、同じ文章を両方のモデルで数えて比を取ってください。
import anthropic
client = anthropic.Anthropic() # ANTHROPIC_API_KEY 環境変数を読みます
def count(model, text):
r = client.messages.count_tokens(
model=model,
messages=[{"role": "user", "content": text}],
)
return r.input_tokens
sample = open("prompts/typical_request.txt", encoding="utf-8").read()
new_tok = count("claude-sonnet-5", sample)
old_tok = count("claude-haiku-4-5", sample)
print(f"new={new_tok} old={old_tok} ratio={new_tok / old_tok:.3f}")出た比を tokenizer_ratio に入れれば、先ほどの試算が自分の文章に合ったものになります。代表的なリクエストを3つほど測って平均を取れば、当面はそれで足ります。
長いプロンプトを送る前に見積もりを取る使い方については、20万トークンの崖で夜間バッチの請求が跳ねた に書いています。
今日やるなら、この一つだけ
直近7日ぶんの usage を合計して、この記事のスクリプトに入れてみてください。内訳の4行のうち、どれが一番大きいかが分かります。
それだけで、次に手を入れる場所が決まります。私はこの内訳を出して初めて、キャッシュ書き込みが最大であること、そしてリクエストの間隔が5分の有効期限をまたいでいた時間帯があることに気づきました。単価を眺めているうちは見えなかったことです。
価格が変わるたびに定数を手で書き換えるのが面倒になってきたら、単価そのものに有効期間を持たせる設計に切り替える手があります。7月に書いた Sonnet 5 導入価格の期限を織り込む実効日付きコスト予測 は、まさにその話です。皮肉なことに、あの記事が想定していた「9月1日の切り替え」は起きませんでした。それでも、価格を静的な定数として持たない、という結論のほうは今回の件でむしろ裏づけられたと感じています。
予定されていた変更が取り消される、という方向の変化まで想定できていなかったのは、私の見落としでした。同じ落とし穴を踏まずに済む方がいれば嬉しいです。お読みいただきありがとうございました。