生成AI入門 2026.07.07

OpenAI APIのトークン数を送信前に計算する方法|token_counterツール・料金見積もり【2026年版】

タグ:ChatGPT API / トークン計算 / コスト管理 / Python / API料金

ひとことで言うと

OpenAI APIを使う際、リクエスト送信前にトークン数を正確に計算することで、予期しない高額課金を防げます。Pythonの「tiketokenライブラリ」を使えば、実際のAPI計算と同じ方法でトークン数をカウント可能。料金予測ツールの実装パターンも紹介します。

なぜトークン数の事前計算が重要か

OpenAI APIの課金は「トークン(言葉を分割した最小単位のこと)」ごとに発生します。テキストを送信する前にトークン数を知ることで、以下のメリットが得られます。

  • 💰 予算を守る:送信前に「この質問は〇円かかる」と把握できる
  • 🛡️ バグによる超過課金を防ぐ:ループ処理の暴走など、予期しない大量リクエストの損失を検知できる
  • 📊 コスト削減の工夫が見える:どこのテキストが「トークン無駄使い」か特定できる

多くの日本のスタートアップ・個人開発者が「気づいたら高額請求が来た」という経験をしています。送信前計算はそうした事故を防ぐ最初の防線です。

トークンとは?基本理解

トークンの数え方

トークンは1語=1トークンではありません。言語やモデルによって異なります。

トークン数
”hello”1トークン
”Hello, world!“4トークン
”こんにちは”3~4トークン(日本語は英語より多い傾向)
絵文字😊1~2トークン

日本語は1字=1トークンに近い計算になることが多く、英語より効率が悪いことに注意します。

モデルごとにトークン数が違う

重要な落とし穴:同じテキストでも、モデルによってトークナイザー(トークンに分割するプログラムのこと)が異なることがあります。実装時は「どのモデルで計算するか」を明確に指定する必要があります。

tiketokenライブラリの使い方

インストール

pip install tiktoken

公式ドキュメントによると、tiketokenはOpenAIが提供する軽量・高速なPythonライブラリで、ローカルマシンで即座にトークン数をカウントできます。

基本的な使い方

1. モデルを指定してトークンをカウント

import tiktoken

# GPT-4o用のトークナイザーを取得
enc = tiktoken.encoding_for_model("gpt-4o")

# テキストをトークン化
text = "OpenAI APIの料金は思いの外高いことがあります。"
tokens = enc.encode(text)

print(f"トークン数: {len(tokens)}")
print(f"トークン内容: {tokens}")

実行結果の例:

トークン数: 22
トークン内容: [139, 9, 1230, 45, ...]

2. 複数のテキストを計算

import tiktoken

enc = tiktoken.encoding_for_model("gpt-4o")

messages = [
    {"role": "user", "content": "こんにちは"},
    {"role": "assistant", "content": "こんにちは。何かお手伝いできることはありますか?"}
]

# メッセージ全体のトークン数を計算
total_tokens = 0
for msg in messages:
    tokens = enc.encode(msg["content"])
    total_tokens += len(tokens)
    print(f"[{msg['role']}]: {len(tokens)}トークン")

print(f"合計: {total_tokens}トークン")

3. API呼び出し時の「オーバーヘッド」を含める

実は、APIリクエストにはテキスト本体以外にメタデータ用のトークンが消費されます。公式にはメッセージ形式では以下の計算が目安です。

  • システムメッセージ:3~4トークンの固定オーバーヘッド
  • 各メッセージの区切り:3~4トークン追加
def count_message_tokens(messages, model="gpt-4o"):
    """API呼び出しで実際に消費されるトークン数を推定"""
    enc = tiktoken.encoding_for_model(model)
    
    total = 0
    for msg in messages:
        # メッセージの内容
        total += len(enc.encode(msg["content"]))
        # メッセージの区切り記号(3~4トークン)
        total += 4
    
    # 終了マーカー(1トークン)
    total += 1
    
    return total

# 使用例
messages = [
    {"role": "system", "content": "あなたは技術ライターです"},
    {"role": "user", "content": "OpenAI APIの料金を説明してください"}
]

estimated_tokens = count_message_tokens(messages)
print(f"推定トークン数(オーバーヘッド含む): {estimated_tokens}")

料金予測ツールの実装パターン

パターン1:シンプルな料金計算

GPT-4oの現在の料金(2026年8月時点の参考値):

  • 入力:$0.005 / 1,000トークン
  • 出力:$0.015 / 1,000トークン
