claude-api 0.5.0

Type-safe Rust client for the Anthropic API
Documentation

claude-api

Type-safe Rust client for the Anthropic API.

Sibling project to claude-wrapper. That one wraps the claude CLI; this one wraps the HTTP API. They do not depend on each other.

Status

v0.5 -- full API surface. Every documented Anthropic endpoint is now reachable through a typed Rust namespace:

  • Messages, Models, Batches, Files, count_tokens, streaming (carried forward from v0.1-v0.4)
  • Admin API (27 endpoints): organization, invites, users, workspaces + members, api_keys, usage_report, cost_report, rate_limits
  • Skills API (8 endpoints): full CRUD + skill versions, multipart upload
  • User Profiles API (5 endpoints) including the enrollment-URL flow
  • Managed Agents preview: sessions, agents, environments, vaults
    • credentials, memory_stores + memories + memory_versions, session resources, events (list/send/SSE stream), and the multi-agent threads endpoints
  • Bedrock auth via a sigv4 RequestSigner extension point
  • Typed BetaHeader enum for the 23 canonical anthropic-beta values, with Other(String) forward-compat fallthrough
  • Live-test harness with 15 record-or-replay tests covering the cheap and read-only surfaces; cassettes committed for free CI replay
  • Spec-diff tooling that compares every public Rust struct to its OpenAPI schema field-by-field

Carried forward from v0.4 and earlier:

  • v0.4 Agent-loop guardrails (parallel dispatch + cost budget + cancellation), streaming callbacks, dry_run, cost_preview, #[derive(Tool)] proc macro
  • v0.3 Typed Citation enum, context compaction, full Batches API, Files API beta, typed BuiltinTool wrappers
  • v0.2 ToolRegistry + agent loop, Conversation helper, PricingTable, vision and document blocks, prompt-cache sugar, sync feature
  • v0.1 Messages + Models endpoints, forward-compatible serde, retry policy that honors Retry-After, request-id propagation, structured tracing

See CHANGELOG.md for per-version detail.

Quick start

[dependencies]
claude-api = "0.5"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
use claude_api::{Client, messages::CreateMessageRequest, types::ModelId};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new(std::env::var("ANTHROPIC_API_KEY")?);

    let resp = client
        .messages()
        .create(
            CreateMessageRequest::builder()
                .model(ModelId::SONNET_4_6)
                .max_tokens(256)
                .system("Be concise.")
                .user("What is the capital of France?")
                .build()?,
        )
        .await?;

    for block in &resp.content {
        if let claude_api::messages::ContentBlock::Known(
            claude_api::messages::KnownBlock::Text { text, .. },
        ) = block
        {
            println!("{text}");
        }
    }
    Ok(())
}

See examples/ for streaming, streaming-with-callbacks, tool use, derived tools, vision, document, conversation, and the agent loop.

Why another Anthropic Rust client?

  • Forward-compatible by design. New ContentBlock, StreamEvent, ContentDelta, Citation, and BuiltinTool variants from the API are preserved as raw JSON via an Other(Value) arm rather than breaking older SDK versions. New type tags round-trip byte-for-byte. No other Rust crate (and no official SDK in any language) does this.

    Upgrade contract: when a server-side type tag that previously fell through to Other becomes a recognized Known variant in a new release, that's a minor version bump. Code that pattern-matched on Other(v) if v["type"] == "thinking" will silently stop matching -- the value now arrives as Known(KnownBlock::Thinking { .. }). When you bump claude-api, sweep your Other matches and route the newly-known variants explicitly. Releases that promote variants will call them out in the changelog.

  • Retry-After honored. Most existing Rust crates for the Anthropic API ignore the header. This one respects it, with configurable RetryPolicy { max_attempts, initial_backoff, max_backoff, jitter, respect_retry_after }.

  • Cost-aware by default. PricingTable ships with bundled rates for current models; Conversation::cost(&PricingTable) and cost_preview(&request, &pricing) give you live and pre-flight USD numbers. Agent loop accepts a cost_budget that aborts a run before the next iteration if a cap is exceeded.

  • Operational quality from day one. request-id is surfaced on every error (critical for support tickets), ApiKey: Debug redacts the secret, base-URL override makes wiremock-based testing the default story, structured tracing spans on every request and retry.

  • Tools you can derive. #[derive(Tool)] on a struct generates the Tool impl from the struct's JsonSchema and an inherent async fn run(self). No string-literal schemas, no boilerplate name / description overrides unless you want them.

  • No surprise abstractions. No prompt-template DSL, no multi-provider routing, no response caching, no bundled CLI. If you want those, use a different crate.

