openrouter-rs 0.9.0

A type-safe OpenRouter Rust SDK
Documentation

openrouter-rs

Type-safe, async Rust SDK for the OpenRouter API.

Crates.io Documentation CI License: MIT

docs.rs | examples | crate | docs map | openrouter-cli | contributing | changelog

openrouter-rs is a community-maintained Rust SDK for OpenRouter. It exposes a domain-oriented client for chat, responses, messages, rerank, audio speech, video generation, models, embeddings, and management APIs, plus a companion CLI in the same repository.

The current repo snapshot implements 51 / 51 official OpenAPI method/path entries, with published live integration coverage tracked in docs/operations/official-endpoint-test-matrix.md.

Why openrouter-rs

  • Domain-oriented clients: chat(), responses(), messages(), rerank(), audio().speech(), videos(), models(), management(), and opt-in legacy()
  • Typed request/response models with builder-style ergonomics
  • Tokio-native reqwest + rustls transport with no surf / curl dependency chain
  • Streaming support for chat, responses, and messages, including a unified stream abstraction
  • Typed tools, manual JSON-schema tools, and multimodal chat content
  • Discovery, rerank, audio speech, video generation, embeddings, API-key management, workspace management, organization members, guardrails, activity, credits, and generation metadata/content coverage
  • A companion CLI for profile resolution, discovery, management, and billing/usage workflows

Installation

[dependencies]
openrouter-rs = "0.9.0"
tokio = { version = "1", features = ["full"] }

Legacy text completions are opt-in:

[dependencies]
openrouter-rs = { version = "0.9.0", features = ["legacy-completions"] }
tokio = { version = "1", features = ["full"] }

Requirements:

  • Rust 1.85+
  • Tokio 1.x
  • OPENROUTER_API_KEY for API-backed examples and live tests
  • OPENROUTER_MANAGEMENT_KEY for management-governed examples and tests

Quick Start

use openrouter_rs::{
    OpenRouterClient,
    api::chat::{ChatCompletionRequest, Message},
    types::Role,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = OpenRouterClient::builder()
        .api_key(std::env::var("OPENROUTER_API_KEY")?)
        .http_referer("https://yourapp.example")
        .x_title("my-openrouter-app")
        .app_categories(["cli-agent"])
        .build()?;

    let request = ChatCompletionRequest::builder()
        .model("anthropic/claude-sonnet-4")
        .messages(vec![Message::new(
            Role::User,
            "Explain Rust ownership in plain English.",
        )])
        .build()?;

    let response = client.chat().create(&request).await?;
    println!("{}", response.choices[0].content().unwrap_or(""));

    Ok(())
}

The SDK keeps setup intentionally narrow: configure runtime values on OpenRouterClient::builder(), then choose the final model on each request builder. File/profile config resolution belongs in the companion CLI or in your application layer, not in the SDK core.

API Surface

The canonical public surface is domain-oriented:

Domain Canonical methods Primary endpoints Auth note
chat() create, stream, stream_tool_aware, stream_unified /chat/completions API key
responses() create, stream, stream_unified /responses API key
messages() create, stream, stream_unified /messages API key
rerank() create /rerank API key
audio().speech() create /audio/speech (legacy /tts fallback) API key
videos() create, list_models, get_generation, get_content /videos* API key
models() list, list_by_category, list_by_parameters, list_endpoints, list_providers, list_user_models, get_model_count, list_zdr_endpoints, create_embedding, list_embedding_models /models*, /providers, /endpoints/zdr, /embeddings* API key
management() create_api_key, create_api_key_in_workspace, list_api_keys, list_api_keys_in_workspace, create_auth_code, create_api_key_from_auth_code, list_guardrails, list_guardrails_in_workspace, create_guardrail, list_organization_members, list_workspaces, create_workspace, get_workspace, update_workspace, delete_workspace, add_workspace_members, remove_workspace_members, get_activity, get_credits, create_coinbase_charge, get_generation, get_generation_content /keys*, /auth/keys*, /guardrails*, /organization/members, /workspaces*, /activity, /credits*, /generation*, /key Governed endpoints require a management key; billing/session endpoints still use the normal API key because that is how OpenRouter authenticates them
legacy() completions().create /completions legacy-completions feature + API key

