# JavaScript and TypeScript SDK

```bash
npm install @veqen/sdk
```

Node.js 20+, ESM, no dependencies, TypeScript types included. MIT licensed.

## Configure

```ts
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

```ts
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](/docs/price-book#matching-model-names-and-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

```ts
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

```ts
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

```ts
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

```ts
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](/docs/integrations).