Feature flags

Flag Default Adds
async yes Async client (tokio + reqwest)
rustls yes rustls TLS backend
streaming yes SSE streaming + EventStream::aggregate + callback hooks
sync Blocking client (reqwest blocking)
native-tls native-tls backend instead of rustls
schemars-tools Tool::from_schemars::<T: JsonSchema> ctor
derive #[derive(Tool)] (pulls in claude-api-derive)
pricing PricingTable + cost calculation + cost_preview
conversation Multi-turn Conversation helper
bedrock AWS sigv4 BedrockSigner (custom auth signer)
vertex GCP Vertex auth -- v0.6+ (placeholder; not wired yet)
admin Admin API (organizations, users, workspaces, etc.)
skills Skills API (CRUD + versions, multipart upload)
user-profiles User Profiles API + enrollment-URL flow
managed-agents-preview Managed Agents preview (sessions, agents, vaults, ...)

Disable defaults if you only want the type definitions:

claude-api = { version = "0.5", default-features = false }

Tool example with #[derive(Tool)]

use claude_api::derive::Tool;
use claude_api::tool_dispatch::{ToolError, ToolRegistry};
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::{json, Value};

/// Get the current weather for a city.
#[derive(Deserialize, JsonSchema, Tool)]
struct GetWeather {
    /// City to look up.
    city: String,
}

impl GetWeather {
    async fn run(self) -> Result<Value, ToolError> {
        Ok(json!({"temp_f": 72, "city": self.city}))
    }
}

let mut registry = ToolRegistry::new();
registry.register_tool(GetWeather::tool());

The tool name (get_weather) is derived from the struct name; the description from the doc comment. Override either with #[tool(name = "...", description = "...")].

Roadmap

  • v0.1 Messages, Models, streaming, retry, forward-compat
  • v0.2 ToolRegistry + agent loop, Conversation helper, PricingTable, vision and document blocks, prompt-cache sugar, sync feature
  • v0.3 Batches (with poller), Files API, citations, typed built-in tool wrappers, context compaction
  • v0.4 Agent-loop guardrails (parallel dispatch + cost budget + cancellation), streaming callbacks, dry_run, cost_preview, #[derive(Tool)] proc macro, mid-stream tool approval gates, record/replay test harness, Bedrock auth (sigv4 signer)
  • v0.5 Admin / Skills / User Profiles / Managed Agents preview endpoints, typed BetaHeader enum, spec-diff tooling, live-test cassettes
  • v0.6+ Vertex AI auth, Tower Service -> Tool integration, typed promotions for stop_details / context_management / ModelCapabilities, streaming-SSE cassette recording, vault credential live tests

Anti-goals

If a feature request comes in for any of these, the answer is "different crate":

  • Local prompt templating
  • Multi-provider routing (OpenAI, Gemini, etc.)
  • Response caching layer
  • Background queues or scheduling
  • Vector store integrations
  • Embedding API (Anthropic does not have one; do not stub)
  • Legacy /v1/complete Completions API

MSRV

Rust 1.82 or later.

License

Licensed under either of Apache-2.0 or MIT at your option. Contributions are dual-licensed under the same terms.