「Claude API を使ったプロダクトで収益化できないか」と考え始めると、最初にぶつかる壁が課金設計です。定額制なら計算しやすいのですが、API のコストはユーザーの使い方によって大きくばらつきます。重いプロンプトを1日100回送るユーザーが1人いるだけで、固定プランの利益が吹き飛ぶことはよくある話です。
個人開発で Claude API を使ったサービスを作り、実際に Stripe の従量課金を組み込んだ経験から言うと、「最初から完璧な設計を目指さない」ことが大切だと感じています。ここでは動く最小構成を最短で実装するための具体的なコードと、実際に本番で詰まったポイントを共有します。
定額制 vs 従量制 — 個人開発における現実的な選択
定額制の分かりやすさは魅力的ですが、Claude API を使ったサービスには特有のリスクがあります。
ユーザーが1回のリクエストで何トークン消費するかは、入力の長さによって桁が変わることがあります。短い質問なら 200 トークン、長い文書の要約なら 8,000 トークン。月額 980 円のプランを設計したとき「短い質問しかしないユーザー」を想定していたら、文書処理を大量に行うユーザーが来た瞬間に赤字になります。
従量課金はこの問題を構造的に回避します。消費したトークン数に応じて課金するため、ヘビーユーザーが来ても利益率が変わりません。
もう一つ個人的に気に入っている理由があります。最初のユーザーが付いてから「誰がどのくらい使うか」の実データが集まり、適切な月額プランの設計に使えるのです。従量制で始めて、データを見てから定額プランに移行する判断ができます。
Claude API の料金体系とコスト計算も参考にしながら、自分のサービスに合った課金モデルを考えてみてください。
Stripe Meter Events の仕組みを理解する
Stripe の従量課金は、現在は「Meter Events」という仕組みで実装します。以前の Usage Records API から移行が完了しており、新規実装では Meter を使うのが推奨です。
仕組みはシンプルです。
- Meter: 計測するイベントの定義(例:「トークン消費量」を集計する定義体)
- Meter Event: 実際の使用量を Stripe に送信するアクション
- Subscription + Price: Meter に紐づいた従量課金プライス
ユーザーが Claude API を呼び出すたびに、レスポンスから消費トークン数を取得して Stripe に送信するだけです。月末になると Stripe が自動で集計し、登録済みのカードに請求を行います。
Claude API でトークン数を取得する
Claude API のレスポンスには、消費トークン数が標準で含まれています。response.usage オブジェクトを見るだけで取得できます。
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: "こんにちは" }],
});
// トークン使用量を取得
const inputTokens = response.usage.input_tokens;
const outputTokens = response.usage.output_tokens;
const totalTokens = inputTokens + outputTokens;
console.log(
`消費トークン: 入力 ${inputTokens} + 出力 ${outputTokens} = 合計 ${totalTokens}`
);
// 例: 消費トークン: 入力 12 + 出力 38 = 合計 50プロンプトキャッシングを有効にしている場合は cache_read_input_tokens も含まれますが、最小構成ではひとまず入力と出力の合計だけ計測すれば十分です。詳しいトークン計算の最適化は Claude API のトークンカウントとコスト最適化を参照してください。
最小構成の実装 — Node.js 完全コード
以下が動く最小構成です。Claude API の呼び出しから Stripe Meter Event の送信まで、一つの関数にまとめました。
// claude-billing.js
import Anthropic from "@anthropic-ai/sdk";
import Stripe from "stripe";
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
// Stripe ダッシュボードで作成した Meter のイベント名
const METER_EVENT_NAME = "claude_tokens";
/**
* Claude API を呼び出し、消費トークン数を Stripe に記録する
* @param {string} stripeCustomerId - Stripe の顧客 ID(例: "cus_XXXXXXXXXX")
* @param {string} userMessage - ユーザーのメッセージ
*/
async function callClaudeWithBilling(stripeCustomerId, userMessage) {
// 1. Claude API を呼び出す
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: userMessage }],
});
const totalTokens =
response.usage.input_tokens + response.usage.output_tokens;
// 2. Stripe Meter Event を送信する(課金記録)
try {
await stripe.v2.billing.meterEvents.create({
event_name: METER_EVENT_NAME,
payload: {
value: String(totalTokens), // 文字列型で渡す必要がある
stripe_customer_id: stripeCustomerId,
},
});
console.log(
`✅ 課金記録完了: ${totalTokens} トークン / 顧客 ${stripeCustomerId}`
);
} catch (billingError) {
// 課金記録の失敗は必ずログに残す(サイレント漏れを防ぐ)
console.error(`⚠️ 課金記録失敗: ${billingError.message}`, {
customerId: stripeCustomerId,
tokens: totalTokens,
timestamp: new Date().toISOString(),
});
// TODO: リトライキューへの追加
}
return {
content: response.content[0].text,
tokensUsed: totalTokens,
};
}
// 使用例
const result = await callClaudeWithBilling(
"cus_XXXXXXXXXX", // Stripe の顧客 ID
"Next.js のパフォーマンス最適化について教えてください"
);
console.log("応答:", result.content);
console.log("消費トークン:", result.tokensUsed);期待する動作: Claude から応答を受け取り、Stripe のダッシュボードに「claude_tokens」イベントが記録されます。月末に自動集計されて請求書が生成されます。
Stripe ダッシュボードの設定手順
コードを動かす前に、Stripe 側で以下を設定します。一度設定すれば変更は不要です。
Step 1: Meter を作成する
Stripe ダッシュボード → Billing → Meters → 「+ Create meter」
- Event name:
claude_tokens(コードのMETER_EVENT_NAMEと一致させる) - Default aggregation:
sum(月間の合計トークン数を集計する)
作成後に表示される Meter ID を STRIPE_METER_ID 環境変数に保存しておきます(後でプライス作成時に使います)。
Step 2: 従量課金プライスを作成する
Products → 「+ Add product」→ Pricing model で「Usage-based」を選択
- Meter: 先ほど作成した
claude_tokensを選択 - Per unit: 1,000 トークンあたりの金額を設定
価格の参考: claude-sonnet-4-6 の場合、入力が $3/1M トークン、出力が $15/1M トークンです。サービスのマージンを乗せた価格設定が必要です。
Step 3: ユーザー登録時にサブスクリプションを作成する
// ユーザーがサインアップした際にサブスクリプションを作成
const subscription = await stripe.subscriptions.create({
customer: stripeCustomerId,
items: [{ price: process.env.STRIPE_USAGE_PRICE_ID }],
});この3ステップで、あとはコードが自動的に課金を記録します。
本番環境で詰まりやすいポイント
実装して本番に出した後に気づいたことを3つ共有します。
① Meter Event の送信失敗は課金漏れになる
Claude API の呼び出しは成功したが、Stripe へのイベント送信がネットワークエラーで失敗するケースがあります。この場合、実際のトークン消費が課金記録に残りません。
上のコードでは try/catch でエラーをログに残していますが、本番では専用のログ基盤(Datadog、CloudWatch など)に送るか、失敗したイベントをデータベースに保存してリトライする仕組みが必要になります。まずはログだけでも残すことを徹底してください。
② stripe.v2 は Stripe SDK のバージョン確認が必要
Meter Events は stripe.v2.billing.meterEvents.create() を使います。これは古い stripe.billing や stripe.usageRecords とは別の API です。Stripe SDK が古いと v2 プロパティが存在しないため、npm install stripe@latest で最新版に更新してから実装を始めることをお勧めします。
③ Stripe 顧客 ID の管理設計は最初から決める
stripeCustomerId を関数に渡す設計にしましたが、実際のアプリではユーザーのセッションから顧客 ID を取得する仕組みが必要です。users テーブルに stripe_customer_id カラムを追加し、ログイン時に取得するパターンが定番です。顧客 ID の管理設計を後から変えると、課金記録の紐づけが複雑になるため、最初から決めておくことをお勧めします。
安心して使ってもらうための仕上げ — 上限設計と Webhook
最小構成が動いたら、本番に出す前に二つだけ足しておきたいものがあります。使いすぎを防ぐ上限と、請求サイクルの節目を受け取る Webhook です。
従量課金はユーザーから見ると「いくら請求されるか分からない」という不安が残ります。月間の利用上限を設けておくと、その不安が和らぎ、導入のハードルが下がります。私自身、個人開発で従量課金を入れたとき、最初の数日で一部のユーザーが想定の何倍も使い、ヒヤリとしたことがありました。上限チェックを後から足すのは配線が増えて手間なので、課金を組む段階で一緒に入れておくことをお勧めします。
// DB に月間使用量を記録し、上限をチェックするミドルウェア
export async function checkAndTrackUsage(
userId: string,
estimatedCredits: number
): Promise<{ allowed: boolean; currentUsage: number; limit: number }> {
const plan = await getUserPlan(userId);
const currentUsage = await getMonthlyUsage(userId);
const limit = plan.monthlyCredits; // 例: ライトプランなら1000クレジット
if (currentUsage + estimatedCredits > limit) {
return { allowed: false, currentUsage, limit };
}
await incrementMonthlyUsage(userId, estimatedCredits);
return { allowed: true, currentUsage: currentUsage + estimatedCredits, limit };
}
// API エンドポイントでの利用例
export async function POST(request: Request) {
const { prompt, userId } = await request.json();
const usageCheck = await checkAndTrackUsage(userId, 10); // 推定10クレジット
if (!usageCheck.allowed) {
return Response.json(
{
error: "月間利用上限に達しました。プランをアップグレードしてください。",
currentUsage: usageCheck.currentUsage,
limit: usageCheck.limit,
},
{ status: 429 }
);
}
const subscriptionItemId = await getSubscriptionItemId(userId);
const result = await callClaudeWithMetering(prompt, userId, subscriptionItemId);
return Response.json({ result });
}上限に達したら 429 を返し、アップグレードへの導線につなげます。エラーの中に現在の使用量と上限を含めておくと、ユーザーが自分の状況を把握しやすくなります。
請求サイクルの節目では、Stripe からの Webhook を受け取って月間使用量をリセットします。invoice.payment_succeeded でカウンタをゼロに戻し、invoice.payment_failed のときはサービスを一時停止する——この二つを押さえれば最小限は回ります。
// app/api/stripe-webhook/route.ts
export async function POST(request: Request) {
const sig = request.headers.get("stripe-signature")!;
const body = await request.text();
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch {
return new Response("Webhook signature verification failed", { status: 400 });
}
switch (event.type) {
case "invoice.payment_succeeded": {
const invoice = event.data.object as Stripe.Invoice;
await resetMonthlyUsage(invoice.customer as string); // 月間使用量をリセット
break;
}
case "invoice.payment_failed": {
const invoice = event.data.object as Stripe.Invoice;
await suspendUserService(invoice.customer as string); // 支払い失敗時は停止
break;
}
}
return new Response("ok");
}Webhook の署名検証(constructEvent)を省くと、誰でも偽のイベントを送れてしまいます。STRIPE_WEBHOOK_SECRET を使った検証は必ず入れてください。
最後に一つ、報告のタイミングについてです。Claude のストリーミングレスポンスを使っている場合、トークン数は全チャンクを受信し終えてから確定します。途中で使用量を報告しようとすると、接続が切れたときに過少報告になります。安全なのは「レスポンス完了後に一括で報告する」設計です。リアルタイムに使用量を見せたいときは、DB 側でカウントして画面に出し、Stripe への報告は完了後に非同期で行うとよいでしょう。
まずテストモードで1件送信してみる
Stripe のテストモード(sk_test_...)では実際の課金は発生しません。環境変数をテスト用キーに設定して、実際にリクエストを1件送ってみてください。
Stripe ダッシュボード → Billing → Usage → 該当の Meter でイベントが記録されていれば、実装は成功です。本番への切り替えは sk_live_... に変更するだけです。
より本格的な従量課金の実装(リトライ処理・使用量上限・プラン切り替え)については、Claude API を使った個人 SaaS の収益化も参考にしてみてください。
個人開発では「完璧な設計よりも動く実装を早く出す」ことが大切だと思っています。この最小構成から始めて、実際のユーザーデータを見ながら改善していくアプローチが、長続きするサービスにつながりやすいのではないでしょうか。まず1件のテストリクエストを送ることから始めてみてください。