ModelSiteAPI

Messages (Anthropic-compatible)

POST /v1/messages — Anthropic Messages API, with complete examples by scenario

Compatible with the Anthropic Messages API. Underneath it is the same routing pipeline as Chat Completions; only the request/response format is adapted to Anthropic style.

This page is organized by scenario: start with the scenario overview, then go to the section for request, response and caveats. Every example carries an evidence label. Tested means it was called for real with a live test account (2026-10-19); response fields come from the real reply, with id, reasoning text and similar values replaced by placeholders. Per official docs (not tested) means it was written from the Anthropic documentation and we did not verify it with a real call.

Authentication difference (important): the platform's /messages uses Authorization: Bearer $MODELSITE_API_KEY, not the official Anthropic x-api-key. With the official anthropic SDK pass auth_token (sent as Bearer) and do not pass api_key (it sends x-api-key and gets 401). The official SDK adds the anthropic-version header automatically; the platform neither enforces it nor forwards it upstream, but when writing raw requests keep sending it for compatibility. The anthropic-beta header is passed through.

Scenario overview

Scenarios follow Anthropic's Messages, Streaming, Tool use, Vision, Extended thinking, Prompt caching and Structured outputs docs.

ScenarioOffered officiallyOur statusExample
Basic chat (single turn, system)YesSupportedBasic chat
Multi-turn (you send the history)YesSupportedMulti-turn
StreamingYesSupportedStreaming
Tool use (tool_use / tool_result, tool_choice)YesSupported: auto / any / tool / none all work; disable_parallel_tool_use: true becomes parallel_tool_calls: falseTool use
Extended thinking (thinking)YesSupported (thinking blocks carry no signature)Thinking
Image input (image blocks)YesPartial: needs a vision model; base64 tested, url source not tested, file_id not supportedImage input
Document input (document blocks, PDF)YesPartial: base64 source only, converted to file; not testedDocument input
Prompt cachingYes (cache_control)Partial: hits from the upstream's automatic caching are reported; explicit cache_control is kept only for some modelsPrompt caching
Token counting (/v1/messages/count_tokens)YesSupportedToken counting
Structured output (output_config.format)YesNot supported: in testing the field was ignored without error and the reply was not JSON; for structured output use response_format in Chat Completions—
Server tools (web_search, web_fetch, code_execution, tool_search)YesNot supported: not converted, forwarded as-is; OpenAI-compatible upstreams do not recognize them—
Client built-in tools (bash, text_editor, computer, memory)YesNot supported: not converted to function tools—
Citations and search results (citations, search_result blocks)YesNot supported: not converted—
Files API, Message Batches, MCP connector, Skills, containerYesNot connected: the platform has no matching API—

Basic chat

When to use: one question, one answer, or the top-level system to set role and tone (in Messages, system is not part of messages).

Evidence: tested (live test account, 2026-10-19).

Request

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3-flash",
    "max_tokens": 1500,
    "system": "Answer briefly",
    "messages": [{"role": "user", "content": "Explain quantum entanglement in one sentence"}]
  }'

max_tokens is required and must be greater than 0. For thinking models the thinking also counts toward max_tokens; if it is too small the reply is cut off during thinking.

Response

The reply text is the text of the blocks in content[] whose type is text; the stop reason is stop_reason; usage is usage. A thinking model adds a thinking block before the text block, so select by type instead of hard-coding an index.

{
  "id": "msg_xxxx",
  "type": "message",
  "role": "assistant",
  "content": [
    { "type": "thinking", "thinking": "The user asks in Chinese to explain quantum entanglement in one sentence… (omitted)" },
    { "type": "text", "text": "量子纠缠是指两个或多个粒子形成一种关联状态后,无论相距多远,对其中一个粒子的测量会瞬间决定另一个粒子的状态,这种超越经典物理的关联性被爱因斯坦称为“幽灵般的超距作用”。" }
  ],
  "model": "glm-5.3-flash",
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 23,
    "output_tokens": 80,
    "reasoning_tokens": 28
  }
}

The reply text above is real output for a Chinese prompt (the real request was written in Chinese).

