Skip to main content

Messages API

The Messages API provides an Anthropic-compatible endpoint for generating conversational responses. This API follows the Anthropic Messages API specification, enabling seamless integration with tools and clients built for the Anthropic ecosystem — including Claude Code, Cursor, and other agentic coding tools.

Overview

The Messages API exposes two endpoints:

  • /v1/messages: Send messages and receive model-generated responses, with support for multi-turn conversations, tool use, streaming, and structured output.
  • /v1/messages/count_tokens: Count the number of tokens in a message before sending it, helping you manage context windows and costs.

Messages Endpoint

Endpoint

POST https://api.inference.nebul.io/v1/messages

Headers

HeaderRequiredDescription
x-api-keyYesYour API key (or use Authorization: Bearer <key>).
anthropic-versionYesAPI version. Use 2023-06-01.
content-typeYesMust be application/json.

Parameters

ParameterTypeRequiredDescription
modelStringYesThe model ID to use (e.g., Qwen/Qwen3-30B-A3B-Instruct-2507).
messagesArrayYesArray of message objects with role and content. Roles: user, assistant.
max_tokensIntegerYesMaximum number of tokens to generate in the response.
systemString or ArrayNoSystem prompt. Can be a string or an array of content blocks.
temperatureNumberNoSampling temperature (0.0–1.0). Defaults to 1.0.
top_pNumberNoNucleus sampling parameter (0.0–1.0). Defaults to 1.0.
top_kIntegerNoOnly sample from the top K options for each subsequent token.
stop_sequencesArrayNoArray of strings that will cause the model to stop generating.
streamBooleanNoWhether to stream responses. Defaults to false.
toolsArrayNoArray of tool definitions. See Function Calling & Tools.
tool_choiceObjectNoTool choice strategy (auto, any, tool, none). See Function Calling & Tools.
metadataObjectNoOptional metadata for the request.
output_configObjectNoOutput configuration including effort and format (JSON schema).

Code Examples

python
1234567891011121314151617181920212223
import requests
url = "https://api.inference.nebul.io/v1/messages"
headers = {
"x-api-key": "<YOUR_API_KEY>",
"anthropic-version": "2023-06-01",
"content-type": "application/json",
}
payload = {
"model": "Qwen/Qwen3-30B-A3B-Instruct-2507",
"max_tokens": 1024,
"system": "You are a helpful assistant.",
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
}
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
tip

API Key Security: Store your API key in environment variables rather than hardcoding it. API keys always start with sk- ("secret key-").

tip

Anthropic SDK: You can also use the official anthropic Python SDK by setting base_url to https://api.inference.nebul.io. See the Integrations section below.

Response Format

The API returns a JSON object with the following structure:

json
1234567891011121314151617181920212223
{
"id": "chatcmpl-77126f46ae0ae1ec76857fb174d6747a",
"type": "message",
"role": "assistant",
"content": [
{
"type": "thinking",
"thinking": "The user is asking about the capital of France...",
"signature": "8a93d07790aa4ff582dbfd045ec24f10"
},
{
"type": "text",
"text": "The capital of France is Paris."
}
],
"model": "Qwen/Qwen3-30B-A3B-Instruct-2507",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 25,
"output_tokens": 19
}
}

Response Fields

FieldTypeDescription
idStringUnique identifier for the message.
typeStringObject type, always "message".
roleStringRole of the response, always "assistant".
contentArrayArray of content blocks (text, tool_use, thinking, etc.).
modelStringModel ID used for the response.
stop_reasonStringReason for completion (end_turn, max_tokens, stop_sequence, tool_use).
stop_sequenceStringThe stop sequence that triggered completion, if applicable.
usageObjectToken usage statistics.
usage.input_tokensIntegerNumber of tokens in the input.
usage.output_tokensIntegerNumber of tokens in the output.

Message Roles

The messages array supports the following roles:

  • user: Represents the user's input or question. The first message must have the user role.
  • assistant: Represents the model's previous responses. Used for multi-turn conversations.

