Skip to Content
Vektos
Guides

Pricing

@vektos/pricing turns the token usage of a model call into dollars, with a date-versioned price table.

What

packages/pricing answers one question: how much did this model call cost? It has:

  • PRICES: a table of models with their rates in dollars per million tokens, for uncached input, cache reads, cache writes and output, plus long-context rates where the provider has them.
  • cost(usage, model, { at }): the cost of one request, from the AI SDK's usage.
  • sumCosts(costs): the total of several requests, such as the steps of one run.
  • priceFor(model, at): the table entry valid on a given day.

It has no dependencies, so it can be published and used outside Vektos.

Why

Cost is the benchmark's second metric (PLAN §2): which context gives compliant code for the least money? Three choices follow from that:

  • The table is date-versioned and append-only. Prices drop often. If we edited the table in place, last month's runs would suddenly cost less and every comparison over time would be wrong. A price change is a new entry with a later from date; a run is priced with the entry valid on the day it ran, and the result stores that date.
  • Cost is computed per request, not per run. Some models charge every token of a request at a higher rate once its prompt passes a threshold (100k tokens for Claude Haiku 5.5, 272k for the GPT-6 family). An agent run is many requests; adding them up first and pricing the total would apply the long-context rate wrongly. So: price each step, then sumCosts.
  • Cache buckets are priced apart. With prompt caching, a cache read costs 5–10% of a normal input token. A context that is large but stable (like a design-system MCP's tool list) may cost much less than its token count suggests. Reports need to show that, so Cost keeps every bucket.

Model ids are the AI SDK gateway ids (anthropic/claude-sonnet-5.5), so the string that picks the model also picks its price. The numbers come from the Vercel AI Gateway model list, which publishes prices per token for every model it serves.

Try it

cd packages/pricing
bun test          # 10 tests

From code, with a usage object exactly as generateText returns it:

import { cost } from "@vektos/pricing";

cost(
  {
    inputTokens: 12_000,
    inputTokenDetails: { noCacheTokens: 2_000, cacheReadTokens: 9_000, cacheWriteTokens: 1_000 },
    outputTokens: 1_500,
  },
  "anthropic/claude-sonnet-5.5",
);
// {
//   model: "anthropic/claude-sonnet-5.5", priceFrom: "2026-11-08", longContext: false,
//   tokens: { input: 2000, cacheRead: 9000, cacheWrite: 1000, output: 1500 },
//   usd:    { input: 0.004, cacheRead: 0.0009, cacheWrite: 0.0025, output: 0.015, total: 0.0224 },
// }

An unknown model, or a date before the model's first entry, throws. The package never guesses a price.

Check it against the gateway

Every response from the AI Gateway carries what the account was charged (providerMetadata.gateway.cost, dollars as a string). The runner keeps it next to our own number: StepRecord.gatewayUsd per request, RunResult.gatewayUsd for the run. costDrift(result) is (ours − gateway) / gateway, and bun run bench prints a warning when it passes 2%:

warning: statement priced 5.0% off the gateway ($0.002600 vs $0.002476). Check @vektos/pricing for openai/gpt-6-luna.

If it fires, add a new entry with a later from date; do not edit the old one. Mock models report no gateway cost, so they never warn.

The first real run settled a doubt. Luna reported 1,158 tokens as cache writes, and OpenAI normally does not charge for those. Our table prices them at $0.125 per million. The gateway charge matched our total to the last digit, so the table was right.

Extend it

A price changed. Add a new entry to PRICES in src/prices.ts with the same model, the new rates and from set to the first day of the new price. Do not edit or delete the old entry.

A new model. Add an entry with its gateway id. To read the current numbers:

curl -s https://ai-gateway.vercel.sh/v1/models \
  | jq '.data[] | select(.id == "anthropic/claude-sonnet-5.5") | .pricing'

The gateway gives dollars per token as strings ("0.000002"); multiply by a million. If it lists input_tiers, the second tier's min is the long-context threshold.

A model only you have (a mock, a fine-tune): pass your own table with cost(usage, model, { table }).

Known gaps

  • Regional, batch, "fast" and service-tier prices are not modelled. The gateway lists batch and fast as separate model ids; add them as their own entries if a runner uses them.
  • Open-weights models (DeepSeek, Kimi) are served by several providers at different prices. The table holds the gateway's default.
  • Staleness is caught per run, not per table. A run through the gateway compares its priced total with the gateway's own charge and warns above 2% (see below). A model you never run is never checked.
  • Floating-point dollars. Totals are plain numbers. Fine for comparing runs; round before showing them.

On this page