Skip to content
Anthropic Format

Anthropic Format

POST /v1/messages takes a request in the Anthropic Messages format and answers in the same format, streamed or not. Use it with the Anthropic SDK and with tools built on it, such as Claude Code: set the base URL, use your Shannon API key and name a Shannon model id.

POST https://api.shannon-ai.com/v1/messages

Point the Anthropic SDK at the Shannon base URL. The SDK adds /v1/messages and sends your key in the x-api-key header.

import anthropic

client = anthropic.Anthropic(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com",
)

message = client.messages.create(
    model="shannon-3",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
)

for block in message.content:
    if block.type == "text":
        print(block.text)

The reply is a message whose content is a list of blocks. Find the blocks by their type: when the model reasoned, the first block is thinking and the text comes after it.

200 JSON
{
  "id": "msg_5f0c1e7a9b3d4c62a8e1f07d2b46c9a3",
  "type": "message",
  "role": "assistant",
  "model": "shannon-3",
  "content": [
    {
      "type": "thinking",
      "thinking": "The user wants a greeting in one sentence. Keep it short and friendly."
    },
    {
      "type": "text",
      "text": "Hello, it is good to meet you."
    }
  ],
  "stop_reason": "end_turn",
  "usage": {
    "input_tokens": 1184,
    "output_tokens": 46
  }
}

Headers

Header Value Description
x-api-key YOUR_API_KEY Your API key. Authorization: Bearer YOUR_API_KEY is accepted in its place.
content-type application/json Required.
anthropic-version 2023-06-01 Accepted, not required. The Anthropic SDK sends 2023-06-01; the reply has the same format whatever the value.
anthropic-beta Accepted, not required.
x-request-id Optional. Your own id for the request. It comes back unchanged on the reply; without it the reply carries an id of 12 hexadecimal characters.

Request fields

Only messages is required. The Applied by column names the models on which a field changes the reply. The hosted open-weight models are the twelve ids of the model list; the Shannon 3 family is shannon-3, shannon-3-pro, shannon-3.1 and shannon-3.1-pro. Models & pricing

Field Type Default Description Applied by
model string shannon-1.6-lite A Shannon model id from the model list. Send it with every request. Matching is not case-sensitive. Any other name returns 400 unknown model, so a tool that sends its own model names has to be set to a Shannon id. All models
messages array Required. The conversation, oldest message first, with the roles user and assistant. See Messages and content blocks below. All models
system string | array Instructions for the model: a string, or an array of text blocks. See System prompt below. Hosted open-weight models, shannon-1.6-*, shannon-2-*, shannon-coder-1
max_tokens integer 4096 Upper limit of the reply, in tokens. Optional on this endpoint; the Anthropic SDK always sends it. A value outside 1 to 65,536 is moved into that range. It is also the amount set aside from your balance while the request runs. Hosted open-weight models, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1
stream boolean false true sends the reply as server-sent events. See Streaming below. All models
temperature number Sampling temperature. On the hosted open-weight models values are kept between 0 and 2. Hosted open-weight models, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1
top_p number Nucleus sampling. Hosted open-weight models
stop_sequences array Up to 4 strings. The answer ends before the first one that appears. stop_reason stays end_turn. Hosted open-weight models
tools array The tools the model may call, each as {"name", "description", "input_schema"}. See Tools below. All models
tool_choice object {"type": "auto"} lets the model decide, {"type": "any"} makes it call a tool, {"type": "tool", "name": "…"} makes it call that tool. Hosted open-weight models
thinking object How much the model reasons before it answers. See Thinking below. Hosted open-weight models
web_search boolean false A Shannon addition to the format: true lets the model search the web before it answers. shannon-1.6-*, shannon-2-*, Shannon 3 family

Without max_tokens the value is 4,096, and 65,536 on shannon-coder-1.

metadata, top_k, service_tier and cache_control markers on blocks are accepted so that existing client code runs unchanged. They do not change the reply; prompt caching on the hosted open-weight models is automatic.

A JSON answer to a schema is asked with response_format on Chat Completions and with text.format on Responses. Structured Outputs

A request with a system prompt and thinking

This request sends a system prompt, a thinking budget, a temperature and a stop sequence. It uses a hosted open-weight model, which applies all of them.

import anthropic

client = anthropic.Anthropic(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com",
)

message = client.messages.create(
    model="DeepSeek-V4-Flash-0731-W4A16-AUTOROUND-REAP",
    max_tokens=4096,
    system="You are a physics teacher. Answer in two sentences.",
    thinking={"type": "enabled", "budget_tokens": 2048},
    temperature=0.3,
    stop_sequences=["\n\n"],
    messages=[{"role": "user", "content": "Why is the sky blue?"}],
)

for block in message.content:
    if block.type == "thinking":
        print("reasoning:", block.thinking)
    elif block.type == "text":
        print("answer:", block.text)
print(message.usage)

