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.