Notes

  • On OpenAI-compatible upstreams stop_reason can only be end_turn / max_tokens / tool_use; hitting stop_sequences is also end_turn with stop_sequence set to null, so you cannot see which sequence matched. Only Anthropic-native upstreams can produce stop_sequence / pause_turn / refusal.
  • usage.reasoning_tokens is a platform extension (the official API uses output_tokens_details.thinking_tokens, which the platform does not emit).
  • The API card below is rendered from the OpenAPI spec (authentication, request-body parameter table, response schema).
POST
/messages

Authorization

BearerAuth
AuthorizationBearer <token>

Authorization: Bearer $MODELSITE_API_KEY(与 OpenAI 格式共享同一 Key)

In: header

Header Parameters

anthropic-version?string

Anthropic API 版本(平台不强制校验,官方 SDK 自动携带)。

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/messages" \  -H "anthropic-version: 2023-06-01" \  -H "Content-Type: application/json" \  -d '{    "model": "string",    "messages": [      {}    ],    "max_tokens": 1  }'

取回复:content[] 里 type 为 text 的块。思考模型会多一个 thinking 块(无 signature)。取自线上真实响应(glm-5.3-flash,2026-10-19),取值为占位。

{  "id": "msg_xxxx",  "type": "message",  "role": "assistant",  "content": [    {      "type": "thinking",      "thinking": "The user asks in Chinese to explain quantum entanglement in one sentence. I should respond in Chinese, concisely, as requested."    },    {      "type": "text",      "text": "量子纠缠是指两个或多个粒子形成一种关联状态后,无论相距多远,对其中一个粒子的测量会瞬间决定另一个粒子的状态,这种超越经典物理的关联性被爱因斯坦称为“幽灵般的超距作用”。"    }  ],  "model": "glm-5.3-flash",  "stop_reason": "end_turn",  "stop_sequence": null,  "usage": {    "input_tokens": 23,    "output_tokens": 80,    "reasoning_tokens": 28  }}

Multi-turn

When to use: conversations that need context. The platform does not store conversations; put the full history in messages on each request, alternating user / assistant.

Evidence: per official docs (not tested). Multi-turn with tools was tested, see Tool use.

Request

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3-flash",
    "max_tokens": 1500,
    "messages": [
      {"role": "user", "content": "What is quantum entanglement?"},
      {"role": "assistant", "content": "Two particles share a state regardless of distance."},
      {"role": "user", "content": "Can it be used for faster-than-light communication?"}
    ]
  }'

Response: same structure as Basic chat.

Notes

  • The role of the first message must be user or assistant, otherwise 400.
  • Whether a trailing assistant message ("prefill") is accepted depends on the upstream; not tested.
  • The rules for sending thinking blocks back are under Thinking.

Streaming

When to use: show output token by token and lower time to first token.

Evidence: tested (live test account, 2026-10-19).

Request

curl -N https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3-flash",
    "max_tokens": 600,
    "stream": true,
    "reasoning_effort": "low",
    "messages": [{"role": "user", "content": "Reply with one character: 好"}]
  }'

Response

Each event is an event: line plus a data: line. Concatenate text_delta.text for the text; there is no [DONE], the stream ends with message_stop.

event: message_start
data: {"type":"message_start","message":{"id":"msg_xxxx","type":"message","role":"assistant","content":[],"model":"glm-5.3-flash","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}}

event: ping
data: {"type":"ping"}

event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"thinking":"","type":"thinking"}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"Simple"}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":": reply \"好\"."}}

event: content_block_stop
data: {"type":"content_block_stop","index":0}

event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"text":"","type":"text"}}

event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"好"}}

event: content_block_stop
data: {"index":1,"type":"content_block_stop"}

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":18,"output_tokens":9,"reasoning_tokens":6}}

event: message_stop
data: {"type":"message_stop"}

Notes

  • The usage in message_start has no real usage yet (all zeros); use message_delta.usage.
  • The event order is message_start → content_block_start → content_block_delta (many) → content_block_stop → message_delta → message_stop.
  • In this example the thinking block has no signature_delta; it appears only when the upstream supplies a signature.
  • A few events (such as the content_block_stop above) carry an upstream-specific numeric stop_reason field; ignore it.
  • In-stream errors are sent as event: error; see "Errors" below.

Tool use

When to use: let the model decide to call tools you define. The flow: declare tools → the model returns a tool_use block → you run it → send back a tool_result → the model gives the final answer.

Evidence: tested (live test account, 2026-10-19): a single call and returning the result. tool_choice any / tool is per official docs (not tested); the tool_choice: none and disable_parallel_tool_use conversions are covered by unit tests (not tested live).