On the hosted open-weight models usage reports the tokens read from the prompt cache apart from the other input tokens.

200 JSON
{
  "usage": {
    "input_tokens": 31,
    "cache_read_input_tokens": 0,
    "cache_creation_input_tokens": 0,
    "output_tokens": 62
  }
}

System prompt

system is a string, or an array of text blocks in the form [{"type": "text", "text": "…"}]. The texts of the blocks are joined with a line break.

It is applied by the hosted open-weight models, shannon-1.6-lite, shannon-1.6-pro, shannon-2-lite, shannon-2-pro and shannon-coder-1. With a Shannon 3 family id, put instructions that must hold into the user message.

Messages and content blocks

A message has a role, user or assistant, and a content. content is a string, or an array of blocks.

Block Description Available on
{"type": "text", "text": "…"} Text. All models
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "…"}}
{"type": "image", "source": {"type": "url", "url": "…"}}
An image in a user message, as base64 or by URL. Shannon 3 family, shannon-1.6-lite, shannon-1.6-pro, and the hosted open-weight models that list image input
{"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": "…"}} A document (PDF, Word, PowerPoint or Excel) in a user message, as base64 or by URL. Shannon 3 family
{"type": "tool_use", "id": "toolu_…", "name": "…", "input": {…}} In an assistant message: a tool call the model made in an earlier reply. All models
{"type": "tool_result", "tool_use_id": "toolu_…", "content": "…"} In a user message: the result of that call. content is a string, or a list of text blocks. All models

You can send the content of an earlier reply back as it is: text and tool_use blocks are read, thinking blocks are skipped.

On the Shannon tiers images and documents are read from the last user message. Sizes and limits have their own page. Images and files

Thinking

The hosted open-weight models map thinking to a reasoning effort. Without the field the effort is high.

Form budget_tokens Effect
{"type": "disabled"} No reasoning. The reply has no thinking block.
{"type": "enabled", "budget_tokens": 2048} ≤ 2,048 Low effort.
{"type": "enabled", "budget_tokens": 8192} ≤ 8,192 Medium effort.
{"type": "enabled", "budget_tokens": 16000} > 8,192 High effort.

A model that reasons returns its reasoning as a block {"type": "thinking", "thinking": "…"} in front of the text. The Shannon tiers that reason send it as well.

The efforts are described on their own page. Reasoning effort

Tools

A tool is a function your code runs. Describe each one with a name, a description and an input_schema. When the model wants a tool, the reply holds a tool_use block and stop_reason is tool_use.

A tool definition in tools:

tools
[
  {
    "name": "get_weather",
    "description": "Current weather for a city.",
    "input_schema": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string"
        }
      },
      "required": [
        "city"
      ]
    }
  }
]

The content of a reply that calls it:

200 content
[
  {
    "type": "text",
    "text": ""
  },
  {
    "type": "tool_use",
    "id": "toolu_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
    "name": "get_weather",
    "input": {
      "city": "Paris"
    }
  }
]

Run the tool, then send the request again with the call and a tool_result block added to messages:

messages
[
  {
    "role": "user",
    "content": "What is the weather in Paris?"
  },
  {
    "role": "assistant",
    "content": [
      {
        "type": "tool_use",
        "id": "toolu_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
        "name": "get_weather",
        "input": {
          "city": "Paris"
        }
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
        "content": "{\"temperature_c\": 18, \"sky\": \"cloudy\"}"
      }
    ]
  }
]

tool_choice

Form Effect Applied by
{"type": "auto"} The model decides whether to call a tool. This is the default. All models
{"type": "any"} The model must call one of the tools. Hosted open-weight models
{"type": "tool", "name": "…"} The model must call the named tool. Hosted open-weight models

Only tools with an input_schema are read. Web search is not a tool on this endpoint: set the web_search field.

The tool loop is described in full on its own page. Function Calling

The reply

Field Type Description Sent by
id string msg_ followed by 32 hexadecimal characters. All models
type string Always message. All models
role string Always assistant. All models
model string The canonical id of the model that answered. All models
content array The blocks of the reply, in this order: thinking when the model reasoned, text, then one tool_use per tool call. All models
stop_reason string Why the reply ended. See the values below. All models
usage object input_tokens and output_tokens. All models
sources array Only on a request with web_search: true whose search found something: the results the model was given, each with index, title and url. [1] in the answer is the entry with index 1. In a stream it comes with the message_delta event. shannon-1.6-*, shannon-2-*, Shannon 3 family
usage.cache_read_input_tokens integer cache_read_input_tokens is the part of the prompt read from cache; input_tokens is the rest. cache_creation_input_tokens is always 0. Hosted open-weight models

Reply blocks

Block Description
{"type": "thinking", "thinking": "…"} The model's reasoning. There is no signature member.
{"type": "text", "text": "…"} The answer text. It can be empty when the reply is a tool call.
{"type": "tool_use", "id": "toolu_…", "name": "…", "input": {…}} A tool call. input is a JSON object. Send the result back with the same id.

