Claude-Native Endpoint: /v1/messages
Call the full Claude lineup over the native Anthropic protocol — message structure, the system parameter, extended thinking, native tool use and multi-turn conversations.
Endpoint: https://api.zaimod.com/v1/messages — structurally identical to the official Anthropic API. The official anthropic SDK only needs a new base_url and key.
import anthropic
client = anthropic.Anthropic(
api_key="sk-your-zaimod-key",
base_url="https://api.zaimod.com",
)Basic call
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=4096, # required
system="You are a senior code reviewer.", # system is a top-level parameter
messages=[
{"role": "user", "content": "Review this function's error handling"}
],
)
print(message.content[0].text)
print(message.usage) # input_tokens / output_tokensThree structural differences from the OpenAI protocol:
max_tokensis required- The system prompt is its own parameter, not a message
- The response
contentis an array of blocks (text / thinking / tool_use)
Multi-turn conversations
The Anthropic protocol never auto-appends — you pass full history every turn:
history = [{"role": "user", "content": "What is idempotency?"}]
m1 = client.messages.create(model="claude-sonnet-5", max_tokens=1024, messages=history)
history.append({"role": "assistant", "content": m1.content[0].text})
history.append({"role": "user", "content": "Give me a RESTful example"})
m2 = client.messages.create(model="claude-sonnet-5", max_tokens=1024, messages=history)Streaming
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=2048,
messages=[{"role": "user", "content": "Explain the CAP theorem"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)Extended thinking
Thinking models (e.g. claude-sonnet-5-thinking) emit reasoning before the answer:
resp = client.messages.create(
model="claude-sonnet-5-thinking",
max_tokens=16000,
thinking={
"type": "enabled",
"budget_tokens": 8000, # thinking token budget
},
messages=[{"role": "user", "content": "Which is larger, 9.11 or 9.9?"}],
)
for block in resp.content:
if block.type == "thinking":
print("[thinking]", block.thinking[:200])
elif block.type == "text":
print("[answer]", block.text)Native tool use
tools = [{
"name": "get_order",
"description": "Look up an order by ID",
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
}]
resp = client.messages.create(
model="claude-sonnet-5",
max_tokens=2048,
tools=tools,
messages=[{"role": "user", "content": "Check order A1024"}],
)
tool_use = next(b for b in resp.content if b.type == "tool_use")
print(tool_use.name, tool_use.input)
# Return the result
resp2 = client.messages.create(
model="claude-sonnet-5",
max_tokens=2048,
tools=tools,
messages=[
{"role": "user", "content": "Check order A1024"},
{"role": "assistant", "content": resp.content},
{"role": "user", "content": [{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": '{"status": "shipped"}',
}]},
],
)Multimodal input
import base64
with open("chart.png", "rb") as f:
data = base64.standard_b64encode(f.read()).decode()
resp = client.messages.create(
model="claude-sonnet-5",
max_tokens=2048,
messages=[{"role": "user", "content": [{
"type": "image",
"source": {"type": "base64", "media_type": "image/png", "data": data},
}, {
"type": "text", "text": "Summarise this chart in three bullets",
}]}],
)The Claude lineup
| Model | Positioning |
|---|---|
| claude-sonnet-5 / -thinking | Workhorse: coding, agents, long documents |
| claude-opus-5 / -thinking | Stronger reasoning, higher price |
| claude-opus-4-7 / 4-8 | Previous flagship, worth comparing |
| claude-fable-5 family | Newest flagship tier |
Pricing on the pricing page.