Guides
Frameworks & runtimes
wrap(client) only captures usage from a provider client you construct and pass in. Any framework that builds its own client internally is invisible to it. This page says exactly where that line falls.
Vercel AI SDK
The ai package builds its own provider client internally, so wrap() never sees it. Use the middleware instead. It sits at the doGenerate/doStream layer, below every provider adapter, so it works with any provider the AI SDK supports. Calls through the OpenAI, Anthropic, Google, Bedrock, Azure and OpenRouter providers, and through Vercel's gateway provider, are recorded under that provider; any other provider (Mistral, xAI, Groq and so on) is recorded with provider other.
import { wrapLanguageModel } from "ai";
import { openai } from "@ai-sdk/openai";
import { tokenmeterMiddleware } from "@tokenmeter/sdk";
const model = wrapLanguageModel({
model: openai("gpt-4o-mini"),
middleware: tokenmeterMiddleware(tl),
});AI SDK 3 and 4 drop the cache and reasoning breakdown
AI SDK 5 and later report cached and reasoning tokens when the provider does, and the middleware splits them intocache_read_tokens, cache_write_tokens (6 and later) and reasoning_tokens. AI SDK 3 and 4 normalize usage to { promptTokens, completionTokens } before the middleware sees it, so on those versions this SDK maps promptTokens → input_tokens, completionTokens → output_tokens, and leaves the cache and reasoning fields null, never guessed. Total-token cost is accurate on every version; cache discount and reasoning pricing are only exact on 5 and later.Vercel AI Gateway
If your requests go through Vercel AI Gateway, connect it under Settings → Providers with an AI Gateway API key from the Vercel team you want to report on. Tokenmeter reads that team's spend from Vercel's Custom Reporting API every day and shows it as its own provider, per model and per day, with up to 90 days of history. Gateway spend is kept separate from any direct OpenAI or Anthropic connection, so the same request is never counted twice.
- Vercel serves these reports to Pro and Enterprise teams only. A Hobby or Pro trial key is refused when you connect it.
- Vercel bills each report query to your team ($5 per 1,000). A sync makes one query to find the days with traffic, then one per such day, so a day-to-day sync makes a handful and the first import makes at most 91.
- Requests that use your own provider keys (BYOK) count as $0, because that spend is billed on your provider account. Connect that provider directly to see it.
Request-level metering works alongside the report. The middleware above records a request made through the AI SDK's Gateway provider as vercel_gateway, keeping Vercel's creator/model id. An OpenAI client pointed at the Gateway is wrapped the same way:
import OpenAI from "openai";
import { wrapOpenAICompatible } from "@tokenmeter/sdk";
const openai = wrapOpenAICompatible(
new OpenAI({ apiKey: process.env.AI_GATEWAY_API_KEY, baseURL: "https://ai-gateway.vercel.sh/v1" }),
tl,
"vercel_gateway",
);Metered requests are priced from Vercel's public model list, synced daily. On Spend, filtered to Vercel AI Gateway, each model in the report shows its SDK-metered cost for the same days beside it, and a model you metered that is missing from the report is listed as such.
Extra breakdowns are opt-in on the connection: by tag, by user, by API key, and own keys vs Vercel credits. Each one adds a report query per day with traffic. They appear on Spend under the Gateway's model list, and are never added to its total: a request counts under every tag it carries. The own-keys breakdown also shows the list value of your BYOK traffic, which Vercel reports as $0.
To make the tag breakdown line up with Tokenmeter's attribution, let the middleware send the call's team, project, feature and environment to the Gateway as reporting tags (team:payments, env:prod). It is off by default because Vercel charges for each tag written ($0.075 per 1,000). It needs AI SDK 5 or later, only touches Gateway models, keeps any tags you already send, and stays within Vercel's 10-tag limit. Customer references are never sent.
import { gateway, wrapLanguageModel } from "ai";
import { tokenmeterMiddleware } from "@tokenmeter/sdk";
const model = wrapLanguageModel({
model: gateway("anthropic/claude-sonnet-4.5"),
middleware: tokenmeterMiddleware(tl, { team: "payments" }, { gatewayReportingTags: true }),
});Serverless (Lambda / Vercel / Cloudflare)
The default 2-second background flush timer is wrong on any runtime that freezes the process between invocations: a frozen setTimeout just never fires, and buffered events sit unsent until the next invocation happens to flush them (or never do, if the instance is recycled). Use createServerlessClient instead, which disables the timer, and flush explicitly at the natural end of the invocation.
// Vercel / Cloudflare edge: extend execution past the response:
import { createServerlessClient, flushOnWaitUntil } from "@tokenmeter/sdk";
const tl = createServerlessClient({ apiKey: process.env.TOKEN_METER_KEY! });
// ...handle request, wrapped calls happen...
flushOnWaitUntil(tl, ctx); // ctx.waitUntil(...)// AWS Lambda: await it as the literal last line before return:
import { createServerlessClient, flushBeforeReturn } from "@tokenmeter/sdk";
const tl = createServerlessClient({ apiKey: process.env.TOKEN_METER_KEY! });
export const handler = async (event: unknown) => {
const result = await doWork(event);
await flushBeforeReturn(tl);
return result;
};The loss bound, stated plainly
Events recorded after the lastflush()/waitUntil() of an invocation are lost if that invocation returns without calling it. There is no background timer left to catch them once serverless mode is on. All three newer wrappers (Gemini, Bedrock, Azure OpenAI) work under this same serverless path unchanged, since none of them touch the client's flush scheduling.Not supported yet
Stated once, honestly, instead of asserting universal framework coverage the SDK doesn't have: any framework that constructs its own provider client internally is invisible to wrap() unless it exposes a hook this SDK reads.
| Framework | Why not | What it would take |
|---|---|---|
| LangChain | Constructs its own provider clients internally (ChatOpenAI, ChatAnthropic); wrap() never sees them. | A LangChain callback handler reading llmStart/llmEnd events. Not built. |
| LlamaIndex | Same shape as LangChain: internal client construction, no exposed hook this SDK uses. | An LlamaIndex callback/event equivalent. Not built. |
| CrewAI | Python-only; out of scope for the TypeScript SDK regardless. | N/A for the TS SDK. |
| LangGraph | Built on LangChain's client construction, so it inherits the same gap. | The same LangChain callback handler would likely cover it once written. |
If your stack is one of these, the fallback is to wrap the raw provider client yourself and configure the framework to use your wrapped instance wherever the framework allows injecting a client. Where it doesn't, usage from that call path is currently unattributed (shown on the coverage screen, never hidden).