Anthropic compatible
Messages with the Anthropic SDKs, curl and Claude Code, streaming, thinking, token counting and format conversion.
Point any Anthropic client at https://ai.helipod.io and use a Helipod AI key. The request and response follow the Anthropic Messages API, for every model in the catalog.
/v1/messages/v1/messages/count_tokensRequest
The body is an Anthropic messages request: model, max_tokens, messages, and optionally system, stream, temperature, top_p, stop_sequences, tools, tool_choice, thinking and metadata.
modelis a Helipod model id fromGET /v1/models, for exampleglm-5.3.max_tokensabove the model's output limit is lowered to that limit instead of failing.anthropic-versionandanthropic-betaheaders are passed on when you send them. Withoutanthropic-version,2023-06-01is used.
Examples
curl https://ai.helipod.io/v1/messages \
-H "x-api-key: hel_ai_your_key" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "glm-5.3",
"max_tokens": 2000,
"system": "You are a concise assistant.",
"messages": [
{ "role": "user", "content": "Explain what a VPS is in two sentences." }
]
}'The base URL for the Anthropic SDKs is https://ai.helipod.io, without /v1: the SDK adds /v1/messages itself. The OpenAI SDKs are the other way round.
Response
A standard Anthropic message:
{
"id": "msg_0b7e4c1f9a2d4e8b8c6f1a3d",
"type": "message",
"role": "assistant",
"model": "glm-5.3",
"content": [
{ "type": "thinking", "thinking": "The user wants a short definition...", "signature": "" },
{ "type": "text", "text": "A VPS is a virtual server with its own operating system and root access..." }
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 226,
"output_tokens": 310,
"cache_read_input_tokens": 1024,
"cache_creation_input_tokens": 0
}
}The response headers are the same as on the OpenAI route (x-request-id, x-helipod-model and the retirement headers). See Response headers.
Token usage
Anthropic counts the prompt differently from OpenAI: input_tokens excludes the cached part.
| Field | Meaning | Billed as |
|---|---|---|
usage.input_tokens | Prompt tokens that were not read from or written to the cache. | Input |
usage.cache_read_input_tokens | Prompt tokens read from the model's prompt cache. | Cached input (cheaper) |
usage.cache_creation_input_tokens | Prompt tokens written to the cache, on models that report them. | The model's cache-write price (input price when it has none) |
usage.output_tokens | Everything the model wrote, including thinking. | Output |
The whole prompt is input_tokens + cache_read_input_tokens + cache_creation_input_tokens. That total decides the price tier on models priced by prompt length.
Streaming
Set "stream": true to receive the standard event sequence: message_start, content_block_start, content_block_delta (text_delta, thinking_delta, input_json_delta), content_block_stop, message_delta, message_stop.
const stream = client.messages.stream({
model: "deepseek-v4.1-flash",
max_tokens: 2000,
messages: [{ role: "user", content: "Write a haiku about Jakarta rain." }],
});
stream.on("text", (text) => process.stdout.write(text));
const final = await stream.finalMessage();
console.log("\n", final.usage);Good to know:
- Final usage is in
message_delta.message_startmay report zeros; the counts inmessage_deltaare the ones that are billed. - Keep-alive while the model starts. If the model has not started after about 10 seconds, the gateway answers
200and sends SSE comment lines (: waiting for the model, then: keep-aliveevery 15 seconds). SSE clients ignore them. - Errors after the
200(while the gateway was waiting for a slow model) arrive as anevent: errorwith an Anthropic error body. A stream that ends withoutmessage_stopdid not finish. See Limits and errors. - Stopping early. If you close the connection mid-stream, the request is charged for what was generated up to then.
Thinking
Send thinking: { "type": "enabled", "budget_tokens": 4096 } to let a reasoning model think first. Thinking tokens are part of output_tokens, are billed as output, and count against max_tokens, so keep budget_tokens below max_tokens. If the budget is not below the model's output cap, the gateway lowers it to just under the cap.
Tools
tools (with input_schema), tool_choice (auto, any, tool, none) and tool_result blocks work as in the Anthropic API. Server-side tools (for example web search) are not available.
Counting tokens
/v1/messages/count_tokensSend the same body you would send to /v1/messages:
curl https://ai.helipod.io/v1/messages/count_tokens \
-H "x-api-key: hel_ai_your_key" \
-H "Content-Type: application/json" \
-d '{"model": "glm-5.3", "messages": [{"role": "user", "content": "Hello!"}]}'{ "input_tokens": 18 }The count is the gateway's own estimate, deliberately on the high side, and the response carries x-helipod-estimate: true. It is the same estimate used for holds. The real count is in usage after the request.
Claude Code
Claude Code speaks the Anthropic format, so it works with the gateway:
export ANTHROPIC_BASE_URL=https://ai.helipod.io
export ANTHROPIC_AUTH_TOKEN=hel_ai_your_key
export ANTHROPIC_MODEL=glm-5.3
export ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4.1-flash # background tasks
claudeSet ANTHROPIC_MODEL (and the background model) to Helipod model ids. Claude Code's calls to /v1/messages/count_tokens are answered with the free estimate. A claude-* model id that is not in your model list may be answered by a default model chosen by Helipod; the x-helipod-model header shows which model served and billed the request.
OpenAI and Anthropic conversion
Every model accepts both formats. When you call a model in the format it does not speak natively, the gateway converts your request, calls the model, and converts the answer (plain or streamed) back to your format. Token counts are always read from the model's own answer, so billing is the same either way.
What carries over in both directions:
- text, images (base64 and URL), system prompts;
- tools, tool calls and tool results, and
tool_choice; - stop sequences,
temperature,top_p, the output cap; - thinking and reasoning, and stop/finish reasons;
- token usage.
What changes or is dropped:
| From | Behaviour |
|---|---|
Anthropic thinking.budget_tokens | Becomes reasoning_effort: up to 2,048 is low, up to 8,192 medium, more is high. |
OpenAI reasoning_effort | Becomes a thinking budget: minimal 1,024, low 2,048, medium 8,192, high 16,384 tokens (always below the output cap). temperature and top_p are dropped while thinking is on. |
OpenAI temperature | Capped at 1. |
OpenAI response_format | Becomes an instruction in the system prompt to answer with JSON (matching the schema, for json_schema). |
Anthropic stop_sequences | Only the first 4 are kept. |
Anthropic cache_control, server tools | Dropped. The model still caches automatically where it can. |
OpenAI n above 1, logprobs, logit_bias, seed | Dropped. |
| Response ids | New ids are generated (msg_... or chatcmpl-...). Thinking blocks have an empty signature. |
These rules apply only when a conversion happens. If an option you rely on seems to be ignored by a model, try the same request in the other format.