Anthropic Messages
The Anthropic-native chat endpoint: thinking, vision, tool use and prompt caching.
The chat endpoint compatible with the Anthropic Messages API, used by Claude Code and other tools built on the Anthropic SDK. For Claude, reasoning and prompt caching are most complete here.
POST https://tokenfunc.com/v1/messagescURL
curl https://tokenfunc.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-xxxxxxxxxxxxxxxx" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"system": "You are a helpful assistant.",
"messages": [{"role": "user", "content": "Hello!"}]
}'Python
# pip install anthropic
import anthropic
# base_url without /v1; the SDK appends /v1/messages
client = anthropic.Anthropic(base_url="https://tokenfunc.com", api_key="sk-xxxxxxxxxxxxxxxx")
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system="You are a helpful assistant.",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.content[0].text)Node.js
// npm install @anthropic-ai/sdk
import Anthropic from '@anthropic-ai/sdk'
// baseURL without /v1; the SDK appends /v1/messages
const client = new Anthropic({ baseURL: 'https://tokenfunc.com', apiKey: 'sk-xxxxxxxxxxxxxxxx' })
const resp = await client.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
system: 'You are a helpful assistant.',
messages: [{ role: 'user', content: 'Hello!' }],
})
console.log(resp.content[0].text)Authenticate with x-api-key or Authorization: Bearer, see Authentication.
Request headers
x-api-keyrequiredYour key on this site;Authorization: Beareralso worksanthropic-versionAPI version. Without it this site sends2023-06-01. From2026-10-21on,temperature,top_pandtop_kare rejectedanthropic-betaTurns on beta features and is passed upstream as is; separate several values with commas
Request parameters
Content block types include text, image, document (PDFs and other documents), tool_use and tool_result. With thinking on, send the returned thinking blocks back unchanged in multi-turn tool use. Add cache_control to a block to use prompt caching.
Do not send temperature, top_p or top_k: Claude 4.7 and later models return 400 for non-default values, and from anthropic-version: 2026-10-21 on, any model returns 400 when a request includes any of the three.
200
{
"id": "msg_01ABC",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4-5",
"content": [{ "type": "text", "text": "Hello! How can I help you today?" }],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 18,
"output_tokens": 12,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"cache_creation": {
"ephemeral_5m_input_tokens": 0,
"ephemeral_1h_input_tokens": 0
},
"service_tier": "standard"
}
}Response fields
Values of stop_reason:
| Value | Meaning |
|---|---|
end_turn | Finished normally |
max_tokens | Hit max_tokens or the model limit; the answer is cut off |
stop_sequence | Hit a string from stop_sequences |
tool_use | Wants to call a tool |
pause_turn | A long-running turn was paused. Send this response back unchanged as the assistant message in a new request and the model carries on |
refusal | The model declined to answer; see stop_details |
model_context_window_exceeded | The model's context window was exceeded |
Streaming
With stream: true events follow Anthropic's format: message_start → content_block_start → several content_block_delta → content_block_stop → message_delta (with the final usage) → message_stop.
cURL
curl https://tokenfunc.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-xxxxxxxxxxxxxxxx" \
-H "anthropic-version: 2023-06-01" \
-N \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"stream": true,
"messages": [{"role": "user", "content": "Write a four-line poem about autumn"}]
}'Python
with client.messages.stream(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Write a four-line poem about autumn"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)Node.js
const stream = client.messages.stream({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Write a four-line poem about autumn' }],
})
stream.on('text', (text) => process.stdout.write(text))
await stream.finalMessage()Examples
Thinking
Pass "thinking": {"type": "adaptive"} to turn on thinking and let the model decide how deeply to think based on the question. Tune how much it does with output_config.effort: low / medium / high / xhigh / max, or leave it out for the model's default. The returned content holds a thinking block first, then the text block. Claude 5 and later models hide the thinking text by default; add "display": "summarized" to see it.
cURL
curl https://tokenfunc.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-xxxxxxxxxxxxxxxx" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 16000,
"thinking": {"type": "adaptive", "display": "summarized"},
"output_config": {"effort": "high"},
"messages": [{"role": "user", "content": "Prove that the square root of 2 is irrational"}]
}'Python
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=16000,
thinking={"type": "adaptive", "display": "summarized"},
output_config={"effort": "high"},
messages=[{"role": "user", "content": "Prove that the square root of 2 is irrational"}],
)
for block in resp.content:
if block.type == "thinking":
print("Thinking:", block.thinking)
elif block.type == "text":
print("Answer:", block.text)The older form {"type": "enabled", "budget_tokens": 8000} only works on Claude 4.6 and earlier models (deprecated on 4.6); Claude 4.7 and later models return 400. The other way round, Claude 4.5 and earlier models do not support adaptive and need the older form, where budget_tokens is at least 1024 and below max_tokens.
Tools that only take a model name can append -thinking to the model name instead.
Vision
Images use the image content block, with either a Base64 or a URL source:
cURL
curl https://tokenfunc.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-xxxxxxxxxxxxxxxx" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [{
"role": "user",
"content": [
{"type": "image", "source": {"type": "url", "url": "https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"}},
{"type": "text", "text": "What is in this image?"}
]
}]
}'Python
import base64
with open("receipt.jpg", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": b64}},
{"type": "text", "text": "Extract the merchant, date and total from this receipt"},
],
}],
)
print(resp.content[0].text)Tool use
Define tools with name, description and input_schema. When the model wants a tool it returns a tool_use block (stop_reason is tool_use); run it and send the result back in a tool_result block.
cURL
curl https://tokenfunc.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-xxxxxxxxxxxxxxxx" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"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 today?"}]
}'Python
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 today?"}]
resp = client.messages.create(model="claude-sonnet-4-5", max_tokens=1024, tools=tools, messages=messages)
messages.append({"role": "assistant", "content": resp.content})
results = []
for block in resp.content:
if block.type == "tool_use":
weather = {"city": block.input["city"], "weather": "sunny", "temperature": 23}
results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(weather)})
messages.append({"role": "user", "content": results})
final = client.messages.create(model="claude-sonnet-4-5", max_tokens=1024, tools=tools, messages=messages)
print(final.content[0].text)Prompt caching
Many requests start the same way: the same system prompt, the same long document, the same tools. Add "cache_control": {"type": "ephemeral"} to the block where the cache should end; everything from the start up to that block is cached, later hits are billed at the lower cache price, and the first token arrives sooner.
If you would rather not pick the spot, put "cache_control": {"type": "ephemeral"} at the top level of the request. It caches up to the last cacheable block and moves forward as the conversation grows.
cURL
curl https://tokenfunc.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: sk-xxxxxxxxxxxxxxxx" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"system": [
{"type": "text", "text": "Answer from the employee handbook."},
{"type": "text", "text": "<the long handbook text>", "cache_control": {"type": "ephemeral"}}
],
"messages": [{"role": "user", "content": "How many vacation days do I get?"}]
}'Python
long_doc = open("handbook.md", encoding="utf-8").read()
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system=[
{"type": "text", "text": "Answer from the employee handbook."},
{"type": "text", "text": long_doc, "cache_control": {"type": "ephemeral"}},
],
messages=[{"role": "user", "content": "How many vacation days do I get?"}],
)
print(resp.usage)In the returned usage:
| Field | Meaning |
|---|---|
cache_creation_input_tokens | Tokens written to the cache, billed at the cache write price (a bit above normal input) |
cache_read_input_tokens | Tokens served from the cache, billed at the cache read price (well below normal input) |
input_tokens | Regular, uncached input |
cache_creation | Cache write tokens split into 5-minute and 1-hour entries |
- The cache matches a prefix that must be identical from the first character. Put fixed content first and keep timestamps or user names in the last user message.
- Short prefixes are not cached; the minimum is 512 to 4096 tokens depending on the model. Below it there is no error, the prompt is just not cached.
- The cache lasts about 5 minutes and every hit resets the timer; use
"cache_control": {"type": "ephemeral", "ttl": "1h"}for an hour at a higher write price. - Cache prices are on the Model Square; Usage Logs in the console shows the cache read and write tokens of every request.
Messages vs Chat Completions
| Messages | Chat Completions | |
|---|---|---|
| Address | https://tokenfunc.com/v1/messages | https://tokenfunc.com/v1/chat/completions |
| System prompt | Top-level system | A system message in messages |
max_tokens | Required | Optional |
| Prompt caching | cache_control supported | No manual caching for Claude |
| Best for | Claude and Claude Code-style tools | Every model and OpenAI ecosystem tools |