# 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.

Source: https://sference.com/docs/anthropic

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](https://platform.claude.com/docs/en/api/messages). The `anthropic` Python/TypeScript SDKs, [Claude Code](https://sference.com/docs/anthropic/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](https://sference.com/docs/models), 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](https://sference.com/docs/guides/batches).
>
> All four share one catalog, one API key, and one billing surface. `/v1/messages` is **realtime only**; see [Not supported](#not-supported).

## Base URL and authentication

| | |
| --- | --- |
| **Endpoint** | `POST https://api.sference.com/v1/messages` |
| **Base URL for Anthropic SDKs** | `https://api.sference.com` |
| **Auth** | `x-api-key: sk_...` **or** `Authorization: Bearer sk_...` |
| **Key format** | sference keys (`sk_...`), minted in the [console](https://app.sference.com) |

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

```bash
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." }
    ]
  }'
```

```json
{
  "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

```python
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

```ts
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](https://sference.com/docs/models) for how to choose.

## Request fields

### Supported

| Field | Notes |
| --- | --- |
| `model` | **Required.** sference catalog id. |
| `max_tokens` | **Required**, as in the Anthropic API. |
| `messages[]` | **Required.** `role` is `user` or `assistant`; `content` is a string or a block array. |
| `system` | String or `text` block array. Folded into one leading system message. |
| `stream` | `true` emits Anthropic SSE; see [Streaming](#streaming). |
| `temperature` | Forwarded to the engine. |
| `top_p` | Forwarded to the engine. |
| `top_k` | Forwarded 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_choice` | `auto`, `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](#tool-use). |
| `thinking` | `{"type": "enabled"}` / `{"type": "disabled"}`; see [Extended thinking](#extended-thinking). |
| `enable_thinking` | sference extension; a plain boolean, and it wins over `thinking`. |

### Content blocks

| Block | Direction | Support |
| --- | --- | --- |
| `text` | in / out | Full. |
| `tool_use` | in / out | Full; see [Tool use](#tool-use). |
| `tool_result` | in | Full, including `content` block arrays. |
| `thinking` | in / out | Text is preserved; `signature` is always `""`. |
| `image` | in | On vision models only; see [Images](#images). |
| `document`, `redacted_thinking`, `search_result`, server tool blocks | in | **Rejected 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.

```bash
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:

| Event | Notes |
| --- | --- |
| `message_start` | `usage.input_tokens` is populated once the prompt is tokenized; the cache counters in this frame are placeholders. |
| `content_block_start` | For each `thinking`, `text`, and `tool_use` block. |
| `content_block_delta` | `thinking_delta`, `signature_delta` (always empty), `text_delta`, and `input_json_delta` for tool arguments. |
| `content_block_stop` | Per block. |
| `message_delta` | Carries the final `stop_reason` and the authoritative `usage`, including `cache_read_input_tokens`. |
| `message_stop` | Terminal frame. |
| `ping` | Keepalive while the request is queued or generating, so intermediaries do not reap a healthy connection during a long time-to-first-token. |
| `error` | Terminal. `{"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:

```json
{
  "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

```json
{
  "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**:

```json
{ "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.)

| Status | Cause |
| --- | --- |
| **400** | Unknown model, unsupported content block, malformed body, image that failed validation or fetch. |
| **401** | Missing or revoked key. |
| **402** | Team balance exhausted (no negative balance allowance). |
| **504** | Inference 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/messages` | Use instead |
| --- | --- |
| `service_tier: "flex"` (ignored here) | `/v1/chat/completions` or `/v1/responses`; see [processing modes](https://sference.com/docs/guides/responses#processing-modes) |
| Background / async execution | `POST /v1/responses` with `background: true` |
| Bulk jobs on the 24h window | [`POST /v1/batches`](https://sference.com/docs/guides/batches) |
| `POST /v1/messages/count_tokens` | Not implemented; read `usage.input_tokens` off a real response |
| Batches, Files, Models list in Anthropic's shape | sference-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 use | Not implemented; server-side tools are Anthropic-hosted |
| Citations, `document` blocks, PDF input | Not implemented |
| Thinking signatures, `redacted_thinking` | Not implemented; see [Extended thinking](#extended-thinking) |

## Differences from api.anthropic.com at a glance

| | Anthropic | sference `/v1/messages` |
| --- | --- | --- |
| Models | `claude-*` | sference catalog ids (open weights, [pinned](https://sference.com/docs/models)) |
| Auth | `x-api-key` | `x-api-key` **or** `Authorization: Bearer` |
| Versioning | `anthropic-version` required | Accepted and ignored |
| Unknown top-level fields | Rejected | Ignored |
| Error body | `{"type": "error", "error": {...}}` | `{"status_code", "detail", "extra"}` |
| Thinking signatures | Cryptographically signed | Always `""` |
| Prompt caching | Explicit `cache_control` | Automatic prefix cache, read-side reporting only |
| Processing tiers | Standard / batch / priority | Realtime only on this endpoint |
| Data residency | US | European GPUs, [audit trail](https://sference.com/docs/guides/responses) on every request |

## Next steps

- [Claude Code on sference](https://sference.com/docs/anthropic/claude-code): Run Claude Code against open models: one-command launch, hybrid routing, manual config.
- [Models](https://sference.com/docs/models): Catalog ids, pinning, and which checkpoints are strongest for agentic tool use.
- [API Reference](https://sference.com/docs/api-reference): Generated schemas for /v1/messages and every other operation.
- [Quickstart](https://sference.com/docs/quickstart): API keys, the CLI, and your first request on any endpoint.
