0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude API の count_tokens で送信前にトークン数を見積もる実装手順 — tools・system 込みで数える、実測とのズレ、レート制限が別枠の3つのハマりどころ【2026】

0
Posted at

はじめに / 対象と前提

Claude API(Messages API)を使ったアプリで、送信前に入力トークン数を見積もりたい人向け。長文を突っ込んで context window を超過して落ちるのを事前に防ぎたい、あるいはリクエスト単位のコストを概算したい、というケースを想定している。

前提環境(動作確認済み):

  • Python 3.13
  • anthropic SDK 1.x
  • モデルは claude-opus-5(count_tokens は現行モデル全対応)

TL;DR

  • POST /v1/messages/count_tokens本番と同じリクエストボディを投げると input_tokens が返る。無料・beta ヘッダー不要
  • systemtools を省いて messages だけ数えると過少見積りになる(ここが一番のハマりどころ)
  • 返り値は推定値で実測と数トークンズレる。さらにレート制限が Messages 本体と別枠なので、毎リクエスト呼ぶ実装は count_tokens 側の 429 を先に踏む

手順 / 動かし方

SDK から呼ぶ

from anthropic import Anthropic

client = Anthropic()  # ANTHROPIC_API_KEY を環境変数から読む

resp = client.messages.count_tokens(
    model="claude-opus-5",
    system="あなたは社内ドキュメントの要約アシスタントです。",
    messages=[{"role": "user", "content": long_text}],
)
print(resp.input_tokens)  # => 3121 のような整数

curl で叩く

curl https://api.anthropic.com/v1/messages/count_tokens \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"claude-opus-5","messages":[{"role":"user","content":"こんにちは"}]}'
# => {"input_tokens":11}

ポイントは3つ。

  • model / messages / system / tools など、Messages API に投げるものをそのまま渡せるmax_tokens は不要(入力を数えるだけなので)
  • レスポンスは {"input_tokens": N} の1フィールドだけ
  • トークンを消費しない=無料。見積りのために課金されることはない

実用形: 送信前の上限チェック

MAX_INPUT = 200_000  # 使うモデルの context window に合わせる

def build_request(messages: list) -> dict:
    # 本番リクエストと見積りで「同じボディ」を使うのが肝
    return dict(model="claude-opus-5", system=SYSTEM_PROMPT,
                tools=TOOLS, messages=messages)

req = build_request(messages)
n = client.messages.count_tokens(**req).input_tokens
if n >= MAX_INPUT:
    raise ValueError(f"入力が大きすぎる: {n} tokens")
resp = client.messages.create(**req, max_tokens=16000)

動作確認は、実際に送ったレスポンスの resp.usage.input_tokens と見積り値を突き合わせればよい。自分の環境では 3,000 トークン級の入力で誤差は数トークンだった。

ハマりどころ

1. messages だけ数えると過少見積りになる(system・tools が抜ける)

症状: 見積り 3,000 に対して実測 4,200、のように大きくズレる。

原因: count_tokens は「渡したものだけ」を数える。本番リクエストでは system プロンプトと tools の定義(+ tool use 有効時のシステム側オーバーヘッド)も input に含まれる。ツールを10個も定義していると、定義だけで数千トークン食っていることは普通にある。

回避策: 上のコードのようにリクエストボディを組み立てる関数を共通化して、count_tokens と messages.create に同じものを渡す。見積りロジックだけ別実装にすると必ずズレる。

2. 返り値は「推定値」— 実測と微妙にズレる。tiktoken 代用は論外

count_tokens の返り値は公式ドキュメント上も推定値の扱いで、実測の usage.input_tokens と数トークン程度ズレることがある。ギリギリの上限判定やコスト計算に使うなら 1〜数%のマージンを取っておく。

トークナイザはモデル固有なので、model に渡す ID は推論に使う ID と必ず揃えること。モデル世代でトークナイザが変わることがあり、同じ文章でもカウントが数%〜数十%変わる。

あと、ローカルで済ませたくて tiktoken(OpenAI のトークナイザ)で代用するのは NG。Claude に対しては典型的な英文で 15〜20% 過少、コードや日本語混じりだとさらにズレる。自分は最初これで見積もっていて、上限チェックをすり抜けた入力が本番で validation エラーになった。

なお prompt caching を使っていても count_tokens は全量のトークン数を返す。キャッシュヒットで安くなる分は見積りに反映されないので、コスト概算に使う場合はキャッシュ分を別途差し引く。

3. レート制限が Messages 本体と別枠 — 毎回呼ぶと count_tokens 側の 429 を先に踏む

count_tokens は無料だが、リクエスト数ベースのレート制限が Messages API とは別に設定されている。「毎リクエスト見積り→送信」を素直に実装すると、Messages 側の枠は余裕なのに count_tokens 側だけ 429(rate_limit_error)を返し始める。

import anthropic

try:
    n = client.messages.count_tokens(**req).input_tokens
except anthropic.RateLimitError as e:
    retry_after = int(e.response.headers.get("retry-after", "60"))
    # retry_after 秒待って再試行するか、見積りをスキップして概算で進める

回避策: 毎回呼ばない。会話が続くケースなら「前回の usage.input_tokens + 今回追加分の概算」で足し込んでおき、閾値に近づいたときだけ count_tokens で厳密に測り直す、という2段構えにすると呼び出し回数を一桁減らせる。

背景・補足

Claude のトークナイザは公開されていないため、正確に数えるにはサーバーに聞くしかない、というのがこの専用エンドポイントの存在理由。逆に言うと「オフラインで完結する正確なカウント手段は存在しない」ので、ローカル推定(文字数 ÷ 定数など)を使うなら誤差前提の設計にしておく必要がある。

まとめ

  • count_tokens は無料・beta ヘッダー不要。本番と同じボディを渡して input_tokens を得る
  • system・tools 込みで数える。ボディ組み立て関数を共通化するのが確実
  • 返り値は推定値。マージンを取る。tiktoken 代用は 15〜20% 過少で危険
  • レート制限は別枠。毎回呼ばず、閾値付近だけ厳密に測る2段構えにする

環境: Python 3.13 / anthropic SDK 1.x(2026年9月時点)。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?