Token Router
API reference
One TokenSpend API for every model. Routing is free on your own keys. Prompt logging is off by default.
SDK quickstartBase 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 CompletionsPOST /v1/responses: text-only Responses subsetPOST /v1/messages: Anthropic MessagesPOST /v1/messages/count_tokens: supported Messages modelsGET /v1/healthGET /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.
autopermits text or valid calls.requiredrequires at least one valid call.nonerejects 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.
providerrouting 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 id | Vendor | Context | Input /M | Output /M | Images |
|---|---|---|---|---|---|
| anthropic/claude-opus-4-8 | Anthropic | 1000K | $5 | $25 | yes |
| anthropic/claude-sonnet-5 | Anthropic | 1000K | $2 | $10 | yes |
| anthropic/claude-haiku-4-5 | Anthropic | 200K | $1 | $5 | yes |
| anthropic/claude-opus-5 | Anthropic | 1000K | $5 | $25 | yes |
| anthropic/claude-fable-5 | Anthropic | 1000K | $10 | $50 | yes |
| openai/gpt-5.6-terra | OpenAI | 1050K | $2 | $12 | yes |
| openai/gpt-5.6-luna | OpenAI | 1050K | $0.2 | $1.2 | yes |
| openai/gpt-5.6-sol | OpenAI | 1050K | $5 | $30 | yes |
| xai/grok-4.5 | xAI | 500K | $2 | $6 | yes |
| xai/grok-4.3 | xAI | 1000K | $1.25 | $2.5 | yes |
| xai/grok-build-0.1 | xAI | 256K | $1 | $2 | yes |
| meta/muse-spark-1.2 | Meta | 1048K | $1.25 | $4.25 | yes |
| meta/muse-spark-1.1 | Meta | 1048K | $1.25 | $4.25 | yes |
| meta/muse-glimmer-30b | Meta | 131K | $0.3 | $1.2 | yes |
| google/gemini-3.7-flash | 1000K | $0.75 | $3.75 | yes | |
| google/gemini-3.6-flash | 1048K | $1.5 | $7.5 | yes | |
| google/gemini-3.1-pro | 1048K | $2 | $12 | yes | |
| google/gemini-3.5-flash-lite | 1048K | $0.3 | $2.5 | yes | |
| google/gemini-3.1-flash-lite | 1048K | $0.25 | $1.5 | yes | |
| zai/glm-5.2 | Z.ai | 1048K | $0.75 | $2.4 | no |
| moonshot/kimi-k2.7-code | Moonshot | 256K | $0.74 | $3.5 | no |
| deepseek/deepseek-v4-flash | DeepSeek | 1024K | $0.09 | $0.18 | no |
| deepseek/deepseek-v4-flash-0731 | DeepSeek | 1024K | $0.08 | $0.18 | no |
| deepseek/deepseek-v4-pro-0813 | DeepSeek | 1024K | $1.3 | $2.6 | no |
| qwen/qwen3.8-2.4t-a95b | Qwen | 256K | $2 | $6 | no |
| qwen/qwen3.8-27b | Qwen | 262K | $0.4 | $3 | yes |
| qwen/qwen3.7-max | Qwen | 1000K | $1.25 | $3.75 | no |
| qwen/qwen3.7-plus | Qwen | 1000K | $0.32 | $1.28 | no |
| qwen/qwen3.6-plus | Qwen | 1000K | $0.5 | $3 | no |
| qwen/qwen3.5-397b | Qwen | 262K | $0.45 | $3 | yes |
| openai/gpt-oss-120b | OpenAI | 131K | $0.037 | $0.17 | no |
| anthropic/claude-opus-4-7 | Anthropic | 1000K | $5 | $25 | yes |
| anthropic/claude-opus-4-6 | Anthropic | 1000K | $5 | $25 | yes |
| anthropic/claude-opus-4-5 | Anthropic | 200K | $5 | $25 | yes |
| anthropic/claude-sonnet-4-6 | Anthropic | 1000K | $3 | $15 | yes |
| anthropic/claude-sonnet-4-5 | Anthropic | 200K | $3 | $15 | yes |
| openai/gpt-5.5 | OpenAI | 1050K | $5 | $30 | yes |
| openai/gpt-5.4 | OpenAI | 1050K | $2.5 | $15 | yes |
| openai/gpt-5.4-mini | OpenAI | 400K | $0.75 | $4.5 | yes |
| openai/gpt-5.4-nano | OpenAI | 400K | $0.2 | $1.25 | yes |
| openai/gpt-5.2 | OpenAI | 400K | $1.75 | $14 | yes |
| openai/gpt-5.1 | OpenAI | 400K | $1.25 | $10 | yes |
| openai/gpt-4.1 | OpenAI | 1047K | $2 | $8 | yes |
| openai/gpt-4o | OpenAI | 128K | $2.5 | $10 | yes |
| google/gemini-3.5-flash | 1048K | $1.5 | $9 | yes | |
| google/gemini-2.5-pro | 1048K | $1.25 | $10 | yes | |
| google/gemini-2.5-flash | 1048K | $0.3 | $2.5 | yes | |
| google/gemini-2.5-flash-lite | 1048K | $0.1 | $0.4 | yes | |
| thinking-machines/inkling | Thinking Machines | 524K | $1 | $4.05 | yes |
| thinking-machines/inkling-small | Thinking Machines | 524K | $0.5 | $1.2 | yes |
| moonshot/kimi-k3 | Moonshot | 1000K | $3 | $15 | yes |
| minimax/minimax-m3 | MiniMax | 524K | $0.3 | $1.2 | yes |
| meta/llama-4-maverick | Meta | 1048K | $0.2 | $0.8 | yes |
| meta/llama-4-scout | Meta | 327K | $0.1 | $0.3 | yes |
| meta/llama-3.3-70b | Meta | 131K | $0.1 | $0.32 | no |
| meta/llama-3.2-90b-vision | Meta | 32K | $0.35 | $0.4 | yes |
| meta/llama-3.1-8b | Meta | 131K | $0.02 | $0.04 | no |
| deepseek/deepseek-v4-pro | DeepSeek | 1048K | $1.3 | $2.6 | no |
| mistral/mistral-small-3.2 | Mistral | 128K | $0.075 | $0.2 | yes |
| nvidia/nemotron-3-ultra | NVIDIA | 1000K | $0.5 | $2.2 | no |
| openai/gpt-oss-20b | OpenAI | 131K | $0.03 | $0.14 | no |
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 request401: rail key invalid or revoked402: monthly spend cap reached, or managed credits exhausted403: model blocked by your admin's allowlist, or no provider key connected404: unknown model or API route405: wrong method for a known route429: rate limit (100 requests per minute per key), withRetry-After5xx: 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