Anthropic API

Anthropic Messages API

POST /v1/messages on sference: an Anthropic Messages-compatible endpoint for open models. Auth, streaming SSE, tool use, extended thinking, vision, usage, and the exact compatibility surface.

Anthropic Messages API

sference serves open models behind an Anthropic Messages-compatible endpoint: POST https://api.sference.com/v1/messages. Anything that already speaks the Anthropic Messages API. The anthropic Python/TypeScript SDKs, Claude Code, LangChain's ChatAnthropic, and your own client all point at sference by changing the base URL and the model id.

The request and response bodies are Anthropic-shaped. The models behind them are open-weight checkpoints from the sference catalog, running on European GPUs with the same logging and audit trail as every other endpoint.

Which endpoint should I use?

  • /v1/messages (this page): you already have Anthropic-shaped code, or a tool that only speaks Anthropic (Claude Code).
  • /v1/chat/completions: you have OpenAI-shaped code. Also takes service_tier: "flex", which /v1/messages does not.
  • /v1/responses: sference-native; adds background: true, streams, and the 24h async window.
  • /v1/batches: thousands of rows on the 24h window.

All four share one catalog, one API key, and one billing surface. /v1/messages is realtime only; see Not supported.

Base URL and authentication

EndpointPOST https://api.sference.com/v1/messages
Base URL for Anthropic SDKshttps://api.sference.com
Authx-api-key: sk_... or Authorization: Bearer sk_...
Key formatsference keys (sk_...), minted in the console

Both header styles work, so an Anthropic SDK configured with api_key (which sends x-api-key) and a client configured with a bearer token both authenticate unchanged. anthropic-version is accepted and ignored; there are no dated API versions on sference.

A ?beta=true query parameter is accepted and ignored; Claude Code sends it on every request.

Your first request

curl https://api.sference.com/v1/messages \
  -H "x-api-key: $SFERENCE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "zai-org/GLM-5.2",
    "max_tokens": 1024,
    "messages": [
      { "role": "user", "content": "Say hello in one sentence." }
    ]
  }'
{
  "id": "msg_0f8a1c2e-...",
  "type": "message",
  "role": "assistant",
  "model": "zai-org/GLM-5.2",
  "content": [{ "type": "text", "text": "Hello, good to meet you." }],
  "stop_reason": "end_turn",
  "usage": { "input_tokens": 14, "output_tokens": 9 }
}

Anthropic Python SDK

from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.sference.com",
    api_key="sk_...",  # your sference key, not an Anthropic key
)

message = client.messages.create(
    model="zai-org/GLM-5.2",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(message.content[0].text)

Anthropic TypeScript SDK

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.sference.com",
  apiKey: process.env.SFERENCE_API_KEY,
});

const message = await client.messages.create({
  model: "zai-org/GLM-5.2",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Say hello in one sentence." }],
});

Model ids are sference catalog ids

Pass a catalog id such as zai-org/GLM-5.2 or moonshotai/Kimi-K2.7-Code, not a Claude model name. There are no claude-* aliases; claude-sonnet-4-5 returns 400. Matching is case-insensitive, but the id must exist in your catalog. List what your key can reach with GET /v1/models, and see Models for how to choose.

Request fields

Supported

FieldNotes
modelRequired. sference catalog id.
max_tokensRequired, as in the Anthropic API.
messages[]Required. role is user or assistant; content is a string or a block array.
systemString or text block array. Folded into one leading system message.
streamtrue emits Anthropic SSE; see Streaming.
temperatureForwarded to the engine.
top_pForwarded to the engine.
top_kForwarded to the engine (Anthropic-only knob; it has no OpenAI equivalent).
stop_sequences[]Forwarded as the engine's stop strings.
tools[]Anthropic-native (name + input_schema) and OpenAI-shaped (type: "function") definitions both accepted.
tool_choiceauto, none, and required are honored (string or object form), as is OpenAI's forced-function object. Defaults to auto when tools are present. See the caveat under Tool use.
thinking{"type": "enabled"} / {"type": "disabled"}; see Extended thinking.
enable_thinkingsference extension; a plain boolean, and it wins over thinking.

Content blocks

BlockDirectionSupport
textin / outFull.
tool_usein / outFull; see Tool use.
tool_resultinFull, including content block arrays.
thinkingin / outText is preserved; signature is always "".
imageinOn vision models only; see Images.
document, redacted_thinking, search_result, server tool blocksinRejected with 400 (Unsupported content block type).