The system parameter is used separately (not as a message role) to set the assistant's behavior and context.

Content Block Types

Content in messages can be a simple string or an array of structured content blocks:

Text Content

json
1234
{
"role": "user",
"content": "Hello, how are you?"
}

Or as a structured block:

json
123456
{
"role": "user",
"content": [
{ "type": "text", "text": "Describe this image." }
]
}

Image Content

json
1234567891011121314
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "<base64-encoded-image-data>"
}
},
{ "type": "text", "text": "What is in this image?" }
]
}

Tool Use Content

When the model requests a tool call, the response contains a tool_use content block:

json
123456
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "location": "San Francisco, CA" }
}

Tool results are sent back in a user message with a tool_result content block:

json
12345678910
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "72°F, sunny"
}
]
}

Thinking Content

Models with extended thinking enabled (e.g., reasoning models) return a thinking content block before the text response:

json
12345
{
"type": "thinking",
"thinking": "Let me reason through this step by step...",
"signature": "8a93d07790aa4ff582dbfd045ec24f10"
}

The signature field is an opaque verification hash. You can safely ignore thinking blocks if you only need the final text output.

Multi-turn Conversations

Include previous assistant responses in the messages array to maintain conversation context:

python
123456789101112131415161718192021222324252627282930
import requests
url = "https://api.inference.nebul.io/v1/messages"
headers = {
"x-api-key": "<YOUR_API_KEY>",
"anthropic-version": "2023-06-01",
"content-type": "application/json",
}
payload = {
"model": "Qwen/Qwen3-30B-A3B-Instruct-2507",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
},
{
"role": "assistant",
"content": "The capital of France is Paris."
},
{
"role": "user",
"content": "What is its population?"
}
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())

Streaming Responses

Enable streaming by setting stream: true. The response will be sent as a series of Server-Sent Events (SSE) with event types: message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop, and ping.

Content block delta types include text_delta for text output and thinking_delta for reasoning model thinking output.

python
123456789101112131415161718192021222324252627282930313233343536373839
import requests
import json
url = "https://api.inference.nebul.io/v1/messages"
headers = {
"x-api-key": "<YOUR_API_KEY>",
"anthropic-version": "2023-06-01",
"content-type": "application/json",
}
payload = {
"model": "Qwen/Qwen3-30B-A3B-Instruct-2507",
"max_tokens": 1024,
"stream": True,
"messages": [
{
"role": "user",
"content": "Tell me a short story."
}
]
}
response = requests.post(url, headers=headers, json=payload, stream=True)
for line in response.iter_lines():
if line:
line_text = line.decode('utf-8')
if line_text.startswith('data: '):
data = line_text[6:]
try:
event = json.loads(data)
if event.get('type') == 'content_block_delta':
delta = event.get('delta', {})
if delta.get('type') == 'text_delta':
print(delta.get('text', ''), end='', flush=True)
elif delta.get('type') == 'thinking_delta':
pass # Skip thinking output
except json.JSONDecodeError:
pass

Count Tokens Endpoint

The count tokens endpoint lets you determine the number of tokens in a message before sending it, helping you manage context windows, costs, and prompt optimization.

Endpoint

POST https://api.inference.nebul.io/v1/messages/count_tokens

Headers

Same as the Messages endpoint.

Parameters

ParameterTypeRequiredDescription
modelStringYesThe model ID to use for token counting.
messagesArrayYesArray of message objects (same format as the Messages endpoint).
systemString or ArrayNoSystem prompt to include in the count.
toolsArrayNoTool definitions to include in the count.
tool_choiceObjectNoTool choice configuration to include in the count.

Code Examples

