GASでClaude APIの利用トークン数と
料金を自動で記録・集計する方法

GASからClaude APIを業務で使い続けると気になるのが「毎月いくら使っているか」です。呼び出しのたびにレスポンスのusageから正確なトークン数を取り出し、単価をかけて料金をスプレッドシートに自動記録・月次集計する方法を動くコードで解説します。

|対象: GAS / Claude API / コスト管理

Table of Contents

なぜトークン数と料金を記録すべきか

Claude APIはトークン(単語や文字を細かく区切った処理単位)ごとに課金されます。GASで自動処理を回し始めると呼び出し回数は静かに積み上がり、月末の請求で初めて金額に気づく、ということが起こりがちです。呼び出しのたびに消費量を記録しておけば、「どの処理が」「どれだけ」使っているかが可視化でき、コストの予測と削減がしやすくなります。

正確な消費量が分かる

文字数からの推測ではなく、APIが返す実測トークン数を記録する

用途ごとに把握できる

要約・分類など処理の種類ごとに料金を分けて集計できる

モデル別に比較できる

軽量モデルと高性能モデルのコスト差を数字で判断できる

予算オーバーに気づける

しきい値を超えたら通知し、月末の請求で驚かない

仕組みはシンプルです。呼び出しの戻り値からトークン数を取り出し、単価をかけて1行のログに残す。あとはそのログを集計するだけです。順番に作っていきます。

レスポンスのusageからトークン数を取り出す

Claude APIの成功レスポンスにはusageという項目があり、その中のinput_tokensoutput_tokensに、その呼び出しで実際に消費したトークン数が入っています。文字数から推測する必要はありません。呼び出し関数の戻り値に、本文だけでなくトークン数とモデル名も含めて返すようにします。

// Claude APIの成功レスポンスには usage が含まれる。
// ここに正確な入力・出力トークン数が入っている。
// {
//   "content": [{ "type": "text", "text": "..." }],
//   "model": "claude-haiku-4-5",
//   "usage": { "input_tokens": 1234, "output_tokens": 567 }
// }

function callClaude(userText) {
  const apiKey = PropertiesService.getScriptProperties()
    .getProperty("CLAUDE_API_KEY");
  const model = "claude-haiku-4-5";

  const res = UrlFetchApp.fetch("https://api.anthropic.com/v1/messages", {
    method: "post",
    contentType: "application/json",
    headers: {
      "x-api-key": apiKey,
      "anthropic-version": "2023-06-01",
    },
    payload: JSON.stringify({
      model: model,
      max_tokens: 512,
      messages: [{ role: "user", content: userText }],
    }),
    muteHttpExceptions: true,
  });

  if (res.getResponseCode() !== 200) {
    throw new Error("Claude API error: " + res.getContentText());
  }

  const body = JSON.parse(res.getContentText());
  return {
    text: body.content[0].text,
    model: body.model,                     // 実際に使われたモデル名
    inputTokens: body.usage.input_tokens,  // 入力トークン数
    outputTokens: body.usage.output_tokens // 出力トークン数
  };
}

ポイントは、レスポンスに含まれるbody.model(実際に使われたモデル名)も一緒に受け取ることです。後で単価テーブルを引くときのキーになります。

モデル別の単価をコードで持って料金を計算する

料金は入力トークン数 × 入力単価 + 出力トークン数 × 出力単価で概算できます。単価はふつう「100万トークンあたり◯ドル」で表され、入力と出力で単価が異なります。モデル名をキーにした単価テーブルを持っておき、呼び出したモデルに応じて参照します。

// モデルごとの単価テーブル(100万トークンあたりのドル単価)。
// ※単価は変更されることがあるため、必ず公式の料金ページで
//   最新の値を確認して設定すること。ここはサンプル値。
const PRICE_PER_MTOK = {
  // model名: { input: 入力単価, output: 出力単価 }
  "claude-haiku-4-5":  { input: 1.0,  output: 5.0 },
  "claude-sonnet-4-5": { input: 3.0,  output: 15.0 },
  // 未知のモデルに備えたフォールバック
  "default":           { input: 3.0,  output: 15.0 },
};

const USD_TO_JPY = 160; // 円換算レート(任意。ドルのままでもよい)

// トークン数から概算料金(USDと円)を計算する
function estimateCost(model, inputTokens, outputTokens) {
  const price = PRICE_PER_MTOK[model] || PRICE_PER_MTOK["default"];
  const usd =
    (inputTokens  / 1000000) * price.input +
    (outputTokens / 1000000) * price.output;
  return {
    usd: usd,
    jpy: usd * USD_TO_JPY,
  };
}