Unknown top-level fields are ignored rather than rejected, so a client that sends metadata, service_tier, or container gets a normal response; those values simply have no effect. Unknown content block types are a hard 400, because silently dropping message content would change what the model sees.

Streaming

Set stream: true to receive Server-Sent Events in Anthropic's wire format. Tokens are streamed as the engine produces them.

curl -N https://api.sference.com/v1/messages \
  -H "x-api-key: $SFERENCE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "zai-org/GLM-5.2",
    "max_tokens": 1024,
    "stream": true,
    "messages": [{ "role": "user", "content": "Count to five." }]
  }'

Events emitted, in order:

EventNotes
message_startusage.input_tokens is populated once the prompt is tokenized; the cache counters in this frame are placeholders.
content_block_startFor each thinking, text, and tool_use block.
content_block_deltathinking_delta, signature_delta (always empty), text_delta, and input_json_delta for tool arguments.
content_block_stopPer block.
message_deltaCarries the final stop_reason and the authoritative usage, including cache_read_input_tokens.
message_stopTerminal frame.
pingKeepalive while the request is queued or generating, so intermediaries do not reap a healthy connection during a long time-to-first-token.
errorTerminal. {"type": "error", "error": {"type": "api_error", "message": "..."}}.

Two details worth coding against:

  • Tool arguments arrive as one input_json_delta containing the complete JSON, not as incremental fragments. Clients that concatenate partials work fine; clients that try to parse each fragment as standalone JSON also work.
  • cache_read_input_tokens only appears on message_delta. The prefix-cache hit is not known when message_start is written.

If a stream ends without a final frame, whether a timeout or a worker that dropped, the endpoint emits an error event before closing, so a client is never left hanging on an open connection.

Tool use

Anthropic-native tool definitions work as written:

{
  "model": "moonshotai/Kimi-K2.7-Code",
  "max_tokens": 1024,
  "messages": [{ "role": "user", "content": "What files are in the repo root?" }],
  "tools": [
    {
      "name": "list_files",
      "description": "List files in a directory",
      "input_schema": {
        "type": "object",
        "properties": { "path": { "type": "string" } },
        "required": ["path"]
      }
    }
  ]
}

The model replies with stop_reason: "tool_use" and a tool_use block carrying id, name, and a parsed input object. Send the result back as a tool_result block in a user turn, keyed by tool_use_id, exactly as with Anthropic.

Notes specific to sference:

  • OpenAI-shaped tools are also accepted. A tools[] entry with type: "function" and a nested function.name / function.parameters is normalized alongside the Anthropic name / input_schema form. Mixed arrays are fine. This exists because some Anthropic-compatible clients send the OpenAI shape.
  • tool_choice forcing is partial. auto, none, and required are normalized and honored, in string or object form, as is OpenAI's {"type": "function", "function": {"name": "..."}}. Anthropic's {"type": "any"} and {"type": "tool", "name": "..."} are forwarded to the engine unchanged and are not reliably honored; if you must force a specific tool, send the OpenAI object form.
  • Malformed tool arguments are repaired, not dropped. If a model emits arguments that are not valid JSON, sference retries a repair pass and, failing that, hands you {"__malformed_arguments__": "<raw text>"} rather than an empty input. You always see what the model actually produced.
  • Tool quality tracks the model. Tool calling on open models is parser-dependent; moonshotai/Kimi-K2.7-Code and zai-org/GLM-5.2 are the strongest agentic choices in the catalog today.

Extended thinking

Reasoning models return their chain of thought as a thinking block ahead of the text block, matching Anthropic's response shape.