python
12345678910111213141516171819202122
import requests
url = "https://api.inference.nebul.io/v1/messages/count_tokens"
headers = {
"x-api-key": "<YOUR_API_KEY>",
"anthropic-version": "2023-06-01",
"content-type": "application/json",
}
payload = {
"model": "Qwen/Qwen3-30B-A3B-Instruct-2507",
"system": "You are a helpful assistant.",
"messages": [
{
"role": "user",
"content": "Hello, how are you?"
}
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())

Response Format

json
123456
{
"input_tokens": 11,
"context_management": {
"original_input_tokens": 11
}
}
FieldTypeDescription
input_tokensIntegerEstimated number of tokens in the input messages, system prompt, and tools.
context_managementObjectContext management metadata.
context_management.original_input_tokensIntegerToken count before any context window adjustments.
info

Token counts are estimates. The actual number of tokens used when creating a message may differ by a small amount.

Count Tokens with Tools

Token counting supports tool definitions, so you can measure the full prompt size before making a request:

python
12345678910111213141516171819202122232425262728293031323334353637
import requests
url = "https://api.inference.nebul.io/v1/messages/count_tokens"
headers = {
"x-api-key": "<YOUR_API_KEY>",
"anthropic-version": "2023-06-01",
"content-type": "application/json",
}
payload = {
"model": "Qwen/Qwen3-30B-A3B-Instruct-2507",
"tools": [
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
}
},
"required": ["location"]
}
}
],
"messages": [
{
"role": "user",
"content": "What is the weather in San Francisco?"
}
]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())

Using with the Anthropic SDK

You can use the official Anthropic Python or TypeScript SDKs by pointing base_url to the Nebul Inference API:

python
12345678910111213141516
import anthropic
client = anthropic.Anthropic(
api_key="<YOUR_API_KEY>",
base_url="https://api.inference.nebul.io",
)
message = client.messages.create(
model="Qwen/Qwen3-30B-A3B-Instruct-2507",
max_tokens=1024,
messages=[
{"role": "user", "content": "What is the capital of France?"}
],
)
print(message.content[0].text)

Count Tokens with the SDK

python
123456789101112131415
import anthropic
client = anthropic.Anthropic(
api_key="<YOUR_API_KEY>",
base_url="https://api.inference.nebul.io",
)
token_count = client.messages.count_tokens(
model="Qwen/Qwen3-30B-A3B-Instruct-2507",
messages=[
{"role": "user", "content": "Hello, how are you?"}
],
)
print(token_count)

Using with Claude Code

Claude Code can be configured to use the Nebul Inference API as its backend by setting environment variables:

bash
1234567
ANTHROPIC_BASE_URL=https://api.inference.nebul.io \
ANTHROPIC_API_KEY=<YOUR_API_KEY> \
ANTHROPIC_AUTH_TOKEN=<YOUR_API_KEY> \
ANTHROPIC_DEFAULT_OPUS_MODEL=Qwen/Qwen3-30B-A3B-Instruct-2507 \
ANTHROPIC_DEFAULT_SONNET_MODEL=Qwen/Qwen3-30B-A3B-Instruct-2507 \
ANTHROPIC_DEFAULT_HAIKU_MODEL=Qwen/Qwen3-30B-A3B-Instruct-2507 \
claude

For detailed setup instructions, see the Claude Code integration guide.

Model Specifications

In principle, the Messages API supports the same LLM models as the Chat Completions API.

Key Differences from Chat Completions

AspectChat Completions (/v1/chat/completions)Messages (/v1/messages)
CompatibilityOpenAI APIAnthropic API
System promptsystem role in messages arraySeparate system parameter
Required paramsmodel, messagesmodel, messages, max_tokens
Stop sequencesstop parameterstop_sequences parameter
Tool choice typesauto, required, none, specific toolauto, any, tool, none
AuthenticationAuthorization: Bearerx-api-key header
Token countingNot available/v1/messages/count_tokens
Thinking blocksNot supportedthinking content blocks for reasoning models

Best Practices

  1. Always set max_tokens: Unlike Chat Completions, max_tokens is required in the Messages API. Set an appropriate value for your use case.
  2. Use system for instructions: Pass system instructions via the system parameter rather than as a message role.
  3. Token counting: Use /v1/messages/count_tokens to preview prompt sizes and manage context windows before making requests.
  4. Streaming: Use streaming for long responses to improve perceived latency.
  5. Error Handling: Always check response status codes and handle errors appropriately.