- 完全兼容 Anthropic Claude Messages 原生协议(
POST /v1/messages)- 支持多轮对话、流式 SSE、工具调用、extended thinking
- 支持文本、图像等多模态内容
- 响应为上游原样透传,无
{code, data}外层包装
请求示例
bash
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": "你好,世界"}
]
}'
```python
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": "你好,世界"}
]
)
print(message.content)
```
```javascript
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({
apiKey: process.env.API_KEY,
baseURL: 'https://api.openveer.com'
});
const message = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [
{ role: 'user', content: '你好,世界' }
]
});
console.log(message.content);
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
url := "https://api.openveer.com/v1/messages"
payload := map[string]interface{}{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": []map[string]string{
{
"role": "user",
"content": "你好,世界",
},
},
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
req.Header.Set("x-api-key", os.Getenv("API_KEY"))
req.Header.Set("anthropic-version", "2025-10-01")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
```java
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
public class Main {
public static void main(String[] args) throws Exception {
String url = "https://api.openveer.com/v1/messages";
String apiKey = System.getenv("API_KEY");
String payload = """
{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好,世界"
}
]
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("x-api-key", apiKey)
.header("anthropic-version", "2025-10-01")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
```
```php
"claude-sonnet-4-6",
"max_tokens" => 1024,
"messages" => [
[
"role" => "user",
"content" => "你好,世界"
]
]
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"x-api-key: " . $apiKey,
"anthropic-version: 2025-10-01",
"Content-Type: application/json"
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
```
```ruby
require 'net/http'
require 'json'
require 'uri'
url = URI("https://api.openveer.com/v1/messages")
api_key = ENV['API_KEY']
payload = {
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [
{
role: "user",
content: "你好,世界"
}
]
}
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = api_key
request["anthropic-version"] = "2025-10-01"
request["Content-Type"] = "application/json"
request.body = payload.to_json
response = http.request(request)
puts response.body
```
```swift
import Foundation
let url = URL(string: "https://api.openveer.com/v1/messages")!
let apiKey = ProcessInfo.processInfo.environment["API_KEY"] ?? ""
let payload: [String: Any] = [
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
[
"role": "user",
"content": "你好,世界"
]
]
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue(apiKey, forHTTPHeaderField: "x-api-key")
request.setValue("2025-10-01", forHTTPHeaderField: "anthropic-version")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: payload)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Error: (error)")
return
}
if let data = data, let responseString = String(data: data, encoding: .utf8) {
print(responseString)
}
}
task.resume()
```
```csharp
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var url = "https://api.openveer.com/v1/messages";
var apiKey = Environment.GetEnvironmentVariable("API_KEY");
var payload = @"{
""model"": ""claude-sonnet-4-6"",
""max_tokens"": 1024,
""messages"": [
{
""role"": ""user"",
""content"": ""你好,世界""
}
]
}";
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
client.DefaultRequestHeaders.Add("anthropic-version", "2025-10-01");
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
var result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
}
}
```
```c
#include
#include
#include
int main(void) {
CURL curl;
CURLcode res;
const char api_key = getenv("API_KEY");
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
const char *url = "https://api.openveer.com/v1/messages";
const char *payload = "{"
"\"model\":\"claude-sonnet-4-6\","
"\"max_tokens\":1024,"
"\"messages\":[{\"role\":\"user\",\"content\":\"你好,世界\"}]"
"}";
char auth_header[256];
snprintf(auth_header, sizeof(auth_header), "x-api-key: %s", api_key);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, auth_header);
headers = curl_slist_append(headers, "anthropic-version: 2025-10-01");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
```
```objectivec
#import
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSURL url = [NSURL URLWithString:@"https://api.openveer.com/v1/messages"];
NSString apiKey = [NSProcessInfo processInfo].environment[@"API_KEY"];
NSDictionary *payload = @{
@"model": @"claude-sonnet-4-6",
@"max_tokens": @1024,
@"messages": @[
@{
@"role": @"user",
@"content": @"你好,世界"
}
]
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload
options:0
error:&error];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:apiKey forHTTPHeaderField:@"x-api-key"];
[request setValue:@"2025-10-01" forHTTPHeaderField:@"anthropic-version"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:jsonData];
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
return;
}
NSString *result = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(@"%@", result);
}];
[task resume];
[[NSRunLoop mainRunLoop] run];
}
return 0;
}
```
```ocaml
( Requires cohttp and yojson libraries )
open Lwt
open Cohttp
open Cohttp_lwt_unix
let url = "https://api.openveer.com/v1/messages"
let api_key = Sys.getenv "API_KEY"
let payload = {|{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "你好,世界"
}
]
}|}
let () =
let headers = Header.init ()
|> fun h -> Header.add h "x-api-key" api_key
|> fun h -> Header.add h "anthropic-version" "2025-10-01"
|> fun h -> Header.add h "Content-Type" "application/json"
in
let body = Cohttp_lwt.Body.of_string payload in
let response = Client.post ~headers ~body (Uri.of_string url) >>= fun (resp, body) ->
body |> Cohttp_lwt.Body.to_string >|= fun body_str ->
print_endline body_str
in
Lwt_main.run response
```
```dart
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
void main() async {
final url = Uri.parse('https://api.openveer.com/v1/messages');
final apiKey = Platform.environment['API_KEY'];
final payload = {
'model': 'claude-sonnet-4-6',
'max_tokens': 1024,
'messages': [
{
'role': 'user',
'content': '你好,世界'
}
]
};
final response = await http.post(
url,
headers: {
'x-api-key': apiKey!,
'anthropic-version': '2025-10-01',
'Content-Type': 'application/json',
},
body: jsonEncode(payload),
);
print(response.body);
}
```
```r
library(httr)
library(jsonlite)
url <- "https://api.openveer.com/v1/messages"
api_key <- Sys.getenv("API_KEY")
payload <- list(
model = "claude-sonnet-4-6",
max_tokens = 1024,
messages = list(
list(
role = "user",
content = "你好,世界"
)
)
)
response <- POST(
url,
add_headers(
x-api-key = api_key,
anthropic-version = "2025-10-01",
Content-Type = "application/json"
),
body = toJSON(payload, auto_unbox = TRUE),
encode = "raw"
)
cat(content(response, "text"))
```
响应示例
json
{
"model": "claude-sonnet-4-6",
"id": "msg_011CdfeHuC728oxqaLrRNbcB",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "你好!我是Claude。很高兴见到你。"
}
],
"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"
}
}
json
{
"error": {
"code": "model_not_found",
"message": "model not found (request id: 20260803181903172761862QzohKPXF)",
"param": "",
"type": "apimart_error"
}
}
json
{
"error": {
"code": "",
"message": "无效的API密钥 (request id: 20260803181903172761862QzohKPXF)",
"param": "",
"type": "apimart_error"
}
}
json
{
"error": {
"code": "",
"message": "账户余额不足 (request id: 20260803181903172761862QzohKPXF)",
"param": "",
"type": "apimart_error"
}
}
json
{
"error": {
"code": "",
"message": "请求过于频繁 (request id: 20260803181903172761862QzohKPXF)",
"param": "",
"type": "apimart_error"
}
}
json
{
"error": {
"code": "",
"message": "服务器内部错误 (request id: 20260803181903172761862QzohKPXF)",
"param": "",
"type": "apimart_error"
}
}
Authorizations
鉴权支持两种方式,任选其一:
x-api-key stringAuthorization stringanthropic-version stringBody
model stringmessages arraycontent string单条用户消息示例:
json
[{"role": "user", "content": "你好,Claude"}]
多轮对话示例:
json
[
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "你好!我是Claude。"},
{"role": "user", "content": "能解释一下AI吗?"}
]
预填充助手回复:
json
[
{"role": "user", "content": "太阳的希腊名称是?(A) Sol (B) Helios (C) Sun"},
{"role": "assistant", "content": "答案是 ("}
]
max_tokens integerthinking objectsystem string | arraytemperature numbertop_p numbertop_k integerstream booleanstop_sequences arraymetadata objecttools arraytool_choice objectResponse
id stringtype stringrole stringcontent arraymodel stringstop_reason stringstop_sequence string | nullstop_details object | nullusage objectoutput_tokens integercache_creation_input_tokens integercache_read_input_tokens integercache_creation objectservice_tier stringinference_geo stringoutput_tokens_details object使用示例
基础对话
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": "解释量子计算的基本原理"}
]
)
print(message.content[0].text)
多轮对话
messages = [
{"role": "user", "content": "什么是机器学习?"},
{"role": "assistant", "content": "机器学习是人工智能的一个分支..."},
{"role": "user", "content": "能举个实际应用的例子吗?"}
]
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=messages
)
使用系统提示词
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="你是一位资深的Python开发专家,擅长代码审查和优化建议。",
messages=[
{"role": "user", "content": "如何优化这段代码?\n\n[代码]"}
]
)
流式响应
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{"role": "user", "content": "写一篇关于AI的短文"}
]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
工具使用
tools = [
{
"name": "get_stock_price",
"description": "获取股票的实时价格",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "股票代码,例如:AAPL"
}
},
"required": ["ticker"]
}
}
]
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=tools,
messages=[
{"role": "user", "content": "特斯拉的股价是多少?"}
]
)
# 处理工具调用
if message.stop_reason == "tool_use":
tool_use = next(block for block in message.content if block.type == "tool_use")
print(f"调用工具: {tool_use.name}")
print(f"参数: {tool_use.input}")
视觉理解
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": "描述这张图片"
}
]
}
]
)
Base64图像
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": "分析这张图片"
}
]
}
]
)
最佳实践
1. 提示词工程
清晰的角色定义:
system = """你是一位经验丰富的数据科学家,专长包括:
- 统计分析和数据可视化
- 机器学习模型开发
- Python和R编程
请提供专业、准确的建议。"""
结构化输出:
message = "请以JSON格式返回分析结果,包含summary、key_findings和recommendations字段。"
2. 错误处理
from anthropic import APIError, RateLimitError
try:
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "你好"}]
)
except RateLimitError:
print("速率限制,请稍后重试")
except APIError as e:
print(f"API错误: {e}")
3. Token优化
# 使用更短的提示词
messages = [
{"role": "user", "content": "总结要点:\n\n[长文本]"}
]
# 限制输出长度
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=500, # 限制输出
messages=messages
)
4. 预填充响应
# 引导模型以特定格式回复
messages = [
{"role": "user", "content": "列出5个Python最佳实践"},
{"role": "assistant", "content": "以下是5个Python最佳实践:\n\n1."}
]
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=messages
)
流式响应处理
Python流式示例
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": "写一个Python装饰器示例"}
]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
JavaScript流式示例
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: '写一个React组件示例' }
]
});
for await (const chunk of stream) {
if (chunk.type === 'content_block_delta' &&
chunk.delta.type === 'text_delta') {
process.stdout.write(chunk.delta.text);
}
}
平台差异与对接注意
响应无包装
POST /v1/messages 成功时直接返回 Anthropic message 对象,没有 {code, data} 外层。官方 SDK、Claude Code、Cline 等才能 1:1 兼容。
错误格式(与官方唯一实质差异)
{
"error": {
"code": "model_not_found",
"message": "... (request id: ...)",
"param": "",
"type": "apimart_error"
}
}
相对 Anthropic 官方:顶层缺少 "type": "error";error.type 固定为 apimart_error,而非 invalid_request_error 等语义化类型。
对接建议:不要依赖 error.type 做重试分支,改用 HTTP 状态码 + error.code:
| 状态码 | 含义 | 建议动作 |
|---|---|---|
| 400 | 请求参数错误 | 不重试,检查请求体 |
| 401 | Key 无效 | 不重试 |
| 402 | 余额不足 | 不重试,提示充值 |
| 429 | 限流 | 退避后重试 |
| 5xx | 上游/网关异常 | 指数退避重试 |
报障请提供:error.message 末尾的 request id,以及响应头 x-oneapi-request-id。
流式 SSE
请求加 "stream": true。事件序列与官方一致:
message_start → content_block_start → ping → content_block_delta(多次)→ content_block_stop → message_delta → message_stop
⚠️ 流式与非流式的 usage 结构不同:message_delta.usage 通常只有 4 个 token 字段,没有 cache_creation、service_tier、inference_geo。请分开解析或全部设为可选。
未实现接口
POST /v1/messages/count_tokens 未实现,返回 404。官方 SDK 的 client.messages.count_tokens() 会失败。需要预估 token 时请在本地估算,或读取响应中的 usage.input_tokens。
必须忽略未知字段
本接口对上游透传,Anthropic 可能随时新增字段(如 stop_details、inference_geo、caller、output_tokens_details)。请勿开启严格 schema:
- Go:不要用
DisallowUnknownFields() - Pydantic:不要
extra="forbid" - TypeScript / Zod:用
.passthrough()而非.strict()
模型名建议
带 -thinking 后缀的同名模型是平台扩展别名。推荐用不带后缀的标准模型名 + 请求体 thinking 参数,便于迁移官方端点。
请求体其它字段与官方一致:model、messages、max_tokens(必填)、system、temperature、top_p、top_k、stop_sequences、stream、tools、tool_choice、thinking、metadata。语义以 Anthropic Messages API 为准。
注意事项
-
API 密钥安全:
* 使用环境变量存储 API 密钥
* 不要在代码中硬编码密钥
* 定期轮换密钥 -
速率限制:
* 注意 API 的速率限制
* 实现重试机制(按 HTTP 状态码)
* 使用指数退避策略 -
Token 管理:
* 监控 token 使用量(读usage)
* 优化提示词长度
* 使用适当的max_tokens值
* 开启 thinking 时output_tokens已含 thinking,勿重复计费 -
模型选择:
* Opus: 复杂任务、需要深度思考
* Sonnet: 平衡性能和成本
* Haiku: 快速响应、简单任务 -
内容解析:
* 遍历content取type == "text",不要写死content[0].text
* 模型若返回 Markdown 代码块包裹的 JSON,属模型输出而非接口包装(见下方 FAQ) -
内容过滤:
* 验证用户输入
* 过滤敏感信息
* 实现内容审核机制
FAQ
响应里 content 的 text 是 ` ```json
这不是接口结构问题。text 字段装的是模型生成的原始内容:模型判断你想要 JSON,就用 Markdown 代码块包起来了。接口不会也不应该改写模型输出。
想拿到干净的结构化数据,有三种正确做法(推荐程度从高到低):
- 用 tools 强制结构化输出——最可靠,
input字段直接就是解析好的对象:
{
"tools": [{
"name": "emit_result",
"input_schema": {
"type": "object",
"properties": { "answer": { "type": "string" } }
}
}],
"tool_choice": { "type": "tool", "name": "emit_result" }
}
- prefill 助手消息,让模型从
{接着写:
{
"messages": [
{ "role": "user", "content": "..." },
{ "role": "assistant", "content": "{" }
]
}
- 在 system prompt 里明确要求「只输出 JSON,不要加 Markdown 代码块」。
不推荐用正则去剥 code fence——模型偶尔不加围栏时会解析失败。