Request (first turn)

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3-flash",
    "max_tokens": 1500,
    "tools": [{
      "name": "get_weather",
      "description": "Get the current weather for a city",
      "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }],
    "messages": [{"role": "user", "content": "What is the weather in Hangzhou tomorrow?"}]
  }'

Response (first turn)

stop_reason is tool_use, and the tool arguments are a parsed object input (not a string).

{
  "id": "msg_xxxx",
  "type": "message",
  "role": "assistant",
  "content": [
    { "type": "thinking", "thinking": "The user is asking about tomorrow's weather in Hangzhou… (omitted)" },
    {
      "type": "tool_use",
      "id": "chatcmpl-tool-xxxxxxxx",
      "name": "get_weather",
      "input": { "city": "杭州" }
    }
  ],
  "model": "glm-5.3-flash",
  "stop_reason": "tool_use",
  "stop_sequence": null,
  "usage": { "input_tokens": 160, "output_tokens": 86, "reasoning_tokens": 74 }
}

Returning the tool result (second turn)

Add the assistant message's content back unchanged, then send a user message containing a tool_result block matched by tool_use_id; the tool_result block must come first in that message's content.

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3-flash",
    "max_tokens": 1500,
    "tools": [ ... same as the first turn ... ],
    "messages": [
      {"role": "user", "content": "What is the weather in Hangzhou tomorrow?"},
      {"role": "assistant", "content": [
        {"type": "tool_use", "id": "chatcmpl-tool-xxxxxxxx", "name": "get_weather", "input": {"city": "杭州"}}
      ]},
      {"role": "user", "content": [
        {"type": "tool_result", "tool_use_id": "chatcmpl-tool-xxxxxxxx", "content": "Cloudy, 7-13°C"}
      ]}
    ]
  }'

The second-turn response is an ordinary success response (tested; this request carried reasoning_effort: "low", so there is no thinking block):

{
  "id": "msg_xxxx",
  "type": "message",
  "role": "assistant",
  "content": [
    { "type": "text", "text": "杭州明天天气为**多云**,气温在 **7~13°C** 之间。早晚偏凉,建议适当添加衣物保暖。" }
  ],
  "model": "glm-5.3-flash",
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": { "input_tokens": 182, "output_tokens": 31 }
}

When streaming, the tool_use block gives id and name in content_block_start (input is an empty object), and the arguments arrive as input_json_delta.partial_json fragments to concatenate and then parse (per official docs, not tested):

event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_xxxx","name":"get_weather","input":{}}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"city\":"}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"\"Hangzhou\"}"}}

event: content_block_stop
data: {"type":"content_block_stop","index":0}

Notes

  • tool_choice: auto → auto, any → required, tool → a specific function. none → none; disable_parallel_tool_use: true → parallel_tool_calls: false.
  • The format of tool_use.id depends on the upstream (here a chatcmpl-tool- prefix); send it back unchanged.
  • is_error on tool_result is not passed to OpenAI-compatible upstreams.
  • Only custom tools (name + input_schema) are converted; server tools and built-in tools are not, see the scenario overview.

Thinking

When to use: you want the model to reason before answering.

Evidence: tested (live test account, 2026-10-19): glm-5.3-flash returns a thinking block by default, and with reasoning_effort: "low" thinking is essentially empty. The effect of thinking: {"type":"enabled","budget_tokens":N} depends on the upstream and was not tested.

Request: whether thinking is on and how its strength is controlled depends on the model. GLM models always think; control strength with the top-level reasoning_effort (in the Python SDK put it in extra_body; the platform forwards it as-is). For other models see the vendor pages.

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3-flash",
    "max_tokens": 1500,
    "reasoning_effort": "low",
    "messages": [{"role": "user", "content": "Explain quantum entanglement in one sentence"}]
  }'

Response: thinking is a content[] block with type thinking, placed before text / tool_use; a real response is under Basic chat. When streaming, thinking is thinking_delta before the answer; the real events are under Streaming.