Thinking is enabled by any of:

  • thinking: {"type": "enabled"} (Anthropic's spelling; {"type": "disabled"} turns it off)
  • enable_thinking: true (sference extension, checked first, so it overrides thinking)
  • nothing at all, on a model whose family reasons by default (Qwen, DeepSeek-R1, Kimi, MiniMax, GLM, Hy3). Anthropic clients omit thinking for third-party models, so sference defaults it on where the model expects it.

Models the catalog marks as non-reasoning always run with thinking off, even if you ask for it; forcing a <think> block on a model that has none makes the decoder treat the entire reply as reasoning and return empty content.

Signatures are empty

thinking blocks come back with signature: "". Anthropic's cryptographic thinking signatures are not implemented, and redacted_thinking blocks are not produced. sference re-injects inbound thinking text on multi-turn requests, but a client that validates signatures will not find a valid one.

budget_tokens, thinking: {"type": "adaptive"}, effort, and display are accepted and ignored; use max_tokens to bound total output.

Images

Image blocks are honored on vision-capable catalog models (Qwen/Qwen3-VL-30B-A3B-Instruct today). Both base64 and url sources work, in user turns and inside tool_result content.

On a text-only model, sference does not fail the request. Image blocks are replaced with [image omitted: this model has no vision], a system notice tells the model it has no vision, and a user-turn reminder discourages retrying screenshot tools. This keeps agent loops, which happily attach screenshots, from spinning on a capability the model does not have. If you need vision, pick a vision model; if you get the sentinel, that is why.

Response shape

{
  "id": "msg_<request-uuid>",
  "type": "message",
  "role": "assistant",
  "model": "zai-org/GLM-5.2",
  "content": [
    { "type": "thinking", "thinking": "...", "signature": "" },
    { "type": "text", "text": "..." },
    { "type": "tool_use", "id": "toolu_...", "name": "list_files", "input": { "path": "." } }
  ],
  "stop_reason": "end_turn",
  "usage": { "input_tokens": 812, "output_tokens": 143, "cache_read_input_tokens": 640 }
}
  • id is msg_ plus the sference request id, the same id you look up in the console and in GET /control/v1/activity/{request_id}. That makes every Anthropic-shaped call traceable in the audit trail without extra bookkeeping.
  • stop_reason is tool_use when the turn ends in a tool call, max_tokens when the completion was truncated, otherwise end_turn.
  • usage.cache_read_input_tokens appears when the prefix cache served part of the prompt; cached input tokens are billed at the lower cached rate. There is no cache_creation_input_tokens; caching is automatic and never billed as a write.
  • stop_sequence is not returned on non-streaming responses (streaming message_delta carries stop_sequence: null).

Errors

HTTP status codes follow the usual semantics, but error bodies are sference-shaped, not Anthropic-shaped:

{ "status_code": 400, "detail": "Unsupported content block type: 'document'", "extra": {} }

Anthropic SDKs still raise the right exception class from the status code, but error.type / error.message will not be populated the way they are against api.anthropic.com. Read detail. (SSE error events during a stream are Anthropic-shaped.)

StatusCause
400Unknown model, unsupported content block, malformed body, image that failed validation or fetch.
401Missing or revoked key.
402Team balance exhausted (no negative balance allowance).
504Inference did not complete within the sync wait (600s default). The request is cancelled server-side, so no work is billed after the timeout.

Not supported

/v1/messages is a realtime endpoint. Anything queued, discounted, or bulk lives on the other surfaces:

Not on /v1/messagesUse instead
service_tier: "flex" (ignored here)/v1/chat/completions or /v1/responses; see processing modes
Background / async executionPOST /v1/responses with background: true
Bulk jobs on the 24h windowPOST /v1/batches
POST /v1/messages/count_tokensNot implemented; read usage.input_tokens off a real response
Batches, Files, Models list in Anthropic's shapesference-native /v1/batches, /v1/models
Prompt caching controls (cache_control blocks, anthropic-beta headers)Prefix caching is automatic; hits are reported as cache_read_input_tokens
MCP connectors, web search, code execution, computer useNot implemented; server-side tools are Anthropic-hosted
Citations, document blocks, PDF inputNot implemented
Thinking signatures, redacted_thinkingNot implemented; see Extended thinking

Differences from api.anthropic.com at a glance

Anthropicsference /v1/messages
Modelsclaude-*sference catalog ids (open weights, pinned)
Authx-api-keyx-api-key or Authorization: Bearer
Versioninganthropic-version requiredAccepted and ignored
Unknown top-level fieldsRejectedIgnored
Error body{"type": "error", "error": {...}}{"status_code", "detail", "extra"}
Thinking signaturesCryptographically signedAlways ""
Prompt cachingExplicit cache_controlAutomatic prefix cache, read-side reporting only
Processing tiersStandard / batch / priorityRealtime only on this endpoint
Data residencyUSEuropean GPUs, audit trail on every request

Next steps