At runtime, the builder/client exposes the values the SDK directly consumes:

  • base_url
  • api_key
  • management_key
  • http_referer
  • x_title
  • app_categories

Common Workflows

openrouter-rs is not just a thin /chat/completions wrapper. The repo currently covers:

  • chat completions, responses, and Anthropic-compatible messages
  • rerank, audio speech generation, and video generation polling/content retrieval
  • unified streaming across chat, responses, and messages
  • manual tools and typed tools backed by schemars
  • multimodal chat content, including image, audio, video, and file parts
  • model discovery, provider discovery, embeddings, and ZDR endpoints
  • typed generation metadata, workspace I/O logging controls, and video callback URL support
  • management-key workflows for keys, workspace-scoped keys, auth codes, organization members, workspaces, workspace membership, guardrails, workspace-scoped guardrails, and activity, plus API-key-authenticated credits and generation metadata/content endpoints

For deeper examples, prefer the runnable examples in examples/ over long README snippets.

Examples

The repo includes runnable examples for the highest-value workflows:

Application Patterns

Example Focus
examples/axum_chat_gateway.rs Minimal axum server that proxies prompts through OpenRouterClient
examples/typed_tool_agent.rs Practical typed-tool agent loop with explicit tool dispatch
examples/domain_chat_completion.rs Canonical chat() request with the domain-oriented client

Tokio Streaming

Example Focus
examples/stream_chat_completion.rs Stream chat deltas into a Tokio task/stdout
examples/stream_chat_with_tools.rs Tool-aware streaming aggregation plus a second round after execution
examples/stream_response.rs responses() streaming
examples/stream_messages.rs messages() streaming

API Surface Demos

Example Focus
examples/basic_tool_calling.rs Manual tool-calling loop
examples/typed_tool_calling.rs Typed tools with generated schema
examples/create_response.rs responses() create
examples/create_message.rs messages() create
examples/create_rerank.rs rerank().create(...)
examples/create_speech.rs audio().speech().create(...)
examples/create_video_generation.rs videos().create(...)
examples/create_embedding.rs models().create_embedding(...)
examples/domain_management_api_keys.rs API-key management via management()
examples/list_organization_members.rs management().list_organization_members(...)
examples/list_workspaces.rs management().list_workspaces(...)
examples/exchange_code_for_api_key.rs PKCE/auth-code flow
examples/send_completion_request.rs Legacy completions (legacy-completions required)

Typical local usage:

export OPENROUTER_API_KEY=sk-or-v1-...

cargo run --example axum_chat_gateway
cargo run --example domain_chat_completion
cargo run --example stream_chat_completion
cargo run --example typed_tool_agent
cargo run --example typed_tool_calling
cargo run --example create_response
cargo run --example create_message
cargo run --example create_rerank
cargo run --example create_speech
cargo run --example create_embedding

For shell and CI automation recipes built around the companion CLI, see docs/operations/cli-automation-workflows.md.

CLI Companion

This workspace also contains crates/openrouter-cli, a companion CLI for profile resolution, discovery, management, and usage/billing workflows.

Examples:

cargo run -p openrouter-cli -- --help
cargo run -p openrouter-cli -- profile show
cargo run -p openrouter-cli -- models list --category programming
cargo run -p openrouter-cli -- keys list --include-disabled
cargo run -p openrouter-cli -- workspaces list --limit 20
cargo run -p openrouter-cli -- usage activity --date 2026-03-01

See crates/openrouter-cli/README.md for the full command surface and config/auth precedence rules. For copy-paste shell/CI recipes, see docs/operations/cli-automation-workflows.md.

Project Status

  • Community-maintained third-party SDK; not affiliated with OpenRouter
  • Canonical docs and examples prefer the domain clients over older flat helpers
  • Accepted endpoint coverage is tracked against the current OpenAPI snapshot, and the current baseline is fully implemented at the SDK surface (51 / 51)
  • Live integration coverage and gaps are published in docs/operations/official-endpoint-test-matrix.md
  • Migration guidance for the 0.8.x -> 0.9.0 audio speech/future-proofing release, the 0.7.x -> 0.8.0 transport/error-surface release, and the archived 0.5.x -> 0.6.x naming guide lives in MIGRATION.md
  • Legacy POST /completions support remains available behind the legacy-completions feature