import tiktoken

class APITokenCalculator:
    """OpenAI APIのトークン数・料金を計算するクラス"""
    
    # 2026年8月時点の料金(ドルで定義)
    PRICING = {
        "gpt-4o": {
            "input": 0.005,    # $ per 1,000 tokens
            "output": 0.015
        }
    }
    
    def __init__(self, model="gpt-4o"):
        self.model = model
        self.enc = tiktoken.encoding_for_model(model)
    
    def count_tokens(self, text):
        """テキストのトークン数を計算"""
        return len(self.enc.encode(text))
    
    def estimate_cost(self, input_text, estimated_output_tokens=100):
        """
        入力テキストと予想出力トークン数から料金を推定
        
        Args:
            input_text (str): 送信するテキスト
            estimated_output_tokens (int): 出力の予想トークン数
        
        Returns:
            dict: 料金情報
        """
        input_tokens = self.count_tokens(input_text)
        
        pricing = self.PRICING[self.model]
        input_cost = (input_tokens / 1000) * pricing["input"]
        output_cost = (estimated_output_tokens / 1000) * pricing["output"]
        total_cost = input_cost + output_cost
        
        return {
            "input_tokens": input_tokens,
            "estimated_output_tokens": estimated_output_tokens,
            "input_cost_usd": round(input_cost, 6),
            "output_cost_usd": round(output_cost, 6),
            "total_cost_usd": round(total_cost, 6),
            "total_cost_jpy": round(total_cost * 150, 0)  # 例:1ドル=150円
        }

# 使用例
calc = APITokenCalculator()

question = "OpenAI APIの料金体系について、初心者向けに詳しく説明してください。"
result = calc.estimate_cost(question, estimated_output_tokens=300)

print(f"入力トークン: {result['input_tokens']}")
print(f"推定出力トークン: {result['estimated_output_tokens']}")
print(f"合計料金: ${result['total_cost_usd']} (約{int(result['total_cost_jpy'])}円)")

パターン2:バッチリクエストの超過防止

複数のテキストを処理する際に合計トークン数が予算を超えないようにする実装です。

class TokenBudgetGuard:
    """トークン予算を守りながらバッチリクエストを処理"""
    
    def __init__(self, model="gpt-4o", max_input_tokens=10000):
        self.model = model
        self.enc = tiktoken.encoding_for_model(model)
        self.max_input_tokens = max_input_tokens
    
    def split_texts_by_token_budget(self, texts, max_tokens_per_batch=5000):
        """
        テキストのリストを、トークン予算内に収まるようにバッチ分割
        
        Args:
            texts (list): テキストリスト
            max_tokens_per_batch (int): 1バッチあたりの最大トークン数
        
        Yields:
            list: トークン予算内のテキストバッチ
        """
        batch = []
        batch_tokens = 0
        
        for text in texts:
            text_tokens = len(self.enc.encode(text))
            
            # このテキストを追加すると超過する場合
            if batch_tokens + text_tokens > max_tokens_per_batch:
                if batch:
                    yield batch
                batch = [text]
                batch_tokens = text_tokens
            else:
                batch.append(text)
                batch_tokens += text_tokens
        
        if batch:
            yield batch
    
    def estimate_batch_cost(self, texts, cost_per_1k_input=0.005):
        """バッチ処理全体のコストを推定"""
        total_tokens = sum(len(self.enc.encode(t)) for t in texts)
        cost = (total_tokens / 1000) * cost_per_1k_input
        return {
            "total_texts": len(texts),
            "total_input_tokens": total_tokens,
            "estimated_cost_usd": round(cost, 6)
        }

# 使用例
guard = TokenBudgetGuard(max_input_tokens=50000)

documents = [
    "ドキュメント1:OpenAI APIの基本...",
    "ドキュメント2:トークン計算の方法...",
    "ドキュメント3:料金管理のベストプラクティス...",
    # ... さらに多くのドキュメント
]

# バッチに分割して処理
for i, batch in enumerate(guard.split_texts_by_token_budget(documents, max_tokens_per_batch=3000)):
    print(f"バッチ {i+1}: {len(batch)}件のテキスト")
    cost = guard.estimate_batch_cost(batch)
    print(f"  推定トークン: {cost['total_input_tokens']}")
    print(f"  推定コスト: ${cost['estimated_cost_usd']}")