単価はあくまでサンプル値です。料金は改定されることがあるため、実運用では必ずAnthropicの公式料金ページで最新の単価を確認し、テーブルの値を合わせてください。ここで出す金額は「概算」であり、実際の請求額とは端数などで差が出ます。

未知のモデル名が来ても落ちないよう、defaultのフォールバック単価を用意しておくのが安全です。

呼び出しをラップしてログシートに記録する

あとは、呼び出しの結果を「利用ログ」シートに1行追記するだけです。日時・モデル・トークン数・料金・用途を残しておけば、後からいくらでも集計できます。呼び出しと記録をまとめたcallClaudeTrackedを作り、今後はこれ経由で呼ぶようにするのがコツです。

// 呼び出しの結果を「利用ログ」シートに1行追記する。
// シートの列: 日時 / モデル / 入力トークン / 出力トークン / USD / 円 / 用途
function logUsage(result, purpose) {
  const sheet = SpreadsheetApp.getActive().getSheetByName("利用ログ")
    || SpreadsheetApp.getActive().insertSheet("利用ログ");

  // 見出し行が無ければ作る
  if (sheet.getLastRow() === 0) {
    sheet.appendRow(["日時", "モデル", "入力トークン", "出力トークン", "USD", "円", "用途"]);
  }

  const cost = estimateCost(result.model, result.inputTokens, result.outputTokens);
  sheet.appendRow([
    new Date(),
    result.model,
    result.inputTokens,
    result.outputTokens,
    Number(cost.usd.toFixed(6)),
    Math.round(cost.jpy),
    purpose || "",
  ]);
}

// 呼び出しと記録をまとめたラッパー。今後はこれ経由で呼ぶ。
function callClaudeTracked(userText, purpose) {
  const result = callClaude(userText);
  logUsage(result, purpose);
  return result.text;
}

// 使い方
function demo() {
  const answer = callClaudeTracked("この文を3行で要約して: ...", "議事録要約");
  Logger.log(answer);
}

第2引数のpurpose(用途)に「議事録要約」「問い合わせ分類」などを渡しておくと、後で処理の種類ごとにコストを分けて見られます。これがコスト削減の判断材料になります。

大量処理はまとめ書き込みで速くする

数百件をループで処理する場合、1件ごとにappendRowすると書き込み回数が増えて遅くなります。ログを配列に貯めておき、最後にsetValuesで一括書き込みすると、GASのシート操作が速くなります。

// 数百件を処理するときは、1件ごとにappendRowせず
// 配列に貯めて最後にまとめて書き込むと速い。
function processRowsWithTracking(texts, purpose) {
  const rows = [];
  for (const text of texts) {
    const r = callClaude(text);
    const cost = estimateCost(r.model, r.inputTokens, r.outputTokens);
    rows.push([
      new Date(), r.model, r.inputTokens, r.outputTokens,
      Number(cost.usd.toFixed(6)), Math.round(cost.jpy), purpose || "",
    ]);
  }

  const sheet = SpreadsheetApp.getActive().getSheetByName("利用ログ");
  if (sheet.getLastRow() === 0) {
    sheet.appendRow(["日時", "モデル", "入力トークン", "出力トークン", "USD", "円", "用途"]);
  }
  // まとめて1回で書き込む
  sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length)
    .setValues(rows);
}

記録処理自体は軽いので料金には影響しませんが、シートへの書き込み回数はGASの実行時間に効いてきます。件数が多いほど、まとめ書き込みの効果が大きくなります。

月ごとに料金を自動集計する

溜まった利用ログを読み、当月の合計料金とモデル別の内訳を集計します。日時の年・月が当月と一致する行だけを足し合わせるだけのシンプルな処理です。

// 利用ログを読み、当月の合計とモデル別内訳を集計する。
function summarizeThisMonth() {
  const sheet = SpreadsheetApp.getActive().getSheetByName("利用ログ");
  const values = sheet.getDataRange().getValues();
  const header = values.shift(); // 見出し行を除く

  const now = new Date();
  const y = now.getFullYear();
  const m = now.getMonth(); // 0始まり

  let totalUsd = 0;
  let totalJpy = 0;
  const byModel = {}; // モデル別のUSD合計

  for (const row of values) {
    const date = row[0];
    if (!(date instanceof Date)) continue;
    if (date.getFullYear() !== y || date.getMonth() !== m) continue;

    const model = row[1];
    const usd = Number(row[4]) || 0;
    const jpy = Number(row[5]) || 0;
    totalUsd += usd;
    totalJpy += jpy;
    byModel[model] = (byModel[model] || 0) + usd;
  }

  Logger.log("今月の合計: $" + totalUsd.toFixed(2) + " / 約" + Math.round(totalJpy) + "円");
  Object.keys(byModel).forEach((model) => {
    Logger.log("  " + model + ": $" + byModel[model].toFixed(2));
  });

  return { totalUsd: totalUsd, totalJpy: totalJpy, byModel: byModel };
}