Notes

  • The platform never turns thinking on or off by default. If you send thinking it is forwarded as-is (except GPT models, see below); if you do not, the upstream default applies.
  • thinking: {"type": "adaptive"} is an Anthropic-specific value: it is forwarded as-is for MiniMax and dropped for other OpenAI-compatible models, which then use their own default; when output_config.effort is also present the platform converts it to reasoning_effort (except MiniMax).
  • budget_tokens is not converted to reasoning_effort (GPT / o-series are the exception: the budget is bucketed into low / medium / high). To control strength, add the upstream-supported top-level field, such as reasoning_effort, directly in the request body.
  • Thinking blocks have no signature: in testing, thinking blocks from OpenAI-compatible upstreams carry no signature. Only Anthropic-native upstreams sign them, and then multi-turn conversations must send thinking blocks (with signature) back unchanged.
  • Sending thinking back: thinking content blocks in earlier assistant messages are converted to reasoning_content for models that need it back (DeepSeek, GLM, Qwen, MiniMax); other models drop them. Your client still has to put the previous turn's thinking back in full across turns (especially tool calls). (The conversion logic here depends on code that is not yet released.)
  • See DeepSeek, GLM, Qwen and MiniMax for per-model values and limits.

Image input

When to use: have a vision model answer questions about an image. Choose a model that accepts image input (such as qwen3-vl-plus).

Evidence: base64 source tested (live test account, 2026-10-19, qwen3-vl-plus); url source per official docs (not tested).

Request

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-vl-plus",
    "max_tokens": 300,
    "messages": [{
      "role": "user",
      "content": [
        {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "iVBORw0KGgo..."}},
        {"type": "text", "text": "What is in this image? One sentence."}
      ]
    }]
  }'

Response

{
  "id": "msg_xxxx",
  "type": "message",
  "role": "assistant",
  "content": [
    { "type": "text", "text": "一个红色的实心圆。" }
  ],
  "model": "qwen3-vl-plus",
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": { "input_tokens": 16, "output_tokens": 8 }
}

Notes

  • On this endpoint usage.input_tokens counts only the text part, not image tokens: the same image in Chat Completions gives prompt_tokens 82 (of which image_tokens 66), while here input_tokens is only 16. For the full input usage use Chat Completions, or go by the bill.
  • With source.type url the upstream must be able to reach the address; use base64 when unsure. file_id (Files API) is not supported.

Document input (PDF)

When to use: pass a PDF or similar document to the model as an input block.

Evidence: per official docs (not tested).

Request

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-vl-plus",
    "max_tokens": 600,
    "messages": [{
      "role": "user",
      "content": [
        {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": "JVBERi0xLjQK..."}},
        {"type": "text", "text": "Summarize this document"}
      ]
    }]
  }'

Response: same structure as Basic chat.

Notes

  • Only document blocks with a base64 source are converted to a file content part; url, text, content and file_id sources are not converted and are forwarded as-is, which the upstream usually does not recognize.
  • Whether the upstream can read PDFs, and its page and size limits, vary by model and were not verified.

Prompt caching

When to use: many requests share a long prefix (system prompt, documents, tool definitions) and you want lower cost and latency.

Evidence: tested (live test account, 2026-10-19, deepseek-v4.1-flash): with cache_control on the system text block and a prefix of about 2,900 tokens that was already cached, cache_read_input_tokens was 2,816.

Request

curl https://api.modelsite.ai/v1/messages \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash",
    "max_tokens": 300,
    "reasoning_effort": "low",
    "system": [{
      "type": "text",
      "text": "You are a support assistant. Answer using these terms:\n(about 2,900 tokens of long text here)",
      "cache_control": {"type": "ephemeral"}
    }],
    "messages": [{"role": "user", "content": "What does clause 3 say? One sentence."}]
  }'

Response

{
  "id": "msg_xxxx",
  "type": "message",
  "role": "assistant",
  "content": [
    { "type": "thinking", "thinking": "We need to answer the user's question… (omitted)" },
    { "type": "text", "text": "第3条规定:平台对所有请求按实际用量计费,调用方应当在客户端保存请求编号以便对账。" }
  ],
  "model": "deepseek-v4.1-flash",
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 87,
    "output_tokens": 91,
    "cache_read_input_tokens": 2816,
    "reasoning_tokens": 66
  }
}