パターン3:応答が途中で切れたときの補足計算

トークン上限に達して応答が途中で切れるケースでは、入力トークンだけでなく、すでに消費された出力トークンをカウントして補続リクエストを最適化する必要があります。

def estimate_continuation_cost(
    previous_input_tokens,
    previous_output_tokens,
    continuation_input_tokens,
    input_rate=0.005,
    output_rate=0.015
):
    """
    途中で切れた応答を続けるときの追加コスト計算
    
    Args:
        previous_input_tokens: 最初のリクエストの入力トークン
        previous_output_tokens: 最初のリクエストで得た出力トークン
        continuation_input_tokens: 補続リクエストの入力トークン
        input_rate: 入力の単価($/1000token)
        output_rate: 出力の単価($/1000token)
    """
    # すでに支払った分
    previous_cost = (
        (previous_input_tokens / 1000) * input_rate +
        (previous_output_tokens / 1000) * output_rate
    )
    
    # 補続リクエストのコスト(入力のみ、出力は不明)
    continuation_cost = (continuation_input_tokens / 1000) * input_rate
    
    return {
        "previous_cost_usd": round(previous_cost, 6),
        "continuation_cost_usd": round(continuation_cost, 6),
        "total_so_far_usd": round(previous_cost + continuation_cost, 6),
        "note": "出力コストは補続の応答を得た後に追加される"
    }

# 使用例
result = estimate_continuation_cost(
    previous_input_tokens=500,
    previous_output_tokens=2000,
    continuation_input_tokens=250
)
print(result)

実装のベストプラクティス

1. 開発環境で常にトークン計算を有効にする

import os
import tiktoken

# 環境変数で「テストモード」を制御
TEST_MODE = os.getenv("API_TEST_MODE") == "true"

def call_api_with_token_check(messages, model="gpt-4o"):
    """送信前トークン数をチェック"""
    enc = tiktoken.encoding_for_model(model)
    
    total_tokens = sum(len(enc.encode(m["content"])) for m in messages)
    
    print(f"[LOG] 予想トークン数: {total_tokens}")
    
    # テストモードなら実際のAPIを呼ばず、推定結果を返す
    if TEST_MODE:
        print("[LOG] テストモード: APIは呼ばずシミュレーション")
        return {"role": "assistant", "content": "[テスト応答]"}
    
    # 本番環境ではここで実際のAPI呼び出し
    # response = openai.ChatCompletion.create(...)

2. ログに「予想 vs 実績」を記録

import json
from datetime import datetime

class APICallLogger:
    """API呼び出しのコストを記録"""
    
    def __init__(self, log_file="api_calls.jsonl"):
        self.log_file = log_file
    
    def log_call(self, input_tokens, output_tokens, model="gpt-4o", cost_usd=None):
        """1回のAPI呼び出しを記録"""
        record = {
            "timestamp": datetime.now().isoformat(),
            "model": model,
            "input_tokens": input_tokens,
            "output_tokens": output_tokens,
            "cost_usd": cost_usd
        }
        
        with open(self.log_file, "a") as f:
            f.write(json.dumps(record) + "\n")
    
    def analyze_usage(self):
        """集計結果を表示"""
        total_input = 0
        total_output = 0
        
        with open(self.log_file, "r") as f:
            for line in f:
                record = json.loads(line)
                total_input += record["input_tokens"]
                total_output += record["output_tokens"]
        
        total_cost = (total_input / 1000) * 0.005 + (total_output / 1000) * 0.015
        
        print(f"累計入力トークン: {total_input}")
        print(f"累計出力トークン: {total_output}")
        print(f"推定累計コスト: ${round(total_cost, 2)}")

3. 予期しない大量リクエストを検知

def safe_batch_api_call(requests, max_cost_usd=10.0):
    """
    複数のリクエストを処理する際、コストが予算を超えないようにチェック
    
    Args:
        requests (list): { "input": "...", "expected_output_tokens": 100 } のリスト
        max_cost_usd (float): 最大許容コスト(ドル)
    
    Returns:
        dict: 実行可否と理由
    """
    enc = tiktoken.encoding_for_model("gpt-4o")
    
    total_cost = 0
    for req in requests:
        input_tokens = len(enc.encode(req["input"]))
        output_tokens = req.get("expected_output_tokens", 100)
        
        cost = (input_tokens / 1000) * 0.005 + (output_tokens / 1000) * 0.015
        total_cost += cost
        
        if total_cost > max_cost_usd:
            return {
                "can_execute": False,
                "reason": f"予算超過: ${round(total_cost, 2)} > ${max_cost_usd}",
                "stop_at_request": requests.index(req)
            }
    
    return {
        "can_execute": True,
        "total_cost_usd": round(total_cost, 2),
        "requests_count": len(requests)
    }