返り値として合計と内訳を返しておくと、次のステップの予算チェックからも呼び出せます。用途列(purpose)で同じように集計すれば、「どの処理が一番コストを食っているか」も一目で分かります。

予算超過をメールで通知する

集計ができれば、あとは月の予算を決め、しきい値を超えたら通知するだけです。予算の8割に達したらメールを飛ばすようにしておけば、月末の請求前に手を打てます。時間主導トリガーで毎日1回checkBudgetを回しておくのがおすすめです。

// 予算のしきい値を超えたらメールで通知する。
// 時間主導トリガー(毎日1回)で回しておくと、
// 月末の請求前に気づける。
const MONTHLY_BUDGET_USD = 20; // 月の予算(ドル)

function checkBudget() {
  const summary = summarizeThisMonth();
  const ratio = summary.totalUsd / MONTHLY_BUDGET_USD;

  // 予算の8割を超えたら通知
  if (ratio >= 0.8) {
    const to = Session.getActiveUser().getEmail();
    const subject = "[Claude API] 今月の利用が予算の" + Math.round(ratio * 100) + "%に到達";
    const body =
      "今月のClaude API利用状況\n" +
      "合計: $" + summary.totalUsd.toFixed(2) +
      " / 約" + Math.round(summary.totalJpy) + "円\n" +
      "予算: $" + MONTHLY_BUDGET_USD + "\n";
    MailApp.sendEmail(to, subject, body);
  }
}

メールの代わりにSlackへ通知したい場合は、MailApp.sendEmailの部分を、Incoming WebhookへのUrlFetchApp.fetchに差し替えれば同じ仕組みで実現できます。しきい値は「8割」「10割」の2段階にしておくと、予兆と超過を分けて把握できます。

まとめ

Claude APIのコスト管理は、レスポンスのusageから正確なトークン数を取り出し、モデル別単価で料金に換算してログに残すのが基本です。呼び出しをラップした記録関数を通せば、あとは月次集計と予算アラートを足すだけで、使った分がリアルタイムに見える運用になります。単価は改定されるため公式ページで最新値を確認すること、金額はあくまで概算であることを押さえておけば、安心してAI自動化を本番で回せます。

よくある質問

API呼び出しの成功レスポンス(JSON)には usage という項目が含まれ、その中の input_tokens(送信した入力トークン数)と output_tokens(生成された出力トークン数)で正確な消費量が分かります。GAS側ではレスポンスをJSON.parseし、body.usage.input_tokens / body.usage.output_tokens を読むだけです。文字数から推測する必要はなく、実際に課金対象となった値をそのまま取得できます。

料金は「入力トークン数 × 入力単価 + 出力トークン数 × 出力単価」で概算できます。単価は通常「100万トークンあたり◯ドル」で表記されるため、トークン数を100万で割って単価をかけます。単価はモデルごとに異なるので、モデル名をキーにした単価テーブルをコードに持ち、呼び出したモデルに応じて参照します。ただし単価は変わることがあるため、必ず公式の料金ページで最新の値を確認して設定してください。

課金の単位がトークンだからです。トークンは単語や文字の断片を表す単位で、日本語は1文字が複数トークンになることもあり、文字数と単純に比例しません。文字数から料金を推測すると誤差が大きくなります。レスポンスのusageは実際に課金された正確なトークン数なので、これを記録するのが最も正確です。

1回の記録はスプレッドシートへの1行追記なので負荷はごくわずかです。ただし数百件をループ処理する場合、1件ごとにappendRowすると書き込み回数が増えて遅くなります。件数が多いときはログを配列に貯めておき、最後にsetValuesでまとめて書き込むと高速です。集計処理は件数が増えても月1回など低頻度で回せば問題ありません。

月次集計の関数の中で当月の合計料金を計算し、あらかじめ決めた予算のしきい値を超えたらメールやSlackで通知する処理を足せば実現できます。時間主導トリガーで毎日1回集計・チェックを回しておけば、月末に請求額を見て驚く前に気づけます。しきい値は「予算の8割」など早めに設定しておくと安心です。

AI×GASの業務自動化を
相談する。

Claude APIを使った自動化は、コストの可視化と予算管理まで含めて設計すると安心して本番運用できます。要件整理から運用設計までご相談いただけます。

AI×GAS自動化を見る