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/messages

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": "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-keyrequired
    Your key on this site; Authorization: Bearer also works
  • anthropic-version
    API version. Without it this site sends 2023-06-01. From 2026-10-21 on, temperature, top_p and top_k are rejected
  • anthropic-beta
    Turns 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:

ValueMeaning
end_turnFinished normally
max_tokensHit max_tokens or the model limit; the answer is cut off
stop_sequenceHit a string from stop_sequences
tool_useWants to call a tool
pause_turnA long-running turn was paused. Send this response back unchanged as the assistant message in a new request and the model carries on
refusalThe model declined to answer; see stop_details
model_context_window_exceededThe 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:

FieldMeaning
cache_creation_input_tokensTokens written to the cache, billed at the cache write price (a bit above normal input)
cache_read_input_tokensTokens served from the cache, billed at the cache read price (well below normal input)
input_tokensRegular, uncached input
cache_creationCache 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

MessagesChat Completions
Addresshttps://tokenfunc.com/v1/messageshttps://tokenfunc.com/v1/chat/completions
System promptTop-level systemA system message in messages
max_tokensRequiredOptional
Prompt cachingcache_control supportedNo manual caching for Claude
Best forClaude and Claude Code-style toolsEvery model and OpenAI ecosystem tools