Tutorial: Automated Billing for AI with Vercel AI SDK in 5 Minutes
In this tutorial, we’ll go from zero to working automated billing in under 5 minutes. You’ll need: a project with Vercel AI SDK and an Infinitum Pulse account.
Prerequisites
- Node.js 18+
- A project using the
aipackage (Vercel AI SDK) - Infinitum Pulse account (free during beta)
Step 1: Install the SDK
npm install @beinfi/pulse-sdk
The package exports two things:
@beinfi/pulse-sdk— main client for tracking and customer management@beinfi/pulse-sdk/ai— middleware for Vercel AI SDK
Step 2: Create Product and Meters
In the Pulse dashboard, create:
- Product: “My AI Agent”
- Meter 1:
input_tokens— Display: “Input Tokens” — Unit: “tokens” — Price: $0.003/1K - Meter 2:
output_tokens— Display: “Output Tokens” — Unit: “tokens” — Price: $0.008/1K
Note the Product ID and API Key (starts with sk_live_ or sk_test_).
Step 3: Set Up Environment Variables
# .env
PULSE_API_KEY=sk_test_xxxxxxxxxxxxx
PULSE_PRODUCT_ID=prod_xxxxxxxxxxxxx
OPENAI_API_KEY=sk-xxxxxxxxxxxxx
Step 4: Add the Middleware
This is where the magic happens. The middleware intercepts all model calls and tracks tokens automatically.
// lib/ai.ts
import { Pulse } from '@beinfi/pulse-sdk'
import { pulseMiddleware } from '@beinfi/pulse-sdk/ai'
import { wrapLanguageModel } from 'ai'
import { openai } from '@ai-sdk/openai'
const pulse = new Pulse(process.env.PULSE_API_KEY!)
export function createBilledModel(customerId: string) {
return wrapLanguageModel({
model: openai('gpt-4o'),
middleware: pulseMiddleware({
pulse,
customerId,
meters: {
input: 'input_tokens',
output: 'output_tokens',
},
}),
})
}
Step 5: Use the Model in Your App
With generateText (single call)
import { generateText } from 'ai'
import { createBilledModel } from '@/lib/ai'
export async function POST(req: Request) {
const { prompt, userId } = await req.json()
const model = createBilledModel(userId)
const { text } = await generateText({
model,
prompt,
})
return Response.json({ text })
}
With streamText (streaming)
import { streamText } from 'ai'
import { createBilledModel } from '@/lib/ai'
export async function POST(req: Request) {
const { messages, userId } = await req.json()
const model = createBilledModel(userId)
const result = streamText({
model,
messages,
})
// Tokens are tracked when the stream finishes
return result.toDataStreamResponse()
}
With generateObject (structured output)
import { generateObject } from 'ai'
import { createBilledModel } from '@/lib/ai'
import { z } from 'zod'
const model = createBilledModel(userId)
const { object } = await generateObject({
model,
schema: z.object({
summary: z.string(),
sentiment: z.enum(['positive', 'negative', 'neutral']),
topics: z.array(z.string()),
}),
prompt: `Analyze this review: "${reviewText}"`,
})
// Tokens tracked automatically, including the
// extra tokens from schema enforcement
Step 6: Register Customers
When a user signs up in your app, register them as a customer:
// lib/pulse.ts
import { Pulse } from '@beinfi/pulse-sdk'
const pulse = new Pulse(process.env.PULSE_API_KEY!)
export async function registerCustomer(user: {
id: string
name: string
email: string
}) {
await pulse.metering.createCustomer(process.env.PULSE_PRODUCT_ID!, {
externalId: user.id,
name: user.name,
email: user.email,
metadata: {
plan: 'free',
source: 'signup',
},
})
}
Step 7: Query Usage (Optional)
If you want to show consumption in your dashboard:
// api/usage/route.ts
import { Pulse } from '@beinfi/pulse-sdk'
const pulse = new Pulse(process.env.PULSE_API_KEY!)
export async function GET(req: Request) {
const url = new URL(req.url)
const userId = url.searchParams.get('userId')!
const usage = await pulse.metering.getUsage({
customerId: userId,
})
return Response.json(usage)
}
Response:
{
"data": [
{
"meterId": "input_tokens",
"meterName": "Input Tokens",
"totalValue": "1523400",
"count": 342
},
{
"meterId": "output_tokens",
"meterName": "Output Tokens",
"totalValue": "487200",
"count": 342
}
]
}
Step 8: Done! What Happens Now?
With everything set up, the automated flow is:
- Day to day: Each model call tracks tokens via middleware
- End of month: Infinitum aggregates usage per customer
- Invoice generated: Detailed line items (input tokens, output tokens)
- Payment link: Automatically sent to customer
- Payment: Customer pays in USDC/USDT via link
- You receive: Funds in your wallet, instant settlement
Verifying It Works
Local test
// test-billing.ts
import { createBilledModel } from './lib/ai'
import { generateText } from 'ai'
async function test() {
const model = createBilledModel('test_user_123')
const { text, usage } = await generateText({
model,
prompt: 'Explain quantum computing in 2 sentences.',
})
console.log('Response:', text)
console.log('Tokens:', usage)
console.log('Check your Pulse dashboard for the tracked event!')
}
test()
In the dashboard
After running the test, go to the Pulse dashboard and you’ll see:
- The registered metering event
- Input and output tokens separated
- Associated customer
- Value calculated based on defined prices
Multi-Model Billing
If your agent uses different models, set up separate middlewares:
import { Pulse } from '@beinfi/pulse-sdk'
import { pulseMiddleware } from '@beinfi/pulse-sdk/ai'
import { wrapLanguageModel } from 'ai'
import { openai } from '@ai-sdk/openai'
import { anthropic } from '@ai-sdk/anthropic'
const pulse = new Pulse(process.env.PULSE_API_KEY!)
// GPT-4o — more expensive
export const gpt4o = (customerId: string) =>
wrapLanguageModel({
model: openai('gpt-4o'),
middleware: pulseMiddleware({
pulse,
customerId,
meters: {
input: 'gpt4o_input', // $0.005/1K
output: 'gpt4o_output', // $0.015/1K
},
}),
})
// GPT-4o-mini — cheaper
export const gpt4oMini = (customerId: string) =>
wrapLanguageModel({
model: openai('gpt-4o-mini'),
middleware: pulseMiddleware({
pulse,
customerId,
meters: {
input: 'gpt4o_mini_input', // $0.00015/1K
output: 'gpt4o_mini_output', // $0.0006/1K
},
}),
})
// Claude Sonnet
export const claude = (customerId: string) =>
wrapLanguageModel({
model: anthropic('claude-sonnet-4-5-20250929'),
middleware: pulseMiddleware({
pulse,
customerId,
meters: {
input: 'claude_input', // $0.003/1K
output: 'claude_output', // $0.015/1K
},
}),
})
Session-Based Tracking
For agents that make multiple calls per “session” (e.g., agent loops):
import { Pulse } from '@beinfi/pulse-sdk'
const pulse = new Pulse(process.env.PULSE_API_KEY!)
async function runAgent(userId: string, task: string) {
// Creates a session that accumulates events
const session = pulse.metering.session(userId)
// Agent loop (multiple calls)
for (const step of agentPlan) {
const result = await executeStep(step)
session.track('input_tokens', result.inputTokens)
session.track('output_tokens', result.outputTokens)
session.track('tool_calls', result.toolCalls)
}
// Sends everything at once as a batch
await session.end()
}
Conclusion
In under 5 minutes you have:
- Automatic tracking of tokens per user
- Billing that runs on its own at the end of the month
- Payment via crypto with instant settlement
- Zero impact on your AI latency
The complete code is available on our GitHub. Any questions, join our Discord.