Token Router

API reference

One TokenSpend API for every model. Routing is free on your own keys. Prompt logging is off by default.

SDK quickstart

Base URL

https://api.tokenspend.dev
Compatibility
OpenAI + Messages
Routing
Free
Data retention
No content by default

Authentication

Use a rail key (tsr_...) from Router. Either header form works.

x-api-key: tsr_...
# or
Authorization: Bearer tsr_...

Keys are stored hashed, shown once at creation, and revoke instantly (no cache in the auth path).

Endpoints

Base URL https://api.tokenspend.dev

  • POST /v1/chat/completions: OpenAI Chat Completions
  • POST /v1/responses: text-only Responses subset
  • POST /v1/messages: Anthropic Messages
  • POST /v1/messages/count_tokens: supported Messages models
  • GET /v1/health
  • GET /v1/models: canonical model contracts

Chat Completions and Messages support text, system prompts, streaming, function tools, and model-approved images. Validated structured output is available only where the registry marks the selected protocol. A field that cannot survive translation returns 400 with its exact path.

Responses

The text-only subset supports model, string or basic message input, instructions, max_output_tokens, string-valued metadata, and stream. It also accepts validated text.format on capable models. Ordinary streaming uses Responses events.

curl https://api.tokenspend.dev/v1/responses \
  -H "content-type: application/json" \
  -H "Authorization: Bearer $TOKENSPEND_API_KEY" \
  -d '{"model":"zai/glm-5.2","input":"hello","max_output_tokens":256}'

Tools, reasoning controls, stored-response chaining, and background mode are not part of this subset. They return 400 before forwarding.

Structured output

Strict JSON Schema uses response_format in Chat, text.format in Responses, and output_config.format in Messages. TokenSpend validates the final JSON before returning success.

Structured streaming returns 400. Unsupported model and protocol pairs also return 400 before forwarding. A model response that misses the schema returns 502 with upstream_schema_violation. See each model's protocol capability in the registry JSON.

The registry's jsonSchemaProfiles lists accepted and normalized keywords. TokenSpend may send equivalent schema syntax to a model, then validates the result against your original schema.

Tools

TokenSpend validates every tool name and argument against the request's JSON Schema before returning it. Invalid, undeclared, repeated, partial, or truncated calls return a retryable 502.

  • auto permits text or valid calls.
  • required requires at least one valid call.
  • none rejects returned calls.
  • Named choice permits only that declared tool.
  • Disabling parallel calls permits one call.

Tool streams are validation-buffered. No argument delta is released until the complete stream passes validation. Unsupported model, protocol, choice, or streaming combinations return 400 before forwarding. See the registry JSON.

Tool and reasoning requests use one provider attempt. TokenSpend does not hide a retry or model switch. An incurred validation failure still produces one exact failed receipt.

Reasoning

Supported GLM and DeepSeek Chat routes use reasoning.enabled. Marked native OpenAI routes use reasoning_effort. Native Messages routes use thinking.

The registry lists accepted values. Marked OpenAI models currently accept low, medium, and high effort. Messages currently accepts enabled and disabled thinking.

Muse reasoning is always on and requires at least 2,048 output tokens. Reasoning stays separate from visible content. Empty reasoning-only output returns reasoning_output_exhausted.

Public continuation state is preserved only on a compatible protocol. Replayed cross-protocol state returns 400. A response that would require dropped state returns a retryable 502. DSML, thinking tags, parser markers, special tokens, and internal control fields are rejected.

Request contract

Standard fields pass through when the selected model can honor them. Cross-format requests support the shared text, sampling, stop, stream, and function-tool controls. Inexact controls are rejected, never discarded.

  • provider routing blocks are unsupported.
  • Unproven translated reasoning and structured-output controls return 400.
  • Cross-format requests without a token limit use a 4,096-token maximum.
  • Unknown consequential fields return 400 with error.param.
  • Malformed JSON and request shapes return 400 before routing.

Route proof

Every routed response carries x-tokenspend-request-id, canonical model, immutable revision, and registry version headers. Standard response bodies and stream framing stay unchanged.

Workspace admins can open the receipt to see protocol, streaming state, attempt count, same-model retry, configured model switch, token breakdown, itemized cost, metering state, and idempotency outcome. The serving host is never part of this proof.

  • Ordinary requests may try the same model twice after a network fault, 429, or 5xx.
  • Tool, reasoning, and validated structured-output requests use one attempt.
  • A model switch runs only from an admin policy saved before the request.
  • Canceled or interrupted streams receive one failed, incomplete receipt.

Public handlers have a 300-second platform ceiling. Client cancellation stops upstream work. Use error.retryable to decide whether a new request may succeed.

Safe retries

Send an Idempotency-Key on Chat Completions, Responses, or Messages. TokenSpend scopes it to your workspace and route for 24 hours.