Notes

  • Under Anthropic's convention input_tokens excludes the cached part: here 87 + 2,816 = 2,903, the total input.
  • Caching is decided by the upstream. This model's caching is automatic on the upstream side; the cache_control in the request has no effect on it (it is removed), and requests without the marker hit as well.
  • Explicit cache_control: kept for claude / gemini-prefixed models; kept for Qwen but normalized to {"type":"ephemeral"} (ttl removed); removed for other models. A top-level cache_control is not converted. The Qwen item depends on code that is not yet released.
  • For upstreams that write caches, usage also shows cache_creation_input_tokens and the TTL-split cache_creation.
  • The official max_tokens: 0 (cache warm-up only) is not supported.

Token counting

When to use: estimate input token count before sending a request.

Evidence: tested (live test account, 2026-10-19): sending Hi to glm-5.3-flash returned {"input_tokens": 8}. Whether the count matches the input_tokens actually billed for that model was not verified.

Request

curl https://api.modelsite.ai/v1/messages/count_tokens \
  -H "Authorization: Bearer $MODELSITE_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "glm-5.3-flash",
    "messages": [{"role": "user", "content": "Hi"}]
  }'

Response

{ "input_tokens": 8 }

Errors

401 invalid or missing key

{"type":"error","error":{"type":"authentication_error","message":"invalid API key"}}

When the Authorization header is missing, message is missing or invalid authorization header.

402 insufficient balance

{"type":"error","error":{"type":"billing_error","message":"insufficient balance"}}

429 rate limited

{"type":"error","error":{"type":"rate_limit_error","message":"too many requests"}}

The response carries Retry-After (seconds); back off accordingly.

400 unknown model

{"type":"error","error":{"type":"invalid_request_error","message":"unknown model: not-a-model"}}

A wrong or not-yet-live model is a 400 (not 404).

400 invalid parameters (Anthropic format)

{"type":"error","error":{"type":"invalid_request_error","message":"max_tokens is required and must be > 0"}}

400s the platform validates itself include: max_tokens missing or not greater than 0; the first message's role is not user / assistant; tool_choice.type is tool without a name.

403 no access to this model

{"type":"error","error":{"type":"permission_error","message":"model 'glm-5.3-flash' is not allowed for this API key"}}

503 gateway at capacity

{"type":"error","error":{"type":"overloaded_error","message":"gateway at capacity, retry later"}}

Also carries Retry-After: 5.

Upstream temporarily unavailable (502 / 503): a temporarily unavailable upstream returns 502 (error.type is server_error); when all upstreams are in temporary cooldown it returns 503 (error.code is no_upstream_available). Both were seen in testing on Chat Completions; Retry shortly.

Note: on this endpoint (including /v1/messages/count_tokens), errors the gateway produces (401 / 402 / 403 / 429 / unknown model / 503 capacity) and errors from the routing layer (parameters, upstream failures and so on) all use the Anthropic-style envelope, with Anthropic's official type names; insufficient balance is billing_error.

If a streaming request fails after output has started, the HTTP status is already 200 and the error is sent as an in-stream event: event: error with data {"type":"error","error":{"type":"<type>","message":"<message>"}}. Full error codes and handling advice: Errors.

Which format?

Existing codeRecommended entry
OpenAI / LangChain (OpenAI) / most frameworks/chat/completions
Anthropic SDK/messages (this page)

Both entries reach the same models.

Unsupported or different parameters

The request is converted to Chat Completions form and then sent to the upstream. The platform does not drop parameters against a parameter allowlist and does not clamp values: apart from the hard-coded conversions in the table below, official parameters are not converted and are forwarded as unknown top-level fields; whether the upstream recognizes them, ignores them or rejects them (4xx) is up to it.

