Scrawn LogoScrawn Docs
SDK ReferenceFunctionsAI Token Billing

Vercel AI SDK

First-class Vercel AI SDK integration - auto-bill every LLM call

Vercel AI SDK

Scrawn wraps the Vercel AI SDK so every streamText, generateText, streamObject, and generateObject call auto-bills on every step finish. No manual token counting, no per-route instrumentation.

Setup

Add the AI wrapper to your biller.ts:

// scrawn/biller.ts
import { scrawn } from "@scrawn/core";
import { TAGS, EXPRESSIONS } from "./pricerefs";
import * as aisdk from "ai";

export const biller = scrawn({
  apiKey: process.env.SCRAWN_KEY,
  baseURL: process.env.SCRAWN_BASE_URL,
  httpUrl: process.env.SCRAWN_HTTP_URL,
  tags: TAGS,
  expressions: EXPRESSIONS,
});

export const ai = biller.ai(aisdk, {
  inputDebit: biller.tag("GPT_INPUT"),
  outputDebit: biller.expr("COMPLEX_FEE"),
});

Then import ai from your biller in any file:

import { ai } from "./scrawn/biller";

const result = await ai.streamText({
  userId: "user-123",
  model: openai("gpt-4o-mini"),
  prompt: "Hello",
});

The config object accepts:

Prop

Type

The returned ai object works exactly like the original Vercel AI SDK - call streamText, generateText, etc. exactly as you normally would, just with a userId parameter added.

Usage

Every wrapped function works exactly like the original Vercel AI SDK - just add a userId to associate billing:

import { ai } from "./scrawn/biller";

const result = await ai.streamText({
  userId: "user-123",
  model: openai("gpt-4o-mini"),
  prompt: "Hello",
});

The same pattern applies to generateText, streamObject, and generateObject - all original options and types are preserved.

Multiple models

Use tags to charge different rates per model. Define separate wrappers in your biller.ts:

// scrawn/biller.ts
export const gpt = biller.ai(aisdk, {
  inputDebit: biller.tag("GPT_INPUT"),
  outputDebit: biller.tag("GPT_OUTPUT"),
});

export const claude = biller.ai(aisdk, {
  inputDebit: biller.tag("CLAUDE_INPUT"),
  outputDebit: biller.tag("CLAUDE_OUTPUT"),
});

Then import the right one per call:

import { gpt, claude } from "./scrawn/biller";

const result = await gpt.streamText({
  userId: "user-123",
  model: openai("gpt-4o-mini"),
  prompt: "Hello",
});

Manual tracking (trackAI)

For full manual control without the biller.ai() wrapper, use trackAI() inside onFinish or onStepFinish:

import { biller } from "./scrawn/biller";

ai.streamText({
  model: openai("gpt-4o"),
  prompt: "Hello",
  onFinish: async (event) => {
    biller.trackAI({
      userId: "user-123",
      event,
      inputDebit: biller.tag("GPT_INPUT"),
      outputDebit: biller.tag("GPT_OUTPUT"),
    });
  },
});

Prop

Type

Compatibility

The wrapper supports ai SDK versions 3.x and 4.x. If you encounter type issues, ensure ai and @scrawn/core are both on recent versions.