curl https://api.tokenspend.dev/v1/chat/completions \
  -H "Authorization: Bearer $TOKENSPEND_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: deploy-42-generation" \
  -d '{"model":"zai/glm-5.2","messages":[{"role":"user","content":"hello"}]}'
  • A duplicate never creates another model request, receipt, or debit.
  • An in-flight retry returns 409 idempotency_in_progress.
  • A completed retry returns 409 idempotency_already_completed.
  • A changed body returns 409 idempotency_key_reused.

Duplicate responses carry the original x-tokenspend-request-id. TokenSpend stores keyed fingerprints and state, not a replay copy of the response. Use a new key after a failed request.

Models

Model idVendorContextInput /MOutput /MImages
anthropic/claude-opus-4-8 Anthropic1000K$5$25yes
anthropic/claude-sonnet-5 Anthropic1000K$2$10yes
anthropic/claude-haiku-4-5 Anthropic200K$1$5yes
anthropic/claude-opus-5 Anthropic1000K$5$25yes
anthropic/claude-fable-5 Anthropic1000K$10$50yes
openai/gpt-5.6-terra OpenAI1050K$2$12yes
openai/gpt-5.6-luna OpenAI1050K$0.2$1.2yes
openai/gpt-5.6-sol OpenAI1050K$5$30yes
xai/grok-4.5 xAI500K$2$6yes
xai/grok-4.3 xAI1000K$1.25$2.5yes
xai/grok-build-0.1 xAI256K$1$2yes
meta/muse-spark-1.2 Meta1048K$1.25$4.25yes
meta/muse-spark-1.1 Meta1048K$1.25$4.25yes
meta/muse-glimmer-30b Meta131K$0.3$1.2yes
google/gemini-3.7-flash Google1000K$0.75$3.75yes
google/gemini-3.6-flash Google1048K$1.5$7.5yes
google/gemini-3.1-pro Google1048K$2$12yes
google/gemini-3.5-flash-lite Google1048K$0.3$2.5yes
google/gemini-3.1-flash-lite Google1048K$0.25$1.5yes
zai/glm-5.2 Z.ai1048K$0.75$2.4no
moonshot/kimi-k2.7-code Moonshot256K$0.74$3.5no
deepseek/deepseek-v4-flash DeepSeek1024K$0.09$0.18no
deepseek/deepseek-v4-flash-0731 DeepSeek1024K$0.08$0.18no
deepseek/deepseek-v4-pro-0813 DeepSeek1024K$1.3$2.6no
qwen/qwen3.8-2.4t-a95b Qwen256K$2$6no
qwen/qwen3.8-27b Qwen262K$0.4$3yes
qwen/qwen3.7-max Qwen1000K$1.25$3.75no
qwen/qwen3.7-plus Qwen1000K$0.32$1.28no
qwen/qwen3.6-plus Qwen1000K$0.5$3no
qwen/qwen3.5-397b Qwen262K$0.45$3yes
openai/gpt-oss-120b OpenAI131K$0.037$0.17no
anthropic/claude-opus-4-7 Anthropic1000K$5$25yes
anthropic/claude-opus-4-6 Anthropic1000K$5$25yes
anthropic/claude-opus-4-5 Anthropic200K$5$25yes
anthropic/claude-sonnet-4-6 Anthropic1000K$3$15yes
anthropic/claude-sonnet-4-5 Anthropic200K$3$15yes
openai/gpt-5.5 OpenAI1050K$5$30yes
openai/gpt-5.4 OpenAI1050K$2.5$15yes
openai/gpt-5.4-mini OpenAI400K$0.75$4.5yes
openai/gpt-5.4-nano OpenAI400K$0.2$1.25yes
openai/gpt-5.2 OpenAI400K$1.75$14yes
openai/gpt-5.1 OpenAI400K$1.25$10yes
openai/gpt-4.1 OpenAI1047K$2$8yes
openai/gpt-4o OpenAI128K$2.5$10yes
google/gemini-3.5-flash Google1048K$1.5$9yes
google/gemini-2.5-pro Google1048K$1.25$10yes
google/gemini-2.5-flash Google1048K$0.3$2.5yes
google/gemini-2.5-flash-lite Google1048K$0.1$0.4yes
thinking-machines/inkling Thinking Machines524K$1$4.05yes
thinking-machines/inkling-small Thinking Machines524K$0.5$1.2yes
moonshot/kimi-k3 Moonshot1000K$3$15yes
minimax/minimax-m3 MiniMax524K$0.3$1.2yes
meta/llama-4-maverick Meta1048K$0.2$0.8yes
meta/llama-4-scout Meta327K$0.1$0.3yes
meta/llama-3.3-70b Meta131K$0.1$0.32no
meta/llama-3.2-90b-vision Meta32K$0.35$0.4yes
meta/llama-3.1-8b Meta131K$0.02$0.04no
deepseek/deepseek-v4-pro DeepSeek1048K$1.3$2.6no
mistral/mistral-small-3.2 Mistral128K$0.075$0.2yes
nvidia/nemotron-3-ultra NVIDIA1000K$0.5$2.2no
openai/gpt-oss-20b OpenAI131K$0.03$0.14no

