ブログに戻る

コスト最適化ガイド

Python でプロジェクト別の API 支出を追跡し、予算アラートを受け取る

短い Python スクリプトが使用量ログをプロジェクト別コストレポートに変え、月次予算の 50、75、95、100% で警告します。

投稿者: CheapTokens Team公開日 読了時間 6 分

プロジェクト別支出レポートと予算警告を表示するターミナル
このページの内容
  1. Anthropic が提供したもの、そしてコピーできるもの
  2. ログ:リクエストごとに 1 行
  3. スクリプト:プロジェクト別コストとしきい値アラート
  4. 実行して結果を読む
  5. 安価なモデルでの 1 か月
  6. うまくいかないところ

API 呼び出しのたびに JSON を 1 行ファイルに追記し、プロジェクト名でタグ付けします。30 行の Python スクリプトがそのファイルをプロジェクト別コストレポートに変え、月次予算の 50、75、95、100% で警告を出します。Anthropic が Claude Enterprise チームに提供したのと同じビューを、自分のログから作るわけです。

1 つの残高がエージェント、ブログ下書きスクリプト、小さなサポートボットを支えているとします。月末になり、残高は思ったより少なく、3 つのどれが原因かは何も教えてくれません。この記事はそのギャップを埋めます。

Anthropic が提供したもの、そしてコピーできるもの#

Anthropic の発表(2026 年 10 月 28 日)は、スマートレポート、アプリ内サーベイ、プール型グループ予算を扱っており、一般提供は Claude Enterprise プラン限定です(source)。ここで重要なのは 2 点です。レポートはチームの作業をワークストリームにまとめ、それぞれの支出を表示します。プール型予算は 1 つの共有月次金額で、請求管理者はその 50%、75%、95%、100% でメールを受け取ります。Anthropic はさらに、レポートは投資計画のためのものであり、個人を評価するためのものではないと付け加えています。

同じ形なら Enterprise プランは不要です。ワークストリームは自分で選ぶラベルにすぎず、4 つのしきい値はコード内の 1 つのリストです。Anthropic が挙げた理由から、人ではなくプロジェクトでグループ化しましょう。

クイックチェック

プリペイド残高はすでにゼロでリクエストを止めます。なぜ 50% と 75% の警告を追加するのでしょうか?

答えを表示

答え: ゼロでは遅すぎるから:まだ調査する余裕があるうちに、暴走ループに気づきたい

残高はハードストップであり、アラームではありません。早めのしきい値があれば、100% での驚きが 9 日目の疑問になります。

ログ:リクエストごとに 1 行#

すべてのチャット補完レスポンスには、prompt_tokens と completion_tokens を持つ usage オブジェクトが含まれます。レポートに必要なのはこれと、自分で追加するプロジェクトラベルだけです。このラッパーはリクエストを送信し、月次ファイルに 1 行追記します(ファイル名は毎月変わるので、新しい月はゼロから始まります):

python
import json, os, time
from openai import OpenAI

client = OpenAI(base_url="https://api.cheaptokens.dev/v1",
                api_key=os.environ["CHEAPTOKENS_API_KEY"])

def ask(project, model, messages):
    r = client.chat.completions.create(model=model, messages=messages)
    with open(time.strftime("usage-%Y-%m.jsonl"), "a") as f:
        f.write(json.dumps({"project": project, "model": model,
                            "prompt_tokens": r.usage.prompt_tokens,
                            "completion_tokens": r.usage.completion_tokens}) + "\n")
    return r

ask("support-bot", "zai-org/GLM-5.3-Flash",
    [{"role": "user", "content": "Where is my order?"}])

CheapTokens docs の完全なモデル ID(deepseek-ai/DeepSeek-V4-Flash-0731、zai-org/GLM-5.3-Flash)を使ってください。価格表の短い名前は API では受け付けられません。キーは環境変数から取得し、ファイルからは決して取得しません。

各リクエストから使用量ログ、プロジェクトレポート、アラートへの流れ
リクエストがログの 1 行になり、レポートとアラートになる仕組み

スクリプト:プロジェクト別コストとしきい値アラート#

これを report.py として保存します。価格はプレースホルダーです。現在の価格を pricing table からコピーしてください(ここでは計算を読みやすくするためだけに、両モデルが 1 つの例価格を共有しています)。

python
import json, os, sys, time
from collections import defaultdict

LOG = time.strftime("usage-%Y-%m.jsonl")
BUDGET = float(os.environ.get("BUDGET_USD", "10"))  # example, substitute your own
STATE = LOG + ".alert"
LEVELS = [50, 75, 95, 100]
PRICES = {  # USD per 1M tokens (input, output): example, substitute current prices
    "deepseek-ai/DeepSeek-V4-Flash-0731": (1.0, 2.0),
    "zai-org/GLM-5.3-Flash": (1.0, 2.0),
}

spend = defaultdict(float)
with open(LOG) as f:
    for line in f:
        r = json.loads(line)
        p_in, p_out = PRICES[r["model"]]
        spend[r["project"]] += (r["prompt_tokens"] * p_in
                                + r["completion_tokens"] * p_out) / 1_000_000

total = sum(spend.values())
if total == 0:
    sys.exit("log is empty")

for name, usd in sorted(spend.items(), key=lambda kv: -kv[1]):
    print(f"{name:<8} ${usd:6.2f} {usd / total:5.0%}")
print(f"{'total':<8} ${total:6.2f} of ${BUDGET:.2f} ({total / BUDGET:.0%})")

