Skip to content

Anthropic Messages API

POST https://api.bve.me/v1/messages
GET https://api.bve.me/v1/messages/:message_id

Requires Authorization: Bearer sk-bve-YOUR_KEY.

BVE Gateway natively passes through the Anthropic Messages API. The response is in Anthropic format (not OpenAI format). This is useful when you want to use Anthropic-specific features like extended thinking or when targeting Claude models directly.

{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Hello, Claude!" }
]
}
FieldTypeRequiredDescription
modelstringYesClaude model ID (e.g. claude-sonnet-4-6)
messagesarrayYesArray of {role, content} objects
max_tokensintegerYesMaximum tokens to generate
systemstring or arrayNoSystem prompt. May be a plain string or an Anthropic content-block array.
temperaturenumberNoSampling temperature 0–1
top_pnumberNoNucleus sampling
top_kintegerNoTop-k sampling
streambooleanNoEnable SSE streaming
stop_sequencesarrayNoStop sequences
toolsarrayNoTool definitions
tool_choiceobjectNoTool selection strategy
thinkingobjectNoExtended thinking configuration
betasarrayNoAnthropic beta feature flags — e.g. ["extended-thinking-2025-01-16"]; forwarded to upstream as the anthropic-beta header
metadataobjectNoRequest metadata
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Hello! How can I help you today?"
}
],
"model": "claude-sonnet-4-6-20250929",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 10,
"output_tokens": 12
}
}
Terminal window
curl https://api.bve.me/v1/messages \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Hello, Claude!" }
]
}'
Terminal window
curl https://api.bve.me/v1/messages \
-H "Authorization: Bearer sk-bve-YOUR_KEY" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{ "role": "user", "content": "Count to 5." }],
"stream": true
}'

Streaming returns Anthropic SSE events: message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop.

BVE Gateway accepts x-api-key as an alternative to Authorization: Bearer, so the standard Anthropic SDK configuration works without modification.

import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({
apiKey: 'sk-bve-YOUR_KEY',
baseURL: 'https://api.bve.me',
});
const message = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello, Claude!' }],
});
console.log(message.content[0].text);
const stream = await client.messages.stream({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Count to 5.' }],
});
for await (const chunk of stream) {
if (chunk.type === 'content_block_delta' && chunk.delta.type === 'text_delta') {
process.stdout.write(chunk.delta.text);
}
}
ModelID
Claude Sonnet 4.6claude-sonnet-4-6
Claude Sonnet 4.5claude-sonnet-4-5
Claude Sonnet 4claude-sonnet-4
Claude Haiku 4.5claude-haiku-4-5
Claude Haiku 4claude-haiku-4
Claude 3.7 Sonnetclaude-3-7-sonnet
Claude 3.5 Sonnetclaude-3-5-sonnet
Claude 3.5 Haikuclaude-3-5-haiku

See the Models page for the full list.

The gateway validates required fields before forwarding to the Anthropic upstream. Note that max_tokens is required by the Anthropic API — unlike /v1/chat/completions which treats it as optional.

Missing / invalidCodeparam
model absentmissing_required_parameter"model"
model not a stringinvalid_type"model"
max_tokens absentmissing_required_parameter"max_tokens"
max_tokens not a numberinvalid_type"max_tokens"
max_tokens not a positive integerinvalid_value"max_tokens"
messages absentmissing_required_parameter"messages"
messages not an arrayinvalid_type"messages"
messages is emptyinvalid_value"messages"

Each entry in messages[] is validated before forwarding to the upstream. Only user and assistant roles are accepted — unlike /v1/chat/completions, which also accepts system, tool, and developer.

ConditionCodeparam
messages[N] not an objectinvalid_type"messages[N]"
messages[N].role absentmissing_required_parameter"messages[N].role"
messages[N].role not a stringinvalid_type"messages[N].role"
messages[N].role not user or assistantinvalid_value"messages[N].role"
messages[0].role is not userinvalid_value"messages[0].role"
Two consecutive messages share the same roleinvalid_value"messages[N].role"
messages[N].content not a string or arrayinvalid_type"messages[N].content"

When content is an array (multimodal format), each block is validated — see Content block validation below.

The Anthropic Messages API uses a different content block format from the OpenAI image_url format used in /v1/chat/completions. When messages[N].content is an array, the gateway validates each block according to its type field.

All blocks:

ConditionCodeparam
Block not an objectinvalid_type"messages[N].content[M]"
type absentmissing_required_parameter"messages[N].content[M].type"
type not a stringinvalid_type"messages[N].content[M].type"

Unknown block types (thinking, redacted_thinking, and any future Anthropic types) pass through without validation.

type: "text" blocks:

ConditionCodeparam
text absentmissing_required_parameter"messages[N].content[M].text"
text not a stringinvalid_type"messages[N].content[M].text"

type: "image" blocks (base64):

{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "/9j/4AAQ..."
}
}
ConditionCodeparam
source absentmissing_required_parameter"messages[N].content[M].source"
source not an objectinvalid_type"messages[N].content[M].source"
source.type absentmissing_required_parameter"messages[N].content[M].source.type"
source.type not "base64" or "url"invalid_value"messages[N].content[M].source.type"
source.media_type absent (base64)missing_required_parameter"messages[N].content[M].source.media_type"
source.media_type not one of image/jpeg, image/png, image/gif, image/webpinvalid_value"messages[N].content[M].source.media_type"
source.data absent or empty (base64)missing_required_parameter / invalid_value"messages[N].content[M].source.data"

type: "image" blocks (URL):

{
"type": "image",
"source": { "type": "url", "url": "https://example.com/photo.jpg" }
}
ConditionCodeparam
source.url absent or empty (url)missing_required_parameter / invalid_value"messages[N].content[M].source.url"