よくあるトークン計算の落とし穴

間違い問題点対策
文字数でトークン数を推定日本語は1字≠1トークン(実際は多い)必ずtiketokenで計算
同じトークナイザーを再利用しないモデルごとに異なるため非効率グローバル変数・クラス属性で共有
出力トークンを無視実際のコストより安く見積もる過去のログから平均出力トークン数を把握
メッセージのオーバーヘッドを忘れる実際より安く見積もる4トークン/メッセージを加算
max_tokensをAPIに設定しない無限ループで高額課金のリスクmax_tokensパラメータを常に指定
モデルによるトークナイザーの違いを無視同じテキストでもトークン数が異なる実際に利用するAPIモデルに合わせて指定

実践的なトークン計算チェックリスト

ChatGPT APIを本番環境で利用する前に、以下のチェックリストで確認しましょう。

  1. ✓ tiketokenをインストールし、送信前にトークン数をカウントする仕組みを導入したか
  2. ✓ 実装環境と同じモデルのトークナイザーで計算テストを実施したか
  3. ✓ メッセージのオーバーヘッドトークンを計算に含めているか
  4. ✓ 大量リクエスト前に試験的に小規模リクエストでトークン数を検証したか
  5. ✓ 月ごとの予想コストを計算し、予算内に収まっているか確認したか
  6. ✓ ログ・監視を設置し、予算オーバーの警告を自動化したか
  7. ✓ テストモードで本番前にシミュレーションを実施したか

参考:Claude APIの公式トークンカウント機能

Claude APIは別ライブラリ不要で、SDKに組み込みのトークンカウント機能があります。

import anthropic

client = anthropic.Anthropic()

def estimate_cost_before_sending(prompt: str, model: str = "claude-sonnet-4-5") -> dict:
    """送信前にトークン数とコストを見積もる"""
    count_result = client.messages.count_tokens(
        model=model,
        messages=[{"role": "user", "content": prompt}]
    )

    input_tokens = count_result.input_tokens

    pricing = {
        "claude-haiku-4-5-20251001": {"in": 0.80, "out": 4.00},
        "claude-sonnet-4-5": {"in": 3.00, "out": 15.00},
        "claude-opus-5": {"in": 15.00, "out": 75.00},
    }

    estimated_input_cost = (input_tokens * pricing[model]["in"]) / 1_000_000

    return {
        "input_tokens": input_tokens,
        "estimated_input_cost_usd": round(estimated_input_cost, 6),
        "model": model
    }

# 使用例:送信前にコストを確認
prompt = "以下の契約書を要約してください:" + "..." * 500  # 長文の例
estimate = estimate_cost_before_sending(prompt)
print(f"入力トークン数: {estimate['input_tokens']}")
print(f"予想コスト(入力のみ): ${estimate['estimated_input_cost_usd']}")

if estimate["estimated_input_cost_usd"] > 0.10:
    print("⚠️ 想定コストが高額です。送信前に内容を見直してください")

count_tokensはAPIリクエストと同じトークナイザーを使うため、tiktokenのような別ライブラリでの近似計算より正確な見積もりが得られます。


まとめと結論

ChatGPT APIのコスト管理は「運」ではなく「計算」です。tiketokenライブラリを活用することで、APIを実行する前に費用を正確に見積もり、予算超過を防ぐことができます。

以下の流れで実装すれば、ほぼすべての予期しない課金を防げます。

  1. tiketokenをインストールpip install tiktoken
  2. 送信前に必ずトークン数をカウントtiktoken.encoding_for_model() を使う
  3. 料金予測ツールを実装:上記の実装パターンから選ぶ
  4. ログ・監視を設置:予算オーバーの警告を自動化
  5. テストモードで検証:本番環境に入る前にシミュレーション

特にスタートアップ・個人開発者の場合、毎月の固定予算(例:500円)を決めて、その範囲内でAPIを使うのが現実的です。トークン計算はそうした「予算内での最大活用」を可能にする最強のツールです。手間を惜しまず、今からでも実装することをお勧めします。


あわせて読みたい

参考ソース