last = int(open(STATE).read()) if os.path.exists(STATE) else 0
crossed = [l for l in LEVELS if total >= BUDGET * l / 100 and l > last]
if crossed:
    print(f"WARNING: budget passed {crossed[-1]}%")
    open(STATE, "w").write(str(crossed[-1]))

最後の 4 行が忘れられがちな部分です。小さな状態ファイルがなければ、スクリプトは月の残りずっと実行のたびに「50% を超えました」と叫び続けます。状態ファイルがあれば、各レベルは 1 回だけ発火します。

実行して結果を読む#

4 行のトイログを用意します(実際のログはリクエストごとに 1 行なので数千行になります):

text
{"project": "agent", "model": "deepseek-ai/DeepSeek-V4-Flash-0731", "prompt_tokens": 2400000, "completion_tokens": 300000}
{"project": "agent", "model": "deepseek-ai/DeepSeek-V4-Flash-0731", "prompt_tokens": 1000000, "completion_tokens": 100000}
{"project": "blog", "model": "zai-org/GLM-5.3-Flash", "prompt_tokens": 800000, "completion_tokens": 400000}
{"project": "bot", "model": "zai-org/GLM-5.3-Flash", "prompt_tokens": 500000, "completion_tokens": 100000}

例の価格と予算 10 で、python report.py は次を出力します:

text
agent    $  4.20   65%
blog     $  1.60   25%
bot      $  0.70   11%
total    $  6.50 of $10.00 (65%)
WARNING: budget passed 50%

エージェントが請求のほぼ 3 分の 2 を占めており、警告は最後ではなく 50% で出ました。もう一度実行すると、合計が 75% に達するまで警告は静かなままです。

プロジェクト別レポートと 50% 警告を表示するターミナル
トイログでのレポート出力:プロジェクト別シェアと最初の警告

これを毎月機能させるには、3 つのステップが必要です:

ステップバイステップ

3 つのステップで設定する

すべての呼び出しをログに記録

すべての呼び出しを ask(project, model, messages) 経由にし、短く安定したプロジェクトラベルを選びます。月の途中でラベルを変更すると、1 つのプロジェクトが 2 行に分かれます。

価格と予算を設定

価格表の現在の価格を PRICES に入れ、上限をエクスポートします:export BUDGET_USD=25。

スケジュールで実行

毎時の cron 行で十分です:0 * * * * cd ~/ai && python3 report.py。警告をプッシュしたい場合は、出力をメールやチャットのコマンドに送ります。

安価なモデルでの 1 か月#

数字は自分の数字になって初めて役に立ちます。トイログのエージェントが 1 ステップあたり平均 30,000 入力トークンと 2,000 出力トークン、1 タスク 20 ステップ、1 日 2 タスクを使うとします。DeepSeek-V4-Flash の 1 ステップは $0.00065、GLM-5.3-Flash では $0.00068 です。1 日は 40 ステップ、1 か月は 1,200 ステップです。スライダーを自分のエージェントに合わせて動かしてください:

コスト計算機

エージェントの 1 日:1 タスクあたりのステップ数 × 1 日あたりのタスク数

リクエストあたりの入力トークン数: 30,000 · リクエストあたりの出力トークン数: 2,000 · 1日あたりのリクエスト数: 40

モデル1日あたり1か月あたり
GLM-5.3-Flash$0.0272$0.816
DeepSeek-V4-Flash$0.026$0.78
MiniMax-M2.7$0.072$2.16

そして 1,200 ステップの 1 か月、入力と出力を合算して:

モデル リクエストあたり 1,200 リクエスト OpenRouter、1,200 リクエスト
GLM-5.3-Flash $0.00068 $0.816 $1.41
DeepSeek-V4-Flash $0.00065 $0.78 $2.86
MiniMax-M2.7 $0.0018 $2.16 $25.92

リクエストあたり 30,000 入力トークンと 2,000 出力トークン。100万トークンあたりの価格は現行のもので、当社の料金体系に合わせて変動します。OpenRouter 列は同じワークロードの参考価格を示しています。

合計で、入力と出力を合算すると、両モデルともその表の OpenRouter の参照価格より安くなり、時間帯を問わず 1 つの価格で、ピーク時の割増はありません。DeepSeek-V4-Flash は回答あたり最大 32,768 出力トークンと 380K コンテキストを許可し、GLM-5.3-Flash は 390K コンテキストを持ちます。レポートでどのプロジェクトが重いかがわかったら、そのプロジェクトの model 文字列だけを切り替えるのは 1 行の変更で、1 つの残高がすべてのモデルをカバーします。1 回のリクエストが実際に何トークン使うかは how many tokens an API request uses を、エージェントの実行を途中で止める方法は tracking AI agent token cost を参照してください。価格とモデルは CheapTokens models page にあります。

うまくいかないところ#

  • PRICES にモデルがない。 スクリプトはモデル名を示す KeyError で停止します。追加してください。ゼロをデフォルトにしないでください。そうしないとプロジェクトが無料に見えます。
  • ストリーミング呼び出し。 最終チャンクは、要求した場合にのみ使用量を含みます(OpenAI SDK では stream_options={"include_usage": True})。そうでなければログに穴が空きます。
  • 失敗したリクエスト。 usage オブジェクトを持つレスポンスだけをログに記録してください。エラーになったリクエストには数えるものがありません。
  • 個人別レポート。 ラベルはプロジェクト名かエージェント名に保ってください。人についてのレポートは誤った結論を招きます。