Skip to Content
Launching the open-source gateway

Anthropic Messages

Use the Anthropic SDK with the gateway by changing its base URL.

The gateway serves Anthropic’s Messages API. Point the Anthropic SDK at it and pass your Respan API key as the SDK’s key. Routing, routes, limits, and logging all apply.

POST https://gateway.respan.ai/v1/messages

Terminal
curl https://gateway.respan.ai/v1/messages \ -H "x-api-key: $RESPAN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5-20250929", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}] }'

The gateway reads your Respan key from x-api-key (what the SDK sends) or from Authorization: Bearer.

Request body

The body is Anthropic’s.

FieldTypeDescription
modelstringA model ID from the catalog. Required.
max_tokensintegerMaximum output tokens. Required.
messagesarrayThe conversation, with text, image, and document blocks. Required.
systemstring or arraySystem prompt.
streambooleantrue returns Anthropic server-sent events. See Streaming.
tools, tool_choicearray, objectTools the model may call. See Tool calling.
output_config.formatobjectStructured output. See Structured outputs.
thinkingobjectExtended thinking settings.
temperature, top_p, top_k, stop_sequencesSampling settings.
metadata.respan_paramsobjectRespan params. Removed before the provider sees the body.

Other providers’ models

Requests are routed by model, so the Anthropic SDK can call a model from another provider. The gateway translates the request and writes the answer back in Messages format.

Python
message = client.messages.create( model="gpt-5.5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], )

An Anthropic-only field with no equivalent on the target provider is refused with 400 and named in the error, not dropped. See known gaps. Coding agents such as Claude Code rely on such fields, so keep them on Claude models.

Streaming

Set stream: true and the gateway returns Anthropic server-sent events, whichever provider answers. A stream sends message_start, then content_block_start, content_block_delta and content_block_stop for each block, then message_delta, and ends with message_stop. Usage is reported in message_start (input) and message_delta (output).

Python
stream = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about routers."}], stream=True, ) for event in stream: if event.type == "message_start": print(event.message.usage) elif event.type == "content_block_delta" and event.delta.type == "text_delta": print(event.delta.text, end="") elif event.type == "message_delta": print("\n", event.usage)

If the provider breaks off after the stream has started, the gateway sends the error as an event: error, the last event in the stream. A stream that ends with an error event did not finish, even if some content arrived. For errors before a stream starts, cancellation and billing, and the gateway’s fixed 600 second timeout, see Chat Completions.

Structured outputs

Anthropic’s structured output setting is output_config.format. It is also what the gateway sends to Anthropic models when a Chat Completions request carries a json_schema response_format. Anthropic always enforces the schema strictly.

Terminal
curl https://gateway.respan.ai/v1/messages \ -H "x-api-key: $RESPAN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-5-20250929", "max_tokens": 1024, "messages": [{"role": "user", "content": "Ada Lovelace was born in London in 1815."}], "output_config": { "format": { "type": "json_schema", "schema": { "type": "object", "properties": { "name": {"type": "string"}, "birth_year": {"type": "integer"}, "city": {"type": "string"} }, "required": ["name", "birth_year", "city"], "additionalProperties": false } } } }'

From Chat Completions, a json_object response format without a schema is refused with 400 for Anthropic models, and so is a json_schema with a description or strict: false. See Model support. The gateway doesn’t repair JSON; see Invalid JSON.

Tool calling

Tools use Anthropic’s shape: each tool has a name, a description and an input_schema. When the model calls one, the answer has stop_reason: "tool_use" and a tool_use block with an id and the input. Send the result back in a user message as a tool_result block with the same tool_use_id.

Python
import json 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 Paris?"}] message = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=messages, tools=tools, ) call = next(block for block in message.content if block.type == "tool_use") result = {"temp_c": 18, "sky": "clear"} # your function's output for call.input messages.append({"role": "assistant", "content": message.content}) messages.append({ "role": "user", "content": [{"type": "tool_result", "tool_use_id": call.id, "content": json.dumps(result)}], }) final = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=messages, tools=tools, ) print(final.content[0].text)

For the agent loop, tool_choice, routing by tool support, and how tool calls are logged and billed, see Tool calling on Chat Completions.

Prompt caching

Add cache_control to the blocks you want cached, as in Anthropic’s API. It reaches Anthropic, and cached tokens are priced at Anthropic’s cache rates.

Python
message = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, system=[{ "type": "text", "text": "You are a support agent. <long product manual here>", "cache_control": {"type": "ephemeral"}, }], messages=[{"role": "user", "content": "How do I reset my password?"}], ) print(message.usage)

See Prompt caching.

Pin a provider

To serve a model from a specific provider, send the X-Respan-Route-Provider header, such as X-Respan-Route-Provider: anthropic. You can also send a provider offering as model. To use your own provider account, save its key with BYOK.

Python
message = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], extra_headers={"X-Respan-Route-Provider": "anthropic"}, )

Respan params

Put Respan params inside metadata. The gateway removes them before the provider sees the body.

Python
message = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], metadata={"respan_params": {"customer_identifier": "user_8842", "thread_identifier": "ticket_1042"}}, )

You can also send them in a top-level respan_params (with extra_body) or the X-Data-Respan-Params header. A respan_params value must be a JSON object; a string is ignored. See Respan params.

Response

The body is an Anthropic message, whichever provider answered. Every response carries X-Respan-Log-Id and X-Respan-Gateway-Request-Id. Errors come back in Anthropic’s error shape, {"type": "error", "error": {...}}.

Passthrough instead

To send Anthropic’s body to Anthropic exactly as written, on your own Anthropic key, with no routing or translation, use the passthrough path /api/anthropic/passthrough/v1/messages.

Next steps