ParameterBehaviorNotes
max_tokensRequired, must be > 0Missing or 0 returns 400. The official 0 (cache warm-up only) is not supported.
messagesPartialThe first role must be user or assistant. Content blocks converted: text, image (base64 / url source), tool_use, tool_result, thinking, audio (base64), document (base64 source only, converted to file); document with url / text / content sources, file_id sources, search_result and other blocks are not converted and are forwarded as-is. is_error on tool_result is not passed to OpenAI-compatible upstreams
systemRewrittenString or array of text blocks, converted to leading system messages, one per text block; non-text blocks are ignored
stop_sequencesRewrittenConverted to stop. When an OpenAI-compatible upstream hits a stop sequence, the response stop_reason is end_turn and stop_sequence is null; the matched sequence is not available
temperature top_p top_kPass-throughForwarded to the upstream, which decides the valid range; out-of-range values may be rejected by the upstream
tool_choicePartialauto → auto, any → required, tool → a specific function. none → none; disable_parallel_tool_use: true → parallel_tool_calls: false
toolsPartialCustom tools (name + input_schema) are converted to function tools. Server tools (such as web_search_20250305, code_execution_*, text_editor_*, bash_*, computer_*) are not converted and are forwarded as-is; OpenAI-compatible upstreams usually do not recognize them
thinkingRewrittenenabled / disabled forwarded as-is; adaptive is kept only for MiniMax and dropped for other OpenAI-compatible models. GPT / o-series convert thinking to reasoning_effort. See "Thinking" above (depends on unreleased code)
output_config.effortRewrittenConverted to reasoning_effort only when thinking.type = "adaptive" and there is no top-level reasoning_effort (except MiniMax)
output_config.format (structured output)Not convertedNot turned into response_format; the whole output_config is forwarded as an unknown field; in testing it was ignored and the reply was not JSON. For structured output use response_format in Chat Completions
metadataPartialOnly metadata.user_id is used, converted to user
service_tierValue not mappedPlaced into service_tier as-is; Anthropic's auto / standard_only differ from OpenAI-style values, so the upstream may reject or ignore it
cache_control (top level)Not convertedForwarded as an unknown field; OpenAI-compatible upstreams usually ignore it
container mcp_servers speed inference_geo diagnosticsNot convertedForwarded as unknown fields; container and mcp_servers mean nothing to OpenAI-compatible upstreams
cache_control on content and system blocksRemoved or rewrittenKept for claude / gemini-prefixed models; kept for Qwen but normalized to {"type":"ephemeral"} (ttl removed); deleted for all other models (the Qwen part depends on unreleased code)
thinking blocks in historyHandled per modelConverted to reasoning_content for DeepSeek, GLM, Qwen, MiniMax; kept for claude / gemini-prefixed models; deleted for other models. redacted_thinking is deleted as well (except claude / gemini-prefixed models) (depends on unreleased code)
Response stop_details, usage.output_tokens_details, usage.service_tier, usage.inference_geoNot returnedNewer official response fields the platform does not emit; for thinking tokens see the platform extension usage.reasoning_tokens

Platform extensions

What follows is defined by the platform and does not exist in the official Anthropic API.

Authentication and request headers

  • Authentication uses Authorization: Bearer, not the official x-api-key (see the note at the top).
  • The gateway only lets these request headers through and drops all others: Authorization, Content-Type, Accept, Accept-Encoding, Origin, User-Agent, Anthropic-Beta, OpenAI-Beta. Anthropic-Beta is passed to the upstream; anthropic-version is neither validated nor forwarded.
  • X-Request-ID: a value you send is kept and echoed in the response header; if absent, the platform generates one.

Request parameters

ParameterNotes
Top-level reasoning_effort and other vendor fieldsTo control thinking strength, add the upstream-supported top-level field in the request body; it is forwarded as-is
/v1/messages/count_tokensAvailable

Response headers

HeaderNotes
X-Request-IDRequest ID; give it to us when troubleshooting
Retry-AfterSuggested back-off seconds on 429; at gateway capacity it returns 503 with Retry-After: 5
Content-Type: text/event-stream, Cache-Control: no-cache, X-Accel-Buffering: noStreaming responses; when a stream is idle the gateway sends a : heartbeat comment frame every 15 seconds, which SSE clients ignore automatically

The platform does not return X-RateLimit-* or anthropic-ratelimit-* quota headers.

Response fields

FieldNotes
usage.reasoning_tokensThinking token count (the official API uses output_tokens_details.thinking_tokens, which the platform does not emit)
usage.cache_read_input_tokens, cache_creation_input_tokens, cache_creationCache usage; present only when non-zero
usage.cost, usage.cost_detailsThis endpoint does not return cost fields; for cost use Chat Completions or Responses

Automatic behavior

  • Streaming requests turn on include_usage internally; usage is in message_delta.usage.
  • Automatic retry and upstream switching on upstream failure follow the same rules as Chat Completions: only before you receive any response content; parameter errors and content-safety blocks are not retried.
  • When the balance runs out during streaming, the stream is cut and you then receive a data: {"error":{"type":"insufficient_quota","message":"balance exhausted"}} frame and data: [DONE]; this frame is OpenAI-style, not Anthropic's event: error.

On this page