stop_reason

stop_reason Description Sent by
end_turn The model finished its answer, or one of your stop_sequences appeared. All models
tool_use The reply holds one or more tool calls. All models
max_tokens The reply was cut at the output limit. Shannon 3 family, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1, in streams

Streaming

Set stream to true for server-sent events. Each frame has an event: line with the name of the event and a data: line with a JSON object that repeats the name in type. The stream helpers of the Anthropic SDK read these events for you:

import anthropic

client = anthropic.Anthropic(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com",
)

with client.messages.stream(
    model="shannon-3",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Explain server-sent events in three sentences."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
    message = stream.get_final_message()

print()
print(message.stop_reason, message.usage)

The events, in the order they arrive:

Event Data When
message_start message Always first. message holds the id, the role, the model, an empty content and a usage that starts at zero.
content_block_start index, content_block A block opens. content_block is an empty text or thinking block, or a tool_use block with its id and name.
content_block_delta index, delta {"type": "thinking_delta", "thinking"} A piece of the reasoning, for a thinking block.
content_block_delta index, delta {"type": "text_delta", "text"} A piece of the answer, for a text block.
content_block_delta index, delta {"type": "input_json_delta", "partial_json"} The arguments of a tool call, complete in one delta, for a tool_use block.
content_block_stop index The block with this index is complete.
message_delta delta {stop_reason, stop_sequence}, usage The end of the reply: delta.stop_reason, and the usage of the request.
message_stop Always last.

Blocks are numbered by index in the order they open, and one block is complete before the next one opens. Read each block by its type, not by its number: a stream can open an empty text block first, then the thinking block, and deliver the answer in a later text block.

200 text/event-stream
event: message_start
data: {"type":"message_start","message":{"id":"msg_5f0c…","type":"message","role":"assistant","model":"shannon-3","content":[],"usage":{"input_tokens":0,"output_tokens":0}}}

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

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

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

event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"thinking_delta","thinking":"Three sentences: what it is, how it is framed, why it is used."}}

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

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

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

event: content_block_delta
data: {"type":"content_block_delta","index":2,"delta":{"type":"text_delta","text":"Server-sent events let a server push text over one HTTP reply."}}

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

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":1190,"output_tokens":84}}

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

Keep-alive

Sent by Description
Shannon 3 family, shannon-1.6-lite, shannon-1.6-pro, shannon-coder-1 A ping event with the data {"type": "ping"}, every 15 seconds.
Hosted open-weight models A comment line : keepalive, every 15 seconds while the model works.

A stream that fails

A failure after the stream has started is an error event with the same body as an error reply: {"type": "error", "error": {"type", "message"}}. The closing events, message_delta and message_stop, still follow. Treat the reply as failed and send the request again.

Errors

Errors use the Anthropic error body: type is error, and the error object holds a type and a message. The table lists what this endpoint returns most often. The full list, with what to retry, has its own page. Error Handling

400 JSON
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "unknown model: no-such-model"
  }
}
Status Type Message When
401 authentication_error Missing authentication
Invalid API key
No API key was sent, or the key is unknown or revoked.
400 invalid_request_error unknown model: <id> model is not a Shannon model id.
400 invalid_request_error No user message provided Shannon tiers: the request has no user text and no tools.
400 invalid_request_error <id> does not accept image input An image was sent to a hosted open-weight model without image input.
422 invalid_request_error Failed to deserialize the JSON body into the target type: … messages is missing, or a field has the wrong JSON type.
429 rate_limit_error Quota exceeded. Upgrade your plan at shannon-ai.com/plan max_tokens is larger than what is left of your balance.
429 rate_limit_error Too many requests. Retry in <n>s. Flood protection: more than 120 requests in one minute on your account.
429 rate_limit_error Shannon Coder call quota reached. Upgrade your plan at shannon-ai.com/plan shannon-coder-1: your plan's Shannon Coder calls for the current 4-hour window are used.
500 server_error The model backend failed to answer. Please retry. The model did not produce a reply. Send the request again.
502 api_error The model backend failed to answer. Please retry. The model did not produce a reply. Send the request again.

Counting tokens

POST /v1/messages/count_tokens takes the same body as a message request (model, system, messages, tools) and returns {"input_tokens": n}: the number of input tokens the request would be charged. It counts for the hosted open-weight models, it is free and it is not counted by flood protection.

import anthropic

client = anthropic.Anthropic(
    api_key="YOUR_API_KEY",
    base_url="https://api.shannon-ai.com",
)

count = client.messages.count_tokens(
    model="DeepSeek-V4-Flash-0731-W4A16-AUTOROUND-REAP",
    system="You are a physics teacher. Answer in two sentences.",
    messages=[{"role": "user", "content": "Why is the sky blue?"}],
)

print(count.input_tokens)

Counting has its own page, with the plain-text endpoint and the limits. Token counting