トークン数の数え方とLLM APIコストの見積もり方
OpenAI、Claude、Gemini、LlamaのトークナイザーでLLMトークンを正確に数え、API料金を見積もる方法とコンテキスト窓の注意点を解説します。
トークンを正確に数えるには、実際に呼び出しているモデルのトークナイザーにリクエストボディ全体を通し、その上でコストを (input_tokens ÷ 1,000,000) × input_rate + (output_tokens ÷ 1,000,000) × output_rate として見積もります。レートはプロバイダーの料金ページに掲載されている最新の値を使ってください。
これを事前に計算している人はまずいません。請求書が届いた朝や、長い会話が実際のユーザーに対してコンテキストウィンドウのエラーを吐き始めた午後になって初めて浮上し、そこで突然「これは何トークンなのか?」が唯一重要な問いになります。厄介なのは、トークンは単語ではないこと、カウントがモデルに依存すること、そして請求対象の半分はプロンプト文字列にはまったく現れないことです。
本記事では、再現可能な手法を示します。おおよその見積もりで十分なのはどんな場合か、プロバイダーごとに正確なカウントを取得する方法、実際に何が課金対象になるのか、そして価格改定にも耐えるコスト予測へとカウントを変換する方法です。
要点
- トークンは、呼び出しているモデルのトークナイザーで数えること。OpenAI なら tiktoken、Claude なら
messages.countTokens、Gemini ならcountTokens、Llama ならそのモデル自身の Hugging Face 上のトークナイザーを使います。 - 文字数 ÷ 4 のようなヒューリスティックはキャパシティプランニングには使えますが、課金の見積もりには決して使わないこと。コード、JSON、非英語テキスト、絵文字では破綻します。
- 課金対象のプロンプトはリクエストボディ全体であり、システムプロンプト、ロールのフレーミング、ツールスキーマ、再送される会話履歴を含みます。ユーザーのメッセージだけではありません。
- 入力トークン数は決定的ですが、出力トークン数はそうではありません。実リクエストを50〜200件サンプリングし、平均出力長からコストを計画し、p95 から
max_tokensを設定します。 - リクエストあたりの推定コストは (input_tokens ÷ 1M) × input_rate + (output_tokens ÷ 1M) × output_rate で、レートはプロバイダーの料金ページから最新の値を参照します。
なぜトークンは単語ではないのか?
トークンとは、サブワードトークナイザーによって生成されるモデル固有のテキスト単位であり、単語にも文字にも対応しません。OpenAI の tiktoken のような byte pair encoding に基づくトークナイザーは、頻出する文字列を単一トークンにマージし、稀な単語を複数の断片に分割します。“idempotency” という単語は、GPT-4 世代のエンコーディングである cl100k_base では4トークン(“id”、“emp”、“ot”、“ency”)に、現行の OpenAI モデルが使う o200k_base では3トークン(“id”、“empot”、“ency”)にエンコードされます。
課金の観点で重要なのは最後の点です。分割はモデル固有だということです。同じ文でも GPT、Claude、Gemini、Llama の各トークナイザーでは異なるカウントになります。それぞれが異なるデータで、異なる語彙をもって訓練されているからです。誤ったトークナイザーで取ったカウントは、単なる推測にすぎません。
おおよその見積もりで十分なのはどんなときか?
英語の散文であれば、文字数 ÷ 4 または単語数 × 1.33 で、データベースのカラム幅を決めたりキャパシティプランを描いたりするには十分な精度が得られます。ヒューリスティックはキャパシティプランニングに使い、課金やコンテキストウィンドウの判断には決して使わないでください。
ヒューリスティックは、まさに本番トラフィックが存在する領域で破綻します。コード、JSON、非英語テキスト、絵文字です。構造化されたペイロードは、文字数カウントが無視する句読点や空白のパターンでトークン化されますし、絵文字1つが複数トークンに展開されることもあるため、文字数 ÷ 4 は絵文字の多い文字列を大幅に過小評価します。英語の散文では小さく収まっているトークナイザー間の差は、コードや構造化データでは実質的にはるかに大きくなります。そしてそれこそが、要約機能やエージェントが送信するコンテンツなのです。
正確なカウントを返す LLM トークンカウンターはどれか?
原則は一行で表せます。呼び出しているモデルに属するトークナイザーで数えること。プロバイダーごとの経路は次のとおりです。
| プロバイダー | 正確なカウント手段 |
|---|---|
| OpenAI | tiktoken、Node およびエッジランタイムでは js-tiktoken |
| Anthropic | count-tokens エンドポイント、TypeScript SDK では client.messages.countTokens() |
| Gemini | @google/genai SDK の ai.models.countTokens() |
| Llama およびその他のオープンモデル | Hugging Face 上に公開されているそのモデル自身のトークナイザー |
JavaScript では、js-tiktoken が純粋な JS 移植版なので、ロードすべき WASM バイナリも手動で解放するメモリもありません。また、エンコーディング一式ではなく必要な1つだけを取り込めるため、バンドルサイズを小さく保てます。
import { Tiktoken } from "js-tiktoken/lite";
import o200k_base from "js-tiktoken/ranks/o200k_base";
const enc = new Tiktoken(o200k_base);
const count = enc.encode("Summarise this ticket thread for support.").length;
Anthropic のエンドポイントは呼び出しが無料で、独自のレート制限のみが適用されるため、他プロバイダーのトークナイザーで Claude のカウントを近似する理由はコスト面でもありません。ただしその結果は、正確な値ではなく信頼できる事前カウントとして扱ってください。Anthropic 自身が推定値であると明記しており、課金される数値はレスポンスの usage フィールドに由来します。トークナイザーは同一プロバイダー内でもモデル世代間で変わります。Anthropic のトークンカウントのドキュメントによれば、Claude 4.7 以降は新しいトークナイザーを採用しており、同じテキストが従来の Claude モデルより約30パーセント多いトークン数になります。実際の差はコンテンツ次第です。古いカウントは流用できません。実際に呼び出すモデルで新たに取得してください。SDK を組み込むまでもなく数値だけが欲しい場合は、GPT、Claude、Gemini、Llama をカバーする LLM トークンカウンターにプロンプトを貼り付けてください。
なぜカウントが請求額と合わないのか?
課金対象のプロンプトは、あなたが書いた文字列ではなく、リクエストボディ全体です。ロールのフレーミング、システムプロンプト、ツールおよび関数のスキーマ、メッセージごとの区切りのすべてがトークンを追加します。だからこそ、ユーザーのメッセージだけを数えると必ず過小評価になるのです。ツール定義がひとつあるだけで、それを含むすべてのリクエストに数百の入力トークンが加算されることもあります。
会話履歴は乗数として効いてきます。チャット機能はターンごとに履歴全体を再送するため、各ターンの入力にはそれ以前のすべてのターンが含まれ、会話単位のコストは会話の長さに対して超線形に増加します。カウントに関する解決策は単純です。実際に送信するメッセージ配列、システムプロンプト、ツールをそのまま組み立て、それを数えることです。Anthropic の count-tokens エンドポイントは、メッセージ作成時に送るのと同じペイロード(ツール定義を含む)を受け取るので、組み立てたリクエストをそのまま渡せます。
トークン数をコスト見積もりに変換するには?
リクエストあたりの推定コストは、記号のまま扱える一行の算術です。
cost = (input_tokens / 1_000_000) * input_rate + (output_tokens / 1_000_000) * output_rate
プロンプトキャッシュをサポートするプロバイダーでは、キャッシュされた入力トークンは別個のより低い cached_input_rate で課金されます。モデルごとの価格は数週間単位で変わるため、ここには具体的なレートを記載していません。コード内ではレートを注入される設定値として扱い、最新の値はプロバイダーの料金ページから参照し、モデル間の現在の数値を比較するには LLM コスト計算ツールを使ってください。
すべての見積もりを左右する事実が2つあります。第一に、主要プロバイダーでは出力トークンの単価が入力トークンより実質的に高いのが通常であり、そのためレスポンスの長さがコストを支配することが多い点です。第二に、入力カウントは決定的である一方、出力カウントはそうではないという点です。同じリクエストは常に同じ入力カウントになりますが、返ってくるものはサンプリングによって変動します。出力は実測してください。代表的なリクエストを50〜200件実行し、平均出力長からコストを計画し、95パーセンタイルから max_tokens を設定すれば、正当なレスポンスが切り詰められることなく、暴走した生成には上限がかかります。
プロンプトがコンテキストウィンドウに収まるかをどう確認するか?
入力トークンと想定される出力トークンの合計が、モデルのコンテキストウィンドウ内に収まらなければ、呼び出しは失敗するか、レスポンスが切り詰められます。この事前チェックはリクエストのラッパーに置くべきです。ウィンドウをシステムコンテキスト、会話履歴、出力の余裕に配分し、組み立てたリクエストを数え、エラーが出てからではなく送信前に履歴を削ります。コンテキストウィンドウチェッカーを使えば、リリースのたびに変わるウィンドウサイズを暗記しなくても、あるプロンプトがあるモデルに収まるかどうかが分かります。
ラッパーへの配置が重要なのは、超過がユーザーから見えるからです。回答の切り詰めやストリーム途中のエラーが起き、ユーザーは反射的に再試行するので、トークンバジェットのバグは二重に課金されます。LLM を用いた機能のセッションリプレイは、月次でレビューされる請求書に現れるはるか前に、まさにその再試行ループを可視化します。
本番環境で記録すべきもの
価格は変動しても、手法は安定しています。組み立てたリクエストを呼び出し先モデル自身のトークナイザーで数え、実トラフィックをサンプリングして出力の分布を把握し、レートは料金ページから更新する設定値として保持することです。そのうえで本番環境でループを閉じます。主要なプロバイダーはいずれも、レスポンスの usage フィールドで実際のトークン数を返します。たとえば Anthropic の usage.input_tokens や Gemini の usageMetadata です。ただし Gemini の新しい Interactions API(まだ Beta)は、total_input_tokens と total_output_tokens を含む usage を返します。初日からリクエストごとに記録してください。記録するのは些細な作業ですが、驚くような請求書が届いた後に再構成するのはそうではありません。
FAQ
Claude や Gemini のモデルのトークン数を数えるのに tiktoken を使えますか?
いいえ。各プロバイダーのトークナイザーは独自の語彙を持つため、tiktoken のカウントが有効なのは OpenAI のモデルに限られ、同じ入力でも Claude や Gemini では大きく食い違うことがあります。Claude には呼び出しが無料の Anthropic の count-tokens エンドポイントを、Gemini には @google/genai SDK の countTokens メソッドを、Llama のようなオープンモデルには Hugging Face 上に公開されているトークナイザーを使ってください。
tiktoken と js-tiktoken の npm パッケージの違いは何ですか?
tiktoken は WASM バインディングです。コンパイル済みバイナリをロードし、使い終わったらエンコーダのメモリを解放するために free() を呼ぶ必要があります。js-tiktoken は純粋な JavaScript 移植版で、camelCase のメソッド(getEncoding、encodingForModel)を持ち、WASM バイナリも手動のメモリ管理も不要なため、エッジやサーバーレスのランタイムではより安全な選択肢です。単一のエンコーディングのランクファイルだけをインポートすれば、バンドルサイズも小さく保てます。
ストリーミングレスポンスでもトークン使用量は報告されますか?
はい、ただしどこでもデフォルトで報告されるわけではありません。OpenAI の Chat Completions では、stream_options に include_usage を true として設定すると、API がリクエスト全体をカバーする usage フィールドを持ち、choices 配列が空である最終チャンクを追加で1つストリームします。Anthropic は使用量を自動的にストリームします。message_start イベントが input_tokens を、message_delta イベントが累積の output_tokens を運びます。ストリームされたチャンクを自分で数えるのではなく、これらのフィールドをログに記録してください。
どの OpenAI モデルにはどの tiktoken エンコーディングを使うべきですか?
gpt-4o 以降のような現行の OpenAI モデルには o200k_base を、GPT-4 世代のモデルにのみ cl100k_base を使ってください。2つのエンコーディングはテキストの分割方法が異なるため、一方で取ったカウントはもう一方には流用できません。モデル ID を与えれば、js-tiktoken の encodingForModel が対応するエンコーディングを選んでくれるので、モデルが変わったときに誤ったエンコーディングを固定してしまうことを避けられます。