Get API Key Get API
Docs

Claude Messages API

- Fully compatible with the native Anthropic Claude Messages protocol (`POST /v1/messages`)

  • Supports multi-turn conversations, streaming SSE, tool use, and extended thinking
  • Supports multimodal content including text and images
  • Responses are upstream passthrough with no {code, data} wrapper
Warning

Do not mix the two APIs: /v1/* is the inference API (this document — upstream passthrough, no wrapper); /api/* is the management API (balance/logs, etc., response shape {success, message, data}). If you see docs claiming /v1/messages returns {code, data}, this document takes precedence.

Request example

curl https://api.openveer.com/v1/messages \
  -H "x-api-key: $API_KEY" \
  -H "anthropic-version: 2025-10-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Hello, world"}
    ]
  }'

Response example

{
  "model": "claude-sonnet-4-6",
  "id": "msg_011CdfeHuC728oxqaLrRNbcB",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Hello! I'm Claude. Nice to meet you."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "stop_details": null,
  "usage": {
    "input_tokens": 12,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "cache_creation": {
      "ephemeral_5m_input_tokens": 0,
      "ephemeral_1h_input_tokens": 0
    },
    "output_tokens": 18,
    "service_tier": "standard",
    "inference_geo": "global"
  }
}

Authorizations

Authentication supports two methods — use either one:

x-api-key string

Anthropic-style API key header

Visit the API Key Management Page to get your API Key

x-api-key: YOUR_API_KEY
Authorization string

Bearer token authentication (alternative to x-api-key)

Authorization: Bearer YOUR_API_KEY
anthropic-version string

API version (optional — requests work without it)

Recommended for easier future migration to Anthropic's official endpoint:

Example: 2025-10-01

Body

model string

Model name

  • claude-opus-4-8 - Claude Opus 4.8 flagship model
  • claude-opus-4-7 - Claude Opus 4.7 flagship model
  • claude-opus-4-6 - Claude Opus 4.6 flagship model
  • claude-sonnet-4-6 - Claude Sonnet 4.6 balanced version
  • claude-opus-4-5-20251101 - Claude Opus 4.5 model
messages array
List of messages Array of messages for the model to generate the next response. Each message contains role and content fields. **💡 Quick fill (Try it area):** 1. Click "+ Add an item" to add a message 2. role input: user (user message) or assistant (AI response, for multi-turn) 3. content input: your message text
role string

Role type

  Options: user (user message), assistant (AI response, for multi-turn conversations and prefilling)

  Note: Claude API uses a separate system parameter for system prompts, not in messages
content string

Message content

  Text content of the message
**Single user message:**
[{"role": "user", "content": "Hello, Claude"}]
**Multi-turn conversation:**
[
  {"role": "user", "content": "Hello there."},
  {"role": "assistant", "content": "Hi, I'm Claude. How can I help you?"},
  {"role": "user", "content": "Can you explain LLMs in plain English?"}
]
**Prefilled assistant response:**
[
  {"role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun"},
  {"role": "assistant", "content": "The best answer is ("}
]
max_tokens integer

Maximum tokens to generate (required, same as Anthropic official)

Maximum number of tokens to generate before stopping. The model may stop before reaching this limit.

Different models have different maximum values. See model docs. Minimum: 1

thinking object

Extended thinking configuration

When enabled, the response content may include thinking blocks. Prefer the standard model name plus this parameter over platform-side -thinking model aliases, so you can migrate to the official endpoint without code changes.

If multi-turn conversations need to pass thinking blocks back, you must return the signature unchanged, or the upstream will reject the request.

system string | array

System prompt

System prompts set Claude's role, personality, goals, and instructions.

String format:

{
  "system": "You are a professional Python programming tutor"
}

Structured format:

{
  "system": [
    {
      "type": "text",
      "text": "You are a professional Python programming tutor"
    }
  ]
}
temperature number

Temperature parameter, range 0-1

Controls randomness of output:

  • Low values (e.g., 0.2): More deterministic, conservative
  • High values (e.g., 0.8): More random, creative

Default: 1.0

top_p number

Nucleus sampling parameter, range 0-1

Uses nucleus sampling. Recommend using either temperature or top_p, not both.

Default: 1.0

top_k integer

Top-K sampling

Sample from top K options only, removes "long tail" low probability responses.

Recommended for advanced use cases only.

stream boolean

Enable streaming

When true, uses Server-Sent Events (SSE) to stream responses.

Default: false

stop_sequences array

Stop sequences

Custom text sequences that cause the model to stop generating.

Maximum 4 sequences.

Example: ["\n\nHuman:", "\n\nAssistant:"]

metadata object

Metadata

Metadata object for the request.

Includes:

  • user_id: User identifier
tools array

Tool definitions

List of tools the model can use to complete tasks.

Function tool example:

{
  "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"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"],
            "description": "Temperature unit"
          }
        },
        "required": ["location"]
      }
    }
  ]
}

Supported tool types:

  • Custom function tools
  • Computer use tool (computer_20241022)
  • Text editor tool (text_editor_20241022)
  • Bash tool (bash_20241022)
tool_choice object

Tool choice strategy

Controls how the model uses tools:

  • {"type": "auto"}: Auto-decide (default)
  • {"type": "any"}: Must use a tool
  • {"type": "tool", "name": "tool_name"}: Use specific tool

Response

id string

Unique message identifier

Example: "msg_013Zva2CMHLNnXjNJJKqJ2EF"

type string

Object type

Always "message"

role string

Role

Always "assistant"

content array

Content blocks array

content is an array of blocks distinguished by type. A single response may contain multiple blocks (for example, with thinking enabled: a thinking block plus a text block).

text block:

{ "type": "text", "text": "OK" }

tool_use block:

{
  "type": "tool_use",
  "id": "toolu_01QgsazxKXSfQVj9Q1XxjYXo",
  "name": "get_weather",
  "input": { "city": "Beijing" },
  "caller": { "type": "direct" }
}

Note



caller is a newer upstream field not yet documented officially; ignore it when parsing.

thinking block (present when the request body includes the thinking parameter):

{
  "type": "thinking",
  "thinking": "Reasoning process text...",
  "signature": "<signature string, typically 500+ characters>"
}

Warning



When returning thinking blocks in multi-turn conversations, you must pass signature back unchanged, or the upstream will reject the request.

Warning



Do not assume content[0] is text. With thinking enabled, content[0] may be a thinking block. Iterate and filter:

text = "".join(b.text for b in resp.content if b.type == "text")

model string

Model that handled the request

Example: "claude-sonnet-4-6"

stop_reason string

Stop reason

Possible values:

  • end_turn: Natural completion
  • max_tokens: Reached maximum tokens
  • stop_sequence: Hit stop sequence
  • tool_use: Invoked a tool
stop_sequence string | null

Stop sequence triggered

The stop sequence that was generated, if any; otherwise null

stop_details object | null

Newer Anthropic field; null for typical requests

usage object
Token usage statistics (full structure for non-streaming)
input_tokens integer

Number of input tokens

output_tokens integer

Number of output tokens (already includes thinking tokens; do not double-count for billing)

cache_creation_input_tokens integer

Cache write tokens

cache_read_input_tokens integer

Cache hit tokens

cache_creation object

{ ephemeral_5m_input_tokens, ephemeral_1h_input_tokens }

service_tier string

e.g. "standard"

inference_geo string

e.g. "global"

output_tokens_details object

May appear when thinking is enabled: { thinking_tokens: int }

Usage Examples

Basic Conversation

import anthropic

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

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Explain quantum computing basics"}
    ]
)

print(message.content[0].text)

Multi-turn Conversation

messages = [
    {"role": "user", "content": "What is machine learning?"},
    {"role": "assistant", "content": "Machine learning is a branch of AI..."},
    {"role": "user", "content": "Can you give a practical example?"}
]

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=messages
)

Using System Prompts

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system="You are a senior Python developer expert in code review and optimization.",
    messages=[
        {"role": "user", "content": "How to optimize this code?\n\n[code]"}
    ]
)

Streaming Response

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Write a short essay about AI"}
    ]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

Tool Use

tools = [
    {
        "name": "get_stock_price",
        "description": "Get real-time stock price",
        "input_schema": {
            "type": "object",
            "properties": {
                "ticker": {
                    "type": "string",
                    "description": "Stock ticker symbol, e.g., AAPL"
                }
            },
            "required": ["ticker"]
        }
    }
]

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[
        {"role": "user", "content": "What's Tesla's stock price?"}
    ]
)

# Handle tool calls
if message.stop_reason == "tool_use":
    tool_use = next(block for block in message.content if block.type == "tool_use")
    print(f"Calling tool: {tool_use.name}")
    print(f"Arguments: {tool_use.input}")

Vision Understanding

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "url",
                        "url": "https://example.com/image.jpg"
                    }
                },
                {
                    "type": "text",
                    "text": "Describe this image"
                }
            ]
        }
    ]
)

Base64 Image

import base64

with open("image.jpg", "rb") as image_file:
    image_data = base64.b64encode(image_file.read()).decode("utf-8")

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/jpeg",
                        "data": image_data
                    }
                },
                {
                    "type": "text",
                    "text": "Analyze this image"
                }
            ]
        }
    ]
)

Best Practices

1. Prompt Engineering

Clear role definition:

system = """You are an experienced data scientist specializing in:
- Statistical analysis and data visualization
- Machine learning model development
- Python and R programming
Provide professional, accurate advice."""

Structured output:

message = "Please return the analysis results in JSON format with summary, key_findings, and recommendations fields."

2. Error Handling

from anthropic import APIError, RateLimitError

try:
    message = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello"}]
    )
except RateLimitError:
    print("Rate limit exceeded, please retry later")
except APIError as e:
    print(f"API error: {e}")

3. Token Optimization

# Use shorter prompts
messages = [
    {"role": "user", "content": "Summarize key points:\n\n[long text]"}
]

# Limit output length
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=500,  # Limit output
    messages=messages
)

4. Prefilling Responses

# Guide model to specific format
messages = [
    {"role": "user", "content": "List 5 Python best practices"},
    {"role": "assistant", "content": "Here are 5 Python best practices:\n\n1."}
]

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=messages
)

Streaming Response Handling

Python Streaming

import anthropic

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

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Write a Python decorator example"}
    ]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

JavaScript Streaming

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  apiKey: process.env.API_KEY,
  baseURL: 'https://api.openveer.com'
});

const stream = await client.messages.stream({
  model: 'claude-sonnet-4-6',
  max_tokens: 1024,
  messages: [
    { role: 'user', content: 'Write a React component example' }
  ]
});

for await (const chunk of stream) {
  if (chunk.type === 'content_block_delta' && 
      chunk.delta.type === 'text_delta') {
    process.stdout.write(chunk.delta.text);
  }
}

Platform Differences & Integration Notes

Unwrapped response

Successful POST /v1/messages responses return the Anthropic message object directly, with no {code, data} wrapper. This is required for 1:1 compatibility with official SDKs, Claude Code, Cline, and similar tools.

Error format (only real incompatibility with official)

{
  "error": {
    "code": "model_not_found",
    "message": "... (request id: ...)",
    "param": "",
    "type": "openveer_error"
  }
}

Compared with Anthropic official: missing top-level "type": "error"; error.type is always openveer_error, not semantic types like invalid_request_error.

Integration guidance: do not branch retries on error.type; use HTTP status + error.code instead:

Status Meaning Suggested action
400 Bad request parameters Do not retry; fix the request body
401 Invalid key Do not retry
402 Insufficient balance Do not retry; prompt top-up
429 Rate limited Retry with backoff
5xx Upstream/gateway error Retry with exponential backoff

When reporting issues, include the request id at the end of error.message and the response header x-oneapi-request-id.

Streaming SSE

Send "stream": true. Event sequence matches official:

message_start → content_block_start → ping → content_block_delta (multiple) → content_block_stop → message_delta → message_stop

⚠️ Stream vs non-stream usage differs: message_delta.usage typically has only 4 token fields and does not include cache_creation, service_tier, or inference_geo. Parse them separately or treat all as optional.

Unimplemented endpoint

POST /v1/messages/count_tokens is not implemented and returns 404. Official SDK client.messages.count_tokens() will fail. Estimate tokens locally, or read usage.input_tokens from responses.

Ignore unknown fields

This endpoint passes through upstream fields. Anthropic may add fields at any time (e.g. stop_details, inference_geo, caller, output_tokens_details). Do not use strict schemas:

  • Go: do not use DisallowUnknownFields()
  • Pydantic: do not use extra="forbid"
  • TypeScript / Zod: use .passthrough() instead of .strict()

Model name recommendation

Same-name models with a -thinking suffix are platform extension aliases. Prefer the standard model name without the suffix plus the request-body thinking parameter for easier migration to the official endpoint.

Other request-body fields match official: model, messages, max_tokens (required), system, temperature, top_p, top_k, stop_sequences, stream, tools, tool_choice, thinking, metadata. Semantics follow the Anthropic Messages API.

Important Notes

  1. API Key Security:
    * Store API keys in environment variables
    * Never hardcode keys in source code
    * Rotate keys regularly

  2. Rate Limiting:
    * Be aware of API rate limits
    * Implement retry mechanisms (by HTTP status code)
    * Use exponential backoff

  3. Token Management:
    * Monitor token usage (read usage)
    * Optimize prompt length
    * Use appropriate max_tokens values
    * With thinking enabled, output_tokens already includes thinking tokens — do not double-count for billing

  4. Model Selection:
    * Opus: Complex tasks, deep thinking required
    * Sonnet: Balanced performance and cost
    * Haiku: Fast response, simple tasks

  5. Content parsing:
    * Iterate content for type == "text"; do not hardcode content[0].text
    * If the model returns JSON wrapped in Markdown code fences, that is model output — not an API wrapper (see FAQ below)

  6. Content Filtering:
    * Validate user input
    * Filter sensitive information
    * Implement content moderation

FAQ

The response content text is a ```json ... ``` code fence — how do I strip it?

This is not an API structure issue. The text field holds the raw model-generated content: if the model decides you want JSON, it may wrap it in a Markdown code fence. The API does not and should not rewrite model output.

To get clean structured data, use one of these three approaches (recommended from highest to lowest reliability):

  1. Use tools to force structured output — most reliable; the input field is already a parsed object:
{
  "tools": [{
    "name": "emit_result",
    "input_schema": {
      "type": "object",
      "properties": { "answer": { "type": "string" } }
    }
  }],
  "tool_choice": { "type": "tool", "name": "emit_result" }
}
  1. Prefill the assistant message so the model continues from {:
{
  "messages": [
    { "role": "user", "content": "..." },
    { "role": "assistant", "content": "{" }
  ]
}
  1. In the system prompt, explicitly require “output JSON only, with no Markdown code fences.”

Do not rely on regex to strip code fences — parsing will break when the model occasionally omits the fence.

This documentation is built and hosted on Mintlify, a developer documentation platform.