type: "tool_use" blocks:

{
"type": "tool_use",
"id": "toolu_01abc",
"name": "get_weather",
"input": { "location": "London" }
}
ConditionCodeparam
id absent or emptymissing_required_parameter / invalid_value"messages[N].content[M].id"
name absent or emptymissing_required_parameter / invalid_value"messages[N].content[M].name"
input absentmissing_required_parameter"messages[N].content[M].input"
input not an object (including arrays)invalid_type"messages[N].content[M].input"

type: "tool_result" blocks:

{
"type": "tool_result",
"tool_use_id": "toolu_01abc",
"content": "The weather in London is 15°C and cloudy."
}
ConditionCodeparam
tool_use_id absent or emptymissing_required_parameter / invalid_value"messages[N].content[M].tool_use_id"
content not a string or array (when present)invalid_type"messages[N].content[M].content"

type: "document" blocks:

ConditionCodeparam
source absentmissing_required_parameter"messages[N].content[M].source"
source not an objectinvalid_type"messages[N].content[M].source"
source.type absentmissing_required_parameter"messages[N].content[M].source.type"
source.type not "base64", "url", or "text"invalid_value"messages[N].content[M].source.type"
source.url absent or empty (url type)missing_required_parameter / invalid_value"messages[N].content[M].source.url"
source.data absent or empty (base64 or text type)missing_required_parameter / invalid_value"messages[N].content[M].source.data"

Anthropic tools use a different shape from OpenAI function tools. The required field is input_schema (not parameters), and tools are top-level objects without the type: "function" wrapper.

{
"tools": [
{
"name": "get_weather",
"description": "Get the current weather for a city",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
]
}
ConditionCodeparam
tools not an arrayinvalid_type"tools"
tools empty arrayinvalid_value"tools"
tools[N] not an objectinvalid_type"tools[N]"
tools[N].name absent or emptymissing_required_parameter / invalid_value"tools[N].name"
tools[N].name fails [a-zA-Z0-9_-]{1,64} patterninvalid_value"tools[N].name"
tools[N].description not a string (when present)invalid_type"tools[N].description"
tools[N].input_schema absentmissing_required_parameter"tools[N].input_schema"
tools[N].input_schema not an objectinvalid_type"tools[N].input_schema"

Anthropic tool_choice must be an object — unlike OpenAI, which also accepts bare strings ("none", "auto", "required").

ConditionCodeparam
tool_choice not an objectinvalid_type"tool_choice"
tool_choice.type absentmissing_required_parameter"tool_choice.type"
tool_choice.type not "auto", "any", or "tool"invalid_value"tool_choice.type"
tool_choice.name absent when type is "tool"missing_required_parameter"tool_choice.name"

Valid tool_choice shapes:

{ "type": "auto" }
{ "type": "any" }
{ "type": "tool", "name": "get_weather" }
ParameterInvalid conditionCode
temperatureNot a numberinvalid_type
temperatureOutside [0, 1] (note: Anthropic range is [0, 1], not [0, 2])invalid_value
top_pNot a numberinvalid_type
top_pOutside [0, 1]invalid_value
top_kNot a positive integerinvalid_value
streamNot a booleaninvalid_type
systemNot a string or arrayinvalid_type
stop_sequencesNot an arrayinvalid_type
stop_sequencesArray with more than 4 elementsinvalid_value
stop_sequences[N]Not a stringinvalid_type
metadataNot an objectinvalid_type
metadata.user_idNot a stringinvalid_type
thinkingNot an objectinvalid_type
thinking.typeMissingmissing_required_parameter
thinking.typeNot "enabled" or "disabled"invalid_value
thinking.budget_tokensMissing when thinking.type is "enabled"missing_required_parameter
thinking.budget_tokensNot an integer ≥ 1024invalid_value

All 400 responses use the standard error envelope:

{
"error": {
"message": "max_tokens is required",
"type": "invalid_request_error",
"param": "max_tokens",
"code": "missing_required_parameter"
}
}

BVE Gateway adds the following headers to every authenticated response:

HeaderExampleDescription
X-Request-Id550e8400-…UUID for this request (generated per request)
X-BVE-Client-Idmy-trace-123Echo of the client-supplied X-Request-Id (when present and valid: alphanumeric + -_., ≤ 128 chars). Absent when not supplied or value failed validation.
X-BVE-Latency143Total gateway latency in milliseconds
X-BVE-Modelclaude-sonnet-4Model ID resolved for this request
X-BVE-Key-Nameprod-keyName of the API key used for this request (redacted if it matches a provider credential pattern)

The full X-RateLimit-* header set (RPM, RPD, monthly) is also included. See Rate Limits & Quotas for details and example output.

The Anthropic upstream also forwards a request-id response header which BVE Gateway passes through to the client.

  • The anthropic-version header is optional but recommended. BVE Gateway forwards it to the upstream provider.
  • Authentication: BVE Gateway accepts two auth formats on all endpoints:
    • Authorization: Bearer sk-bve-YOUR_KEY — standard BVE Gateway format (cURL, OpenAI SDK)
    • x-api-key: sk-bve-YOUR_KEY — Anthropic SDK default format (no SDK configuration required)
  • Extended thinking ("thinking": {"type": "enabled", "budget_tokens": N}) is forwarded if the model supports it.
  • The betas array maps directly to the anthropic-beta request header sent to Anthropic. Pass feature flag strings such as "extended-thinking-2025-01-16" or "interleaved-thinking-2025-05-14" to opt in to beta features. Unknown values are forwarded unchanged and may be silently ignored by Anthropic.