朝起きて Stripe ダッシュボードより先に Anthropic の請求画面を開く日が、組み込んで最初の数か月は何度もありました。アプリに Claude API をつないだ瞬間、自分の財布と従量課金のメーターが直結します。「収益 − サーバー代 = 利益」という単純な式で考えてきた方ほど、API 利用料という変動コストの読みづらさに戸惑うはずです。
この記事は、壁紙アプリやヒーリングアプリに Claude API を組み込みながら覚えた、月額予算を破綻させないためのコスト管理の現場メモです。「軽い機能のつもりで Sonnet を選んだら、1 日で 8,000 円が消えていた」という失敗を、これから組み込む方が繰り返さずに済むように書き残します。
最初にお伝えしたいのは、コスト管理は節約術ではなく、測る仕組みと止める仕組みの話だということです。順に辿ります。
なぜ個人開発者の予算は崩れるのか
法人向けの API コスト解説は世の中に多くありますが、個人開発者の財布感覚で読むと、どこか噛み合いません。前提が違うからです。
法人なら「1 リクエスト 0.05 ドル」と聞いて単価で計画を立てられます。個人開発者は「月に 5 万円までしか使えない」という天井から逆算します。守りたいのは単価ではなく総額——これが個人開発のリアルです。
私の経験では、予算が崩れる原因はだいたい次の 3 つに集約されます。
第一に、ユーザーあたりの利用回数の見積もりが外れること。「平均で 1 日 5 回使うだろう」と思っていた機能が、ヘビーユーザー数人によって 1 日 200 回呼ばれていた、というケースです。レビューが増えて喜んでいる裏で、請求額だけが伸びていきます。
第二に、コンテキストが肥大化すること。会話履歴を全部送り続ける素朴な実装では、10 ターン目の入力は初回の 10 倍を優に超えます。出力トークンの単価ばかりが気になりますが、請求を押し上げるのは積み上がった入力トークンの側です。
第三に、リトライの罠にはまること。私が一度やってしまったのは、クライアント側のタイムアウトを短く設定したまま「失敗したら 3 回まで再送」としていたことでした。長い出力を生成している最中に私の側が切って再送するので、同じ重い依頼が重なって走ります。エラーログだけが増え、請求は減りませんでした。エラーとして返ったリクエストが課金されるかどうかは状況によって異なるため、「失敗したから無料のはず」と決めつけないほうが安全です。
月額予算から逆算する見積もりフレームワーク
まずは許容できる月額を決めます。私は新しい API 機能を組み込むとき、「その機能の API 費用は、アプリ全体の粗利の 30% を超えない」という上限を自分のルールにしています。超えそうなら、機能を絞るか、課金ユーザー限定にするか、組み込みそのものを見送ります。
許容月額が決まったら、次の式でざっくり逆算します。
許容月額(円)÷ 為替レート(例: 1ドル=155円)÷ 1 リクエストあたり想定コスト(USD)
= 月間許容リクエスト数
月間許容リクエスト数 ÷ 30 日 ÷ 想定 DAU(デイリーアクティブユーザー)
= 1 ユーザーあたり 1 日に許容できるリクエスト数
たとえば月 30,000 円までと決め、Sonnet 4.6 で 1 リクエストあたり平均 0.01 USD かかると見積もるなら、月間約 19,000 リクエストです。DAU 100 人なら 1 ユーザーあたり 1 日 6 回強が上限になります。
ここで「6 回って案外少ないな」と感じることが大事です。多くの方は「フリーミアムで全員に AI 機能を開放しよう」と考えますが、この計算をすると、無料ユーザーは 1 日 3 回まで、残りは課金ユーザーに回す、といった設計の必然性が見えてきます。
為替は動きます。私は円換算の予算を月初に一度だけ固定レートで引き直し、月の途中では動かさないことにしています。月中に何度も式を回すと、数字の揺れに気を取られて判断が鈍るからです。
1 リクエストの単価は「測ってから」置く
上の 0.01 USD は仮置きの数字です。本番に出す前に、実際の入力と出力の長さで 20〜30 回ほど叩き、usage の平均を取って式に戻してください。ここを想像のまま進めると、天井から逆算した設計そのものが砂上の楼閣になります。
事前の見積もりには、トークン数を数える API(count_tokens)が使えます。入力側の長さは送る前に分かるので、「この依頼は上限を超えるから送らない」という判断を、課金が発生する前に下せます。
| 見積もりの要素 | 測り方 | 落とし穴 |
|---|---|---|
| 入力トークン | 送信前に count_tokens、送信後は usage.input_tokens | 履歴・システムプロンプト・ツール定義を含め忘れる |
| 出力トークン | usage.output_tokens の平均と 95 パーセンタイル | 平均だけで置くと長文の日に超える |
| 呼び出し回数 | feature 別・ユーザー別のログ | ヘビーユーザーが平均を押し上げる |
| 再送・失敗分 | リトライ回数をログに残す | 見積もりに入れず、障害の日だけ跳ねる |
モデル別の単価感覚——「持っていくモデル」を先に決める
公式の料金表は誰でも読めるので、ここでは実装する側の体感を共有します。一例として、2026 年前半に私が頭に入れていた目安は次のとおりです(単価は改定されるため、組み込む直前に必ず公式の料金表で引き直してください)。
- Claude Haiku 4.5 — 入力 $1 / 出力 $5(100 万トークンあたり)
- Claude Sonnet 4.6 — 入力 $3 / 出力 $15
- Claude Opus 4.6 — 入力 $5 / 出力 $25
Haiku と Sonnet のあいだには 3 倍の開きがあります。「精度は少し落ちても Haiku で十分」な用途を Sonnet で組んでしまうと、同じ機能の費用が 3 倍になります。新しい世代が出るたびに各モデルの単価と得意不得意は入れ替わるので、この倍率そのものは暗記せず、月に一度、自分のアプリの代表的な依頼を各モデルで回して比べる習慣のほうが長持ちします。
私の手順は単純です。実装の前に「このタスクは小さいモデルで回るか」を先に試します。ユーザーへの定型的な返答や分類は小さいモデルへ、コードレビューや込み入った要約のように本当に判断力が要る場面だけ上位モデルへ振っています。
もうひとつ、リアルタイム性が不要な処理——たとえば夜間の一括分類や翻訳の下訳——は Batch API に回すと、通常より安い料金で処理できます。ユーザーを待たせない処理は、まず「バッチにできないか」を疑ってみてください。
測る仕組み: トークン消費のロギング設計
予算管理と言うと節約のテクニックに目が向きますが、本当に効くのは「いま自分がいくら使っているか」を正確に知る仕組みを最初に作ることです。これがないと、あらゆる節約が推測の域を出ません。
私は新しい API 機能を実装するとき、必ず最初にロギング層を入れます。SDK はレスポンスに usage を返してくれるので、毎回保存します。失敗時の記録も同じ関数に入れておくのがコツです。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
// 単価は改定されるので、設定ファイルに出して月 1 回見直す(USD / 100 万トークン)
const PRICES: Record<string, { in: number; out: number }> = {
"claude-sonnet-4-6": { in: 3, out: 15 },
"claude-haiku-4-5": { in: 1, out: 5 },
};
async function callClaudeWithLogging(params: {
userId: string;
feature: string;
model: string;
messages: Anthropic.MessageParam[];
maxTokens: number; // 出力の天井。呼び出し側で必ず明示させる
}) {
const startTime = Date.now();
try {
const response = await client.messages.create({
model: params.model,
max_tokens: params.maxTokens,
messages: params.messages,
});
const u = response.usage;
const price = PRICES[params.model];
const costUsd = price
? (u.input_tokens * price.in + u.output_tokens * price.out) / 1_000_000
: null; // 未登録モデルは null にして、集計時に気づけるようにする
await db.apiUsageLog.create({
data: {
userId: params.userId,
feature: params.feature,
model: params.model,
status: "ok",
inputTokens: u.input_tokens,
outputTokens: u.output_tokens,
cacheReadTokens: u.cache_read_input_tokens ?? 0,
cacheCreationTokens: u.cache_creation_input_tokens ?? 0,
costUsd,
elapsedMs: Date.now() - startTime,
timestamp: new Date(),
},
});
return response;
} catch (err) {
// 失敗も残す。「障害の日に何回叩いたか」は後から効いてくる
await db.apiUsageLog.create({
data: {
userId: params.userId,
feature: params.feature,
model: params.model,
status: "error",
elapsedMs: Date.now() - startTime,
timestamp: new Date(),
},
});
throw err;
}
}ポイントは 3 つあります。feature 列で「どの機能で使われたか」を識別できるようにすること。max_tokens を呼び出し側に必ず決めさせ、出力の天井を機能ごとに持つこと。そして単価表を設定に出して、モデルを替えた日に取りこぼさないことです。
feature がないと、月末に「今月は請求が 2 倍になりました」と気づいても原因を特定できません。私は「画像の一言生成」「対話モード」「翻訳」のように粒度を分けて記録しています。
このログを毎晩のバッチで集計すれば、機能別・ユーザー別の消費が見えてきます。私は集計結果を自分宛の通知に毎朝流し、前日比で跳ねた日だけ中身を開くようにしています。見る頻度を落とすより、見る負担を下げるほうが続くからです。
あわせて、Anthropic のコンソール側にも月額の上限(Spend limit)を設定しておくことをおすすめします。自分のアプリ側の制御が壊れたときの最後の防波堤になります。設定できる単位や名称は変わることがあるので、コンソールの現行の画面で確認してください。
止める仕組み: ユーザー単位の上限を自前で持つ
プロバイダー側のレート制限はリクエスト数や TPM(1 分あたりのトークン数)の制御で、コスト管理にはあまり役立ちません。「1 ユーザーが今日いくら使ったか」を制御したいなら、アプリ側で上限を持つ必要があります。
素朴な実装は、ユーザー ID をキーにした日次カウンタです。
async function checkAndIncrementUsage(userId: string, estimatedCostUsd: number) {
const today = new Date().toISOString().slice(0, 10);
const key = `usage:${userId}:${today}`;
const dailyCapUsd = await getUserDailyCap(userId); // 無料ユーザー 0.05 / 課金 1.0 など
const currentUsage = parseFloat((await kv.get(key)) ?? "0");
if (currentUsage + estimatedCostUsd > dailyCapUsd) {
throw new Error("DAILY_LIMIT_EXCEEDED");
}
await kv.put(key, String(currentUsage + estimatedCostUsd), {
expirationTtl: 86400 * 2, // 2 日後に自動削除
});
}ただし、このコードには私が一度踏んだ落とし穴があります。読んでから書くまでのあいだに別のリクエストが割り込めるので、同じユーザーが同時に何件も送ると、上限を少し超えて通ってしまいます。Cloudflare KV は結果整合の保存先でもあり、書いた直後に別の場所から読むと古い値が返ることがあります。
厳密に止めたい場合は、INCRBY のような原子的な加算を持つ Redis か、1 ユーザー 1 インスタンスで直列に処理できる Durable Objects のような仕組みに寄せてください。数セントのズレを許容できる無料枠なら、上のコードのままでも実害は小さいと考えています。
「無料ユーザーは 1 日 5 セント分」のような形にしておけば、悪意のある利用や異常なヘビーユーザーが現れても、1 日あたりの被害が限定されます。私のアプリでは、無料ユーザーに小さく、課金ユーザーにはその十数倍のキャップを置いています。荒らしを除けばほとんどのユーザーは上限に届かないので、体験を損なわずに財布を守れています。
コンテキストの肥大化を抑える
「肥大化した入力が請求を押し上げる」と先に書きました。対策は 3 段階で考えると整理しやすいです。
| 段階 | やること | 向く場面 |
|---|---|---|
| 1. 直近 N ターンだけ送る | 古い履歴を切り捨てる(往復単位で) | 雑談・一問一答に近い機能 |
| 2. 古い履歴を要約して置き換える | 小さいモデルで要約し、先頭に 1 つだけ置く | 長く続く相談・作業の文脈 |
| 3. 必要なものだけ都度取りにいく | 履歴ではなく検索や状態の参照に切り替える | ツール連携が増えてきたとき |
段階 1 で気をつけたいのは、履歴を 1 件ずつ削ると、先頭が assistant の発言になって 400 エラーを返されることがある点です。user と assistant の往復をひと組として削ってください。
プロンプトキャッシュで効かせる節約
プロンプトキャッシュは、システムプロンプトや長文コンテキストを再利用するときに、入力側の料金を大きく下げる仕組みです。読み取りは通常入力の約 1/10 で済みます。一方で、キャッシュに書き込む呼び出しは通常より割増になります。つまり、同じ内容が短い間隔で繰り返し使われる場合にだけ得になります。
私の壁紙アプリには、ユーザーが画像を選ぶと Claude が「この画像に合う一言」を考える機能があります。システムプロンプト(口調や禁止事項)が長めなので、ここをキャッシュ対象にしました。同じユーザーが続けて何枚も試すと、2 枚目以降の入力側が軽くなります。
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 200,
system: [
{
type: "text",
text: longSystemPrompt, // ここを cache_control で指定
cache_control: { type: "ephemeral" },
},
],
messages: [{ role: "user", content: userMessage }],
});
// 効いているかは usage で確かめる
console.log(response.usage.cache_creation_input_tokens, response.usage.cache_read_input_tokens);ここで私が気づいたのは、キャッシュは書いたつもりで効いていないことが普通に起こる、ということでした。ブロックが最小トークン数に届かないと、エラーも出さずにキャッシュされません。最小値はモデルによって異なるため、公式の現行の記載を確認してください。
そして、cache_read_input_tokens が 0 のままなら効いていません。月末の請求ではなく、実装した日のうちにこの 2 つの数字を見て確かめる——それが私の確認手順です。標準の保持時間は 5 分で、それより間隔が空く使われ方なら、書き込み割増の分だけ損になる場合もあります。
失敗時のリトライ戦略を見直す
無条件のリトライは、コスト管理上の時限爆弾です。API が不安定なときに 429 や 529/503 を返され続けている状況で、強気に再送を重ねると、復旧した瞬間に溜まった呼び出しが一斉に走ります。
実装の指針として、私は次の 3 点を守っています。
ひとつめは、リトライ回数を 2 回までにすること。3 回以上の再送で救えるエラーは、経験上ほとんどありません。
ふたつめは、指数バックオフ(Exponential Backoff)に少しの揺らぎ(ジッター)を足すこと。429 のときは最低でも数秒、できれば 30 秒ほど待ってから次を試します。サーバーが retry-after を返してきたら、その値を優先します。
みっつめは、サーキットブレーカーを置くこと。直近 1 分間に 5xx が 10 回続いたら、5 分間は API 呼び出しを止めて「混み合っています」の画面に切り替えます。連鎖的なコスト増を止める、いちばん効いた仕組みです。
もうひとつ、冒頭で書いたタイムアウトの話に戻ります。長い出力を伴う機能では、クライアント側のタイムアウトを短くしないでください。ストリーミングで受け取るか、max_tokens を絞って「そもそも長く生成させない」設計にするほうが、再送で膨らむ費用を根元から断てます。
class CircuitBreaker {
private failures: number[] = [];
private openUntil: number = 0;
shouldAllow(): boolean {
return Date.now() >= this.openUntil;
}
recordFailure() {
const now = Date.now();
this.failures = this.failures.filter((t) => now - t < 60_000);
this.failures.push(now);
if (this.failures.length >= 10) {
this.openUntil = now + 5 * 60_000; // 5 分間遮断
this.failures = [];
}
}
recordSuccess() {
this.failures = [];
}
}サーキットブレーカーは、Anthropic 側の障害だけでなく、自分のコードのバグで暴走しているときにも止めてくれる安全装置になります。後者のほうが、実際にはよく起きます。
月額予算管理の現場で、実際にやっていること
ここまでの仕組みを組み合わせた、私の月次運用のリズムを書き残します。
毎週月曜の朝に、先週の API 使用量の集計を自分宛に出します。前週比で 30% 以上増えていたら、必ず原因を調べます。ほとんどは新機能のリリースか、特定ユーザーの異常な使い方です。
毎月 25 日には月末予測を計算し、許容額の 80% を超えそうなら、残りの数日に向けて機能制限の準備をします。ユーザーには「今月の利用が想定を上回ったため、月末まで一部機能の回数を制限させていただきます」と率直に伝えます。これは理解されやすく、課金プランへの案内にもつながりました。
四半期ごとに、機能別の ROI(売上への貢献 ÷ API 費用)を出します。明らかに赤字の機能は、廃止するか有料化を考えます。ある機能が無料ユーザーに人気でも ROI がマイナスなら、Pro 限定に切り替えました。離脱は思ったほど出ず、課金への転換が少し上向いた、というのが私の手触りでした。
数字を見る場所を増やしすぎないことも、続けるための線引きです。毎日見るのは前日比の 1 行だけ、週に一度は機能別、月に一度は ROI と単価表。この 3 段にしてから、確認が負担ではなくなりました。
全体を振り返って
コスト管理は精神論ではなく、計測と上限の仕組み化です。今日ひとつだけ始めるなら、usage をログに残す処理を入れてください。これだけで「気づいたら使いすぎていた」が「使いすぎる前に分かる」に変わります。
あとは数字を見て、上限を一つずつ足していくだけです。まずは自分のアプリの代表的な依頼を 20 回ほど叩いて、1 リクエストあたりの実測値を出すところから始めていただければ幸いです。私も、そこからやり直しました。
次に深掘りしたい方は、姉妹記事の Claude API を組み込んだ収益化アプリ設計 — トークン消費の見える化から課金モデルまで で、ここで紹介したログ設計を課金システムにつなげる具体例を扱っています。