When to use this
Use this guide when you have code written against the Anthropic SDK or the Anthropic Messages wire format. Tokener CA exposes the Anthropic-compatible (Messages) protocol at https://a.tokener.ca/v1/messages, so an Anthropic client only needs a different base URL and your Tokener API key. The same key, wallet, prices, and Usage page back this protocol and the two OpenAI protocols.
Using the Claude Code CLI? It speaks this same protocol — see the Claude Code guide to point it at Tokener.
Base URL and key
Point the Anthropic SDK at:
https://a.tokener.caNote there is no /v1 on the SDK base URL — the Anthropic SDKs append /v1/messages themselves. If you double the /v1 (https://a.tokener.ca/v1 as the base URL) the request 404s. Authenticate with either x-api-key: <your_api_key> (the SDK's default) or Authorization: Bearer <your_api_key>. anthropic-version is optional; a default is applied when you omit it.
Install
pip install anthropic # Pythonnpm install @anthropic-ai/sdk # TypeScriptFirst request
Set TOKENER_API_KEY to your API key, then:
import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://a.tokener.ca", apiKey: process.env.TOKENER_API_KEY,}); const message = await client.messages.create({ model: "glm-5.3:flash", max_tokens: 128, messages: [{ role: "user", content: "Introduce Tokener CA in one sentence." }],});console.log(message.content);max_tokens is required on this protocol. The raw wire equivalent is:
curl https://a.tokener.ca/v1/messages \ -H "x-api-key: $TOKENER_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-5.3:flash", "max_tokens": 128, "messages": [{"role": "user", "content": "Introduce Tokener CA in one sentence."}] }'Streaming
The stream runs message_start … message_delta … message_stop. The SDK's stream helper exposes the text incrementally:
const stream = client.messages .stream({ model: "glm-5.3:flash", max_tokens: 256, messages: [{ role: "user", content: "Give me a minimal HTTP client example." }], }) .on("text", (text) => process.stdout.write(text));await stream.finalMessage();A failure that happens after the stream has started arrives as an event inside the 200 response, so handle stream error events as well as content.
Tool use
Tool calling uses tool_use / tool_result blocks. Round 1 the model may return a tool_use block; send the result back as a tool_result block in a following user message:
tools = [{ "name": "get_weather", "description": "Get the weather for a city.", "input_schema": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], },}] first = client.messages.create( model="glm-5.3:flash", max_tokens=256, tools=tools, messages=[{"role": "user", "content": "What is the weather in Paris? Use the tool."}],) # Echo the assistant's tool_use back, then answer with a tool_result block.final = client.messages.create( model="glm-5.3:flash", max_tokens=256, tools=tools, messages=[ {"role": "user", "content": "What is the weather in Paris? Use the tool."}, {"role": "assistant", "content": first.content}, {"role": "user", "content": [ {"type": "tool_result", "tool_use_id": "<id from the tool_use block>", "content": "21C, sunny"} ]}, ],)print(final.content)Turn thinking on or off
Reasoning is switched with the thinking field — there is no separate effort scale on this protocol:
{ "thinking": { "type": "enabled", "budget_tokens": 1024 } }budget_tokens must be below max_tokens. To answer directly, send { "thinking": { "type": "disabled" } } (or omit it). Which models actually think, and how much, varies by model — the Tokener CA Playground Reasoning selector shows a model's modes.
Reading usage
Each response carries usage.input_tokens, usage.output_tokens, and usage.cache_read_input_tokens. The Usage page records the protocol of every request. On this protocol Tokener bills the input total of the request (cached input at the model's cache-read rate where it has one); when the input accounting cannot be read reliably, Tokener bills the lower reading. For how these fields relate and become a charge, see How the numbers are counted and billed.
Count tokens
POST https://a.tokener.ca/v1/messages/count_tokens returns how many input tokens a request would use, as { "input_tokens": <n> }. It is free — it is not charged and writes no usage record — but it needs a valid API key on an account with a positive balance. Send the same model and messages (and any system / tools) you would send to /v1/messages.
Token counting is available only on accounts that have turned on Also allow suppliers without that commitment under Account settings → Data handling; on other accounts this endpoint answers 404 not_found_error with the message "Token counting is not available on this API." Everything else on this protocol works without it.
import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://a.tokener.ca", apiKey: "tok-..." }); const count = await client.messages.countTokens({ model: "glm-5.3:flash", messages: [{ role: "user", content: "How many tokens is this?" }],});console.log(count.input_tokens);Error handling
Errors use the Anthropic error envelope — { "type": "error", "error": { "type", "message" } } — and every response Tokener builds carries a request-id header. The SDKs surface these as typed errors (AuthenticationError, NotFoundError, BadRequestError, …); read error.request_id when reporting a problem. The API reference lists every endpoint, accepted header, required field and error code across the three protocols.
What is different from the Anthropic API
max_tokensis required.- There is no JSON-output mode on this protocol.
- Reasoning is
thinkingenabled/disabledonly; no prompt-caching controls are promised. - Which features a model supports (images, tools, thinking) varies by model as well as by protocol — try a combination in the Tokener CA Playground.
Troubleshooting
| Symptom | Check first |
|---|---|
401 | The API key, and that it is sent as x-api-key or Authorization: Bearer |
404 on every call | Whether the base URL has a doubled /v1 — use https://a.tokener.ca, not https://a.tokener.ca/v1 |
404 on a model | Whether model is a current id from Models or https://a.tokener.ca/v1/models |
400 without max_tokens | max_tokens is required on this protocol |
400 on an image | Image blocks must be inline — source.type: "base64"; a url source is rejected (see API reference) |
404 on count_tokens | Token counting needs Also allow suppliers without that commitment turned on in Data handling |
