Rust SDK
The Rust ecosystem currently has no official Anthropic SDK. The two paths below:
- OpenAI compatible: use the community-maintained
async-openaicrate and pointapi_baseat Sakrylle. - Anthropic native: send raw HTTP with
reqwest; for a more SDK-like experience, see the community projects listed at the bottom.
Anthropic has not yet released an official Rust SDK. The reqwest examples below cover only minimal usage — refer to the Anthropic API docs for the full surface.
Setup
export SAKRYLLE_API_KEY="sk-xxxxxxxxxxxxxxxx"Rust 1.75+ is required.
OpenAI compatible
Install
cargo add async-openai
cargo add tokio --features full
cargo add futuresMinimal usage
OpenAIConfig::with_api_base plus with_api_key together redirect requests to Sakrylle:
use async_openai::{
config::OpenAIConfig,
types::chat::{
ChatCompletionRequestSystemMessageArgs, ChatCompletionRequestUserMessageArgs,
CreateChatCompletionRequestArgs,
},
Client,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = OpenAIConfig::new()
.with_api_key(std::env::var("SAKRYLLE_API_KEY")?)
.with_api_base("https://api.sakrylle.com/v1");
let client = Client::with_config(config);
let request = CreateChatCompletionRequestArgs::default()
.model("gpt-5.6-sol")
.max_tokens(512u32)
.messages([
ChatCompletionRequestSystemMessageArgs::default()
.content("You are a concise assistant.")
.build()?
.into(),
ChatCompletionRequestUserMessageArgs::default()
.content("Explain vector databases in one sentence.")
.build()?
.into(),
])
.build()?;
let response = client.chat().create(request).await?;
for choice in response.choices {
println!("{:?}", choice.message.content);
}
Ok(())
}Streaming
use async_openai::{
config::OpenAIConfig,
types::chat::{ChatCompletionRequestUserMessageArgs, CreateChatCompletionRequestArgs},
Client,
};
use futures::StreamExt;
use std::io::{stdout, Write};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = OpenAIConfig::new()
.with_api_key(std::env::var("SAKRYLLE_API_KEY")?)
.with_api_base("https://api.sakrylle.com/v1");
let client = Client::with_config(config);
let request = CreateChatCompletionRequestArgs::default()
.model("gpt-5.6-sol")
.max_tokens(512u32)
.messages([ChatCompletionRequestUserMessageArgs::default()
.content("Count to 5.")
.build()?
.into()])
.build()?;
let mut stream = client.chat().create_stream(request).await?;
let mut out = stdout().lock();
while let Some(result) = stream.next().await {
match result {
Ok(chunk) => {
for choice in chunk.choices.iter() {
if let Some(content) = &choice.delta.content {
write!(out, "{content}")?;
out.flush()?;
}
}
}
Err(e) => eprintln!("\nstream error: {e:?}"),
}
}
Ok(())
}Tool calling
use async_openai::types::chat::{
ChatCompletionRequestUserMessageArgs, ChatCompletionToolArgs, ChatCompletionToolType,
CreateChatCompletionRequestArgs, FunctionObjectArgs,
};
use serde_json::json;
let tool = ChatCompletionToolArgs::default()
.r#type(ChatCompletionToolType::Function)
.function(
FunctionObjectArgs::default()
.name("get_weather")
.description("Look up the current weather for a city")
.parameters(json!({
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"],
}))
.build()?,
)
.build()?;
let request = CreateChatCompletionRequestArgs::default()
.model("gpt-5.6-sol")
.max_tokens(256u32)
.tools(vec![tool])
.messages([ChatCompletionRequestUserMessageArgs::default()
.content("What's the weather in Shanghai today?")
.build()?
.into()])
.build()?;
let response = client.chat().create(request).await?;
println!("{:?}", response.choices[0].message.tool_calls);
async-openaiis a community crate; type names may shift between versions. Refer to the crate docs for the latest API.
Anthropic native
Sakrylle's /v1/messages endpoint is wire-compatible with the Anthropic protocol. Here is a minimal direct example:
Dependencies
cargo add reqwest --features json,rustls-tls
cargo add tokio --features full
cargo add serde --features derive
cargo add serde_jsonCall /v1/messages
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, Deserialize)]
struct ContentBlock {
#[serde(rename = "type")]
kind: String,
text: Option<String>,
}
#[derive(Debug, Deserialize)]
struct MessageResponse {
content: Vec<ContentBlock>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = std::env::var("SAKRYLLE_API_KEY")?;
let body = json!({
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "Introduce Claude in one sentence." }
]
});
let resp: MessageResponse = Client::new()
.post("https://api.sakrylle.com/v1/messages")
.header("x-api-key", api_key)
.header("anthropic-version", "2023-06-01")
.json(&body)
.send()
.await?
.error_for_status()?
.json()
.await?;
for block in resp.content {
if let (Some(text), "text") = (block.text, block.kind.as_str()) {
println!("{text}");
}
}
Ok(())
}Prefer
x-api-key(Anthropic's native auth header). Sakrylle also acceptsAuthorization: Bearer <key>— pick one. For streaming, parse SSE yourself witheventsource-streamplus reqwest, or stick with the OpenAI-compatible path above.
Anthropic crates
If you accept community-maintained code, two options to watch:
Neither is as mature as async-openai. Evaluate features (streaming, tool use) before committing.
Error handling
match client.chat().create(request).await {
Ok(resp) => println!("{resp:?}"),
Err(async_openai::error::OpenAIError::ApiError(e)) => {
eprintln!("status={:?} type={:?} msg={}", e.code, e.r#type, e.message);
}
Err(e) => eprintln!("transport error: {e:?}"),
}For a full error table, see Errors.
