tokenbanking
v0.1.2
Published
TokenBanking SDK for Node.js and Bun — OpenAI-compatible client with marketplace price caps and automatic fallback.
Maintainers
Readme
TokenBanking SDK
One API key, every model, always routed to the cheapest available supplier. Streaming, tool calling, retries and timeouts all work out of the box, and you can cap what you're willing to pay per request.
Works with Node.js ≥ 20 or Bun.
- Repo: https://github.com/tokenbanking/tokenbanking-sdk.js
- Dashboard, credits and API keys: https://tokenbanking.aimake.website/
Get an API key
- Sign in at https://tokenbanking.aimake.website/.
- Load credits under Dashboard → Credits (USDC on Arc, or Stripe).
- Create a key under Dashboard → API keys and copy the
tb_…token. Treat it like a password; you can copy it again from the key list.
export TOKENBANKING_API_KEY="tb_…"Installation
npm install tokenbanking
# or
bun add tokenbankingQuickstart
import TB from "tokenbanking";
// apiKey defaults to $TOKENBANKING_API_KEY, baseURL to the hosted router.
const tb = new TB.Client();
const response = await tb.chat.completions.create({
model: "minimax/minimax-m2.7",
messages: [{ role: "user", content: "Explain USDC in two sentences." }],
maxPrice: { input: 1.0, output: 1.0 }, // USD per 1M tokens
});
console.log(response.choices[0].message.content);Model ids are the vendor-prefixed ids listed on the
Models page —
minimax/minimax-m2.7, deepseek/deepseek-v3.2, z-ai/glm-5.2, and so on.
Configuration is picked up from the environment when not passed explicitly:
| Env var | Purpose | Default |
| --- | --- | --- |
| TOKENBANKING_API_KEY | Your TokenBanking consumer key | — |
| TOKENBANKING_BASE_URL | Router base URL | https://tb-proxy.aimake.website/v1 |
Price caps (maxPrice)
maxPrice caps what you're willing to pay, in USD per 1M tokens. Anything you leave out is unlimited. Suppliers above your cap are skipped; if no supplier fits, the request fails with 402 instead of overpaying.
const chat = await tb.chat.completions.create({
model: "minimax/minimax-m2.7",
messages: [{ role: "user", content: "Hello!" }],
maxPrice: { input: 1.0 }, // output & cached: no limit
});The SDK lifts maxPrice out of the request body into x-tb-max-price-* headers, so it never reaches the upstream provider.
TypeScript
maxPrice is an extension to the standard chat-completions body, so TypeScript
needs to be told about it — wrap the params in WithMaxPrice<…>:
import TB, { type WithMaxPrice } from "tokenbanking";
import type { ChatCompletionCreateParamsNonStreaming } from "openai/resources/chat/completions";
const params: WithMaxPrice<ChatCompletionCreateParamsNonStreaming> = {
model: "minimax/minimax-m2.7",
messages: [{ role: "user", content: "Hello!" }],
maxPrice: { input: 1.0, output: 1.0 },
};
const chat = await tb.chat.completions.create(params);On responses.create it is typed natively, so no wrapper is needed there.
Fallback
Pass any OpenAI-compatible client as fallback. If the router is unreachable or returns 402 / 408 / 429 / 5xx, the same request is transparently replayed against the fallback with its own base URL and API key:
import TB from "tokenbanking";
import OpenAI from "openai";
const tb = new TB.Client({
fallback: new OpenAI({ apiKey: process.env.OPENAI_API_KEY }),
});Streaming
const stream = await tb.chat.completions.create({
model: "minimax/minimax-m2.7",
messages: [{ role: "user", content: "Write a haiku about foxes." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}Tool calling
const response = await tb.chat.completions.create({
model: "minimax/minimax-m2.7",
messages: [{ role: "user", content: "What's the weather in São Paulo?" }],
tools: [
{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
},
},
],
});
for (const call of response.choices[0].message.tool_calls ?? []) {
if (call.type !== "function") continue;
console.log(call.function.name, JSON.parse(call.function.arguments));
}Feed results back as role: "tool" messages, exactly as with openai-node.
Responses API
tb.responses.create is a shim over /v1/chat/completions (the router is chat-completions native):
const response = await tb.responses.create({
model: "minimax/minimax-m2.7",
input: "Complete the sentence: 'The quick brown fox jumps over the'",
maxPrice: { input: 1.0, output: 1.0 },
});
console.log(response.output_text);Note: it supports text/image input,
instructions, function tools and streaming. Responses-only features (previous_response_id, built-in tools,background, …) throw a clear error. For full control, usetb.chat.completions.create.
Everything else from openai-node
TB.Client extends the OpenAI client, so per-request options work unchanged:
await tb.chat.completions.create(
{ model: "minimax/minimax-m2.7", messages: [{ role: "user", content: "hi" }] },
{
headers: { "x-my-header": "value" }, // custom headers
timeout: 30_000,
maxRetries: 1,
},
);Errors
Errors are the standard openai-node classes (APIError, RateLimitError, …). A 402 means no supplier fits your maxPrice, or your credits ran out — top up at https://tokenbanking.aimake.website/dashboard/credits.
Using the plain OpenAI SDK
Don't want to switch SDKs? The router is fully OpenAI-compatible — point the official openai client at TokenBanking and set the price-cap headers yourself:
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.TOKENBANKING_API_KEY,
baseURL: "https://tb-proxy.aimake.website/v1",
});
const chat = await client.chat.completions.create(
{
model: "minimax/minimax-m2.7",
messages: [{ role: "user", content: "Explain USDC in two sentences." }],
},
{
headers: {
// USD per 1M tokens; omit a header for no limit on that side
"x-tb-max-price-input": "1.00",
"x-tb-max-price-output": "1.00",
// "x-tb-max-price-cached": "0.10",
},
},
);Or set the caps once for every request via defaultHeaders:
const client = new OpenAI({
apiKey: process.env.TOKENBANKING_API_KEY,
baseURL: "https://tb-proxy.aimake.website/v1",
defaultHeaders: {
"x-tb-max-price-input": "1.00",
"x-tb-max-price-output": "1.00",
},
});The same headers work with any HTTP client (curl, Python openai, etc.). If no supplier fits the caps, the router responds 402. Automatic fallback is a client-side feature of this SDK, so with plain openai you'd handle failover yourself.
Development
bun install
bun test # run tests
bun run typecheck # tsc --noEmit
bun run build # emit dist/