How to Add Usage-Based Billing with Stripe Meters 2026
·StarterPick Team
stripeusage-billingstripe-meterssaas-boilerplatebilling2026
TL;DR
Stripe Meters (GA in 2024) is the cleanest way to implement usage-based billing in 2026. Create a meter → report usage via stripe.billing.meterEvents.create() → attach a metered price to a subscription → Stripe handles aggregation and billing at period end. Works for AI tokens, API calls, storage, emails sent — any countable resource.
Key Takeaways
- Stripe Meters: New GA feature — simpler than legacy
subscriptionItems.createUsageRecord - Setup: Create meter in dashboard → create metered price → subscribe user with meter
- Report usage:
stripe.billing.meterEvents.create()for each event (or batch) - Query usage:
stripe.billing.meters.listEventSummaries()to show customers their usage - Billing: Stripe aggregates at end of billing period (month), charges automatically
Step 1: Set Up Stripe Meter
// Run once to create the meter (or do it in Stripe dashboard):
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// Create meter for AI token usage:
const meter = await stripe.billing.meters.create({
display_name: 'AI Tokens',
event_name: 'ai_tokens_used', // Event name you'll report
default_aggregation: { formula: 'sum' },
customer_mapping: {
event_payload_key: 'stripe_customer_id', // Field in event payload
type: 'by_id',
},
value_settings: { event_payload_key: 'tokens' }, // Field for the count
});
console.log('Meter ID:', meter.id);
// Save this ID in your config/env: STRIPE_AI_TOKENS_METER_ID=mtrid_xxx
// Create metered price (attach to a product):
const price = await stripe.prices.create({
product: 'prod_yourProductId',
currency: 'usd',
billing_scheme: 'per_unit',
unit_amount: 2, // $0.002 per token (0.2 cents)
recurring: {
interval: 'month',
usage_type: 'metered',
meter: process.env.STRIPE_AI_TOKENS_METER_ID!,
},
});
console.log('Metered Price ID:', price.id);
// STRIPE_AI_TOKENS_PRICE_ID=price_xxx
Step 2: Subscribe Users to Metered Price
// lib/stripe-billing.ts:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function subscribeUserToUsagePlan(userId: string) {
const user = await db.user.findUnique({ where: { id: userId } });
// Create subscription with base price + metered price:
const subscription = await stripe.subscriptions.create({
customer: user!.stripeCustomerId!,
items: [
// Base monthly fee (optional):
{ price: process.env.STRIPE_PRO_BASE_PRICE_ID },
// Metered usage (tokens):
{ price: process.env.STRIPE_AI_TOKENS_PRICE_ID },
],
});
await db.user.update({
where: { id: userId },
data: { stripeSubscriptionId: subscription.id },
});
}
Step 3: Report Usage
// lib/usage-tracking.ts — report usage to Stripe Meters:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function reportAITokenUsage(
stripeCustomerId: string,
tokens: number
) {
await stripe.billing.meterEvents.create({
event_name: 'ai_tokens_used', // Must match meter's event_name
payload: {
stripe_customer_id: stripeCustomerId, // Must match customer_mapping key
tokens: tokens.toString(), // Must be a string
},
});
}
// Call this after each AI operation:
// In your AI route handler:
const result = await streamText({ model: openai('gpt-4o-mini'), ... });
result.onFinish(async ({ usage }) => {
await reportAITokenUsage(
session.user.stripeCustomerId,
usage.promptTokens + usage.completionTokens
);
});
Step 4: Show Usage to Customers
// app/api/billing/usage/route.ts:
export async function GET() {
const session = await auth();
const user = await db.user.findUnique({ where: { id: session!.user.id } });
// Get current period usage from Stripe Meters:
const summaries = await stripe.billing.meters.listEventSummaries(
process.env.STRIPE_AI_TOKENS_METER_ID!,
{
customer: user!.stripeCustomerId!,
start_time: Math.floor(new Date(new Date().getFullYear(), new Date().getMonth(), 1).getTime() / 1000),
end_time: Math.floor(Date.now() / 1000),
value_grouping_window: 'day',
}
);
const totalTokens = summaries.data.reduce((sum, s) => sum + (s.aggregated_value ?? 0), 0);
const costUsd = totalTokens * 0.002 / 1000; // $0.002 per 1K tokens
return Response.json({
tokensUsed: totalTokens,
estimatedCost: costUsd.toFixed(4),
dailyBreakdown: summaries.data.map(s => ({
date: new Date(s.start_time * 1000).toISOString().split('T')[0],
tokens: s.aggregated_value,
})),
});
}
// components/billing/UsagePanel.tsx:
export function UsagePanel() {
const { data } = useSWR('/api/billing/usage');
return (
<div className="space-y-4">
<div>
<p className="text-2xl font-bold">{data?.tokensUsed.toLocaleString()}</p>
<p className="text-sm text-muted-foreground">AI tokens used this month</p>
</div>
<div>
<p className="text-xl">${data?.estimatedCost}</p>
<p className="text-sm text-muted-foreground">estimated charges</p>
</div>
<Progress value={(data?.tokensUsed / 1_000_000) * 100} className="h-2" />
</div>
);
}
Add Budget Limits (Optional)
// Prevent runaway costs with soft limits:
export async function checkAIBudget(userId: string): Promise<{ allowed: boolean; remaining: number }> {
const user = await db.user.findUnique({ where: { id: userId } });
const monthlyBudgetTokens = user?.plan === 'pro' ? 1_000_000 : 50_000;
// Get current month usage from DB (faster than Stripe API):
const used = await db.aiUsage.aggregate({
where: {
userId,
createdAt: { gte: new Date(new Date().getFullYear(), new Date().getMonth(), 1) },
},
_sum: { tokens: true },
});
const usedTokens = used._sum.tokens ?? 0;
return {
allowed: usedTokens < monthlyBudgetTokens,
remaining: Math.max(0, monthlyBudgetTokens - usedTokens),
};
}
Decision Guide
Use Stripe Meters if:
→ Billing per AI token, API call, email sent, or storage GB
→ Want Stripe to handle aggregation + billing automatically
→ New project — meters are the modern approach
Use legacy subscriptionItems.createUsageRecord if:
→ Existing Stripe integration before Meters GA
→ Gradual migration path needed
Use DB-only tracking (no Stripe Meters) if:
→ Just enforcing limits (no billing per usage)
→ Free tier with token caps, pro tier with unlimited
Find SaaS boilerplates with Stripe billing at StarterPick.