JavaScript and TypeScript SDK
veqen for Node.js. Wrap OpenAI, Anthropic and Google clients, tag calls with AsyncLocalStorage, set budgets and price models offline.
npm install @veqen/sdk
Node.js 20+, ESM, no dependencies, TypeScript types included. MIT licensed.
Configure
import { init } from "@veqen/sdk";
init(); // optional: the first wrapped call configures from the environment
| Option | Environment variable | Default |
|---|---|---|
apiKey |
VEQEN_API_KEY |
none: price locally, send nothing |
endpoint |
VEQEN_ENDPOINT |
https://veqen-app.com |
environment |
VEQEN_ENVIRONMENT |
none |
ledgerFile |
VEQEN_LEDGER_FILE |
none |
flushIntervalMs |
2000 |
|
budgetPollMs |
30000 |
Wrap a client
import Anthropic from "@anthropic-ai/sdk";
import { GoogleGenAI } from "@google/genai";
import OpenAI from "openai";
import { wrap } from "@veqen/sdk";
const openai = wrap(new OpenAI(), { feature: "search" });
const claude = wrap(new Anthropic(), { residency: "us" });
const gemini = wrap(new GoogleGenAI({ apiKey }));
Instrumented methods: chat.completions.create and .stream, responses.create and .stream, embeddings.create, images.generate and .edit, audio.speech.create, audio.transcriptions.create and audio.translations.create on OpenAI and every OpenAI-compatible API; messages.create and .stream, beta.messages.create on Anthropic; models.generateContent and models.generateContentStream on Google.
An OpenAI client pointed at another provider is priced as that provider, read from its baseURL (Together, Fireworks, Groq, Azure AI Foundry and the rest of the price book's hosts). For your own server, pass wrap(client, { provider: "self-hosted" }). Streams are priced when they end, and stream helpers (finalChatCompletion(), finalResponse(), finalMessage()) are supported.
Tag calls
import { withTags } from "@veqen/sdk";
app.post("/ask", (req, res) =>
withTags({ customer: req.org.id, user: req.user.id }, async () => {
res.json(await openai.responses.create({ model: "gpt-5-mini", input: req.body.q }));
}));
Tags use AsyncLocalStorage, so they follow await, timers and promises started inside the callback.
Budgets
import { budget, BudgetExceeded } from "@veqen/sdk";
budget(5, { customer: "*" }); // $5 a month per customer
budget(200, { feature: "agent", period: "day", mode: "warn" });
budget(1000, { project: "eval-harness" }); // scopes: customer, feature, user, project
try {
await openai.responses.create({ ... });
} catch (error) {
if (error instanceof BudgetExceeded) return showUpgrade(error.limitUsd);
throw error;
}
Record a call by hand
import { record } from "@veqen/sdk";
record("anthropic", "claude-sonnet-4-5", { inputTokens: 1200, outputTokens: 300, cacheReadTokens: 8000 },
{ batch: true, tags: { customer: "acme", feature: "nightly-summary" } });
record("google", "veo-3.1-generate-001", { units: 8 }, { variant: "1080p", tags: { project: "ads" } });
record("fal", "fal-ai/flux/schnell", { units: 4 }, { tags: { feature: "catalog" } });
units counts images, seconds of audio or video, characters, queries or pages, whichever the model is billed by. Pass costUsd for a model the price book doesn't know, or for a call that reports a cost but no usage.
Price without recording
import { cost, lookup } from "@veqen/sdk";
cost("gpt-4o", { inputTokens: 1000, outputTokens: 500 }); // 0.0075
cost("gpt-image-1", { units: 1 }, { variant: "high/1024x1024" });
lookup("gemini-2.5-flash")?.rates.standard;
The whole price book is importable as veqen/prices.
Serverless
Await flush() before your handler returns (Vercel Functions, Lambda, Cloudflare Workers with Node compatibility). In long-running servers, events are sent every two seconds and on beforeExit.
Integrations
The Vercel AI SDK middleware (@veqen/sdk/ai-sdk) and the LangChain.js handler (@veqen/sdk/langchain): see Framework integrations.