Prices are provider list price. We never mark up tokens: BYOK requests bill on your own provider account, and managed credits draw down at exactly these rates.

Managed Muse Spark follows Meta's geographic policy. Requests from restricted territories are refused before forwarding.

Registry 2026-08-23.1, effective 8/23/2026. Every new receipt pins this version. Open registry JSON.

Canonical ids are stable. Accepted aliases resolve to one canonical id. Dated revisions stay distinct on receipts. List contracts with GET /v1/models or inspect one with GET /v1/models/{model}.

Unknown ids return 404 model_not_found with error.param = model before forwarding.

Verified output ceilings fail before forwarding. A null ceiling means the selected provider path manages the limit and TokenSpend does not publish a narrower value.

curl https://api.tokenspend.dev/v1/models \
  -H "Authorization: Bearer $TOKENSPEND_API_KEY"

Quickstart

curl

export TOKENSPEND_API_KEY=tsr_...

curl https://api.tokenspend.dev/v1/chat/completions \
  -H "content-type: application/json" \
  -H "Authorization: Bearer $TOKENSPEND_API_KEY" \
  -d '{"model":"zai/glm-5.2","messages":[{"role":"user","content":"hello"}]}'

Cline, Kilo, aider, any OpenAI SDK

export OPENAI_BASE_URL=https://api.tokenspend.dev/v1
export OPENAI_API_KEY=tsr_...

Python

pip install tokenspend

from tokenspend import TokenSpend

client = TokenSpend()
resp = client.chat.completions.create(
    model="xai/grok-4.5",
    messages=[{"role": "user", "content": "Explain this repo's build step."}],
)

JavaScript and TypeScript

npm install @tokenspend/sdk

import { TokenSpend } from "@tokenspend/sdk";

const client = new TokenSpend();
const resp = await client.chat.completions.create({
  model: "meta/muse-spark-1.2",
  messages: [{ role: "user", content: "Explain this repo's build step." }]
});

The same clients accept thinking-machines/inkling and moonshot/kimi-k3. Model ids select the exact model. Serving hosts are not part of the public API.

TokenSpend SDKs

Use the first-party Python or TypeScript client. Both share the same rail key, models, credits, and receipts.

Open SDK quickstart for copyable requests.

Images

Send images as standard content parts; the rail handles provider differences. Models without image support refuse with a 400 naming the model rather than silently ignoring the image. The Images column above is authoritative.

{"model":"anthropic/claude-sonnet-5","messages":[
  {"role":"user","content":[
    {"type":"text","text":"What is in this screenshot?"},
    {"type":"image_url","image_url":{"url":"data:image/png;base64,<base64>"}}
  ]}
]}

Errors

Every error has the same nested shape. Messages also includes its required outer type: error marker.

{
  "error": {
    "message": "...",
    "type": "invalid_request_error",
    "param": "field.path",
    "code": "unsupported_parameter",
    "retryable": false
  }
}
  • 400: malformed or unsupported request
  • 401: rail key invalid or revoked
  • 402: monthly spend cap reached, or managed credits exhausted
  • 403: model blocked by your admin's allowlist, or no provider key connected
  • 404: unknown model or API route
  • 405: wrong method for a known route
  • 429: rate limit (100 requests per minute per key), with Retry-After
  • 5xx: TokenSpend or model service failure

retryable is true only when retrying can succeed.

If a model fails after a stream opens, Chat Completions and Messages emit a protocol error with the same stable code. Responses emits response.failed. Raw model service errors are not forwarded.

Rate limits and fair use

100 requests per minute per rail key. Routing on your own provider keys is free; there is no request charge and no token markup. Free-tier use should look like a team coding, not a benchmark farm: sustained automated load, resale of access, or traffic that endangers our provider standing may be rate limited or suspended. Talk to us before you plan anything heavy and we will size it with you.

Managed access follows provider acceptable-use, safety, regional, and sanctions rules. Restricted managed requests are refused before forwarding.

Data retention

TokenSpend does not store Router prompts or responses by default. Admins can enable encrypted request logging for 30-day workspace history. Turning logging off affects new requests; existing history keeps its expiry.

Bodies are never written to logs. Receipts carry metadata only: engineer, model, token counts, latency, status and list-price cost. Fleet capture is unaffected by any of this: the hook sends token counts, timestamps and git refs, never code.

Request logging does not change a downstream provider's handling. See the route boundary for BYOK and managed traffic.

Start routing

Connect a provider key, issue a rail key, and point your tools at the base URL.

Get started