🔁 0.9 Audio Speech Migration

  • client.tts().create(...) -> client.audio().speech().create(...)
  • api::tts::TtsRequest -> api::audio::SpeechRequest
  • api::tts::TtsResponseFormat -> api::audio::SpeechResponseFormat
  • api::tts remains as a deprecated compatibility module, but new examples and docs use api::audio
  • Newly added and high-churn public request/response structs use builder construction and may be marked #[non_exhaustive]; prefer request builders over struct literals

🔁 0.8 Transport/Error Migration

Full migration guide: MIGRATION.md

  • OpenRouterError::HttpRequest(surf::Error) -> OpenRouterError::HttpRequest(HttpRequestError)
  • ApiErrorContext.status: surf::StatusCode -> ApiErrorContext.status: http::StatusCode
  • openrouter_rs::utils::{with_bearer_auth, with_request_metadata, with_client_request_headers, handle_error} -> caller-owned transport helpers or direct use of the canonical domain clients
  • No API migration is required if you only use OpenRouterClient plus chat(), responses(), messages(), rerank(), videos(), models(), management(), and legacy()

🔁 0.6 Naming/Pagination Migration

Full migration guide: MIGRATION.md

  • models().count() -> models().get_model_count()
  • models().list_for_user() -> models().list_user_models()
  • management().exchange_code_for_api_key(...) -> management().create_api_key_from_auth_code(...)
  • management().list_guardrails(offset, limit) -> management().list_guardrails(Some(PaginationOptions::with_offset_and_limit(offset, limit)))
  • client.list_api_keys(offset, include_disabled) -> management().list_api_keys(Some(PaginationOptions::with_offset(offset)), include_disabled)

0.6.0 removed the transitional aliases above; use the canonical method names shown here.

Development

Prefer the just recipes so local work stays aligned with CI:

just quality
just quality-ci
just test-live-contract
OPENROUTER_MANAGEMENT_KEY=... just test-live-contract-management

Focused commands:

  • just test-unit
  • just test-lib
  • just test-doc
  • just test-integration-subsets
  • just test-cli
  • just check-migration-docs
  • just test-migration-smoke
  • just test-integration

Environment and model-pool details live in tests/integration/README.md. A starter env file lives at .env.example.

Docs Map

Start with docs/README.md for grouped navigation across root docs, docs/, specs/, and subsystem READMEs.

Users And Setup

Contributors And Project Policies

Design And Roadmap

Operations, Validation, And Distribution

📈 Release History

Version 0.9.0 (Latest)

  • Added the canonical audio().speech() SDK surface for official /audio/speech, with deprecated tts() compatibility aliases.
  • Expanded workspace, workspace-scoped keys/guardrails, workspace I/O logging, generation content/metadata, and video callback coverage while keeping the endpoint snapshot at 51 / 51.
  • Marked high-churn public types as builder-first/future-proof and documented the 0.8.x -> 0.9.0 migration path.

Version 0.8.1

  • Added typed POST /tts support with the canonical tts() client surface and a runnable example.
  • Added live smoke coverage for POST /rerank and GET /organization/members, and restored the repo snapshot to 43 / 43 accepted OpenAPI endpoints.
  • Added openrouter-cli organization members list and aligned the CLI/docs surface with the latest management coverage.

Version 0.8.0

  • Completed the SDK transport migration to reqwest + rustls and removed the legacy surf / curl dependency chain.
  • Made the public HTTP error surface backend-neutral and documented the 0.7.x -> 0.8.0 breaking changes.
  • Expanded the repo snapshot with rerank, video, and organization-member coverage plus refreshed examples and CLI automation docs.

Version 0.7.0

  • Removed the SDK-level config surface and kept file/profile config out of the core crate.
  • Standardized the canonical domain-oriented client docs around the 0.7.x API surface.
  • Improved error normalization for HTTP 200 payloads that actually contain API error bodies.

Contributing

See CONTRIBUTING.md for the full contributor workflow.

At a minimum, if you change public API surface, examples, or docs:

  • update the relevant README/docs in the same change
  • run just quality
  • run just quality-ci if you touched migration docs, CLI behavior, or CI-aligned release/test flows

Related policies:

License

MIT. See LICENSE.