Browse documentation

Rust SDK

The official nRouter Rust SDK providing both standard async-openai integration and a native client for x-nr-* metadata and cost extraction.

Last updated

The official nRouter Rust SDK (nrouter) provides typed, high-performance bindings for nRouter. It offers two distinct entry points: a drop-in client for the standard async-openai ecosystem, and a native client exposing x-nr-* request metadata, real-time cost tracking, and streaming capabilities.

Installation

Add nrouter and tokio to your Cargo.toml. The examples below also use serde_json (the native client takes JSON bodies) and, for the drop-in client, async-openai — add whichever your code imports directly:

[dependencies]
nrouter = "3.1.2"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
serde_json = "1"
async-openai = { version = "0.41.3", features = ["full"] }  # only for nrouter::client()

Or install via cargo add:

cargo add nrouter serde_json
cargo add tokio --features "macros,rt-multi-thread"
cargo add async-openai@0.41.3 --features full   # only for nrouter::client()

Setup & Authentication

Set your virtual API key in your environment:

export NROUTER_API_KEY="sk-nrouter-your-key-here"

The SDK targets https://api.nrouter.ai/v1 by default.

Quickstart

1. Using the async-openai Client

If you already use the async-openai crate, initialize the client using nrouter::client():

use async_openai::types::chat::{
    ChatCompletionRequestUserMessageArgs, CreateChatCompletionRequestArgs,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Automatically reads NROUTER_API_KEY and targets https://api.nrouter.ai/v1
    let client = nrouter::client()?;

    let request = CreateChatCompletionRequestArgs::default()
        .model("gpt-5.4-mini")
        .messages(vec![ChatCompletionRequestUserMessageArgs::default()
            .content("Hello, nRouter!")
            .build()?
            .into()])
        .build()?;

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

    Ok(())
}

2. Using the Native Client (Cost & Metadata)

To access exact USD request costs, token counts, and gateway headers, use nrouter::http::Client:

use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = nrouter::http::Client::from_env()?;

    let out = client
        .chat_completions(&json!({
            "model": "gpt-5.4-mini",
            "messages": [{"role": "user", "content": "Hello, nRouter!"}]
        }))
        .await?;

    println!("Response: {:?}", out.body["choices"]);

    // Inspect real-time cost
    match out.meta.cost {
        Some(usd) => println!("Cost: ${usd}"),
        None => println!("Cost status: {:?}", out.meta.cost_status),
    }

    Ok(())
}

Streaming

The native client incrementally parses Server-Sent Events (SSE) across all text wires:

use serde_json::json;

// `next()` is the stream's own method (Result<Option<StreamChunk>, NRouterError>),
// so no `StreamExt` import is needed.
let mut stream = client
    .messages_stream(&json!({
        "model": "claude-haiku-4-5-20251001",
        "max_tokens": 256,
        "messages": [{"role": "user", "content": "Hello!"}]
    }))
    .await?;

while let Some(chunk) = stream.next().await? {
    print!("{}", chunk.delta);
}

println!("\nRequest ID: {:?}", stream.meta.request_id);

Dropping the stream drops the underlying connection, terminating unread token generation.

Error Handling

Gateway refusals map to variants of NRouterError:

use nrouter::NRouterError;

match client.chat_completions(&body).await {
    Err(NRouterError::GuardrailBlocked(err)) => {
        eprintln!("Blocked by guardrail policy: {:?}", err);
    }
    Err(NRouterError::Credit(err)) => {
        eprintln!("Insufficient credits: {:?}", err);
    }
    Err(e) if e.is_retryable() => {
        eprintln!("Retryable failure (rate limit or 503): {:?}", e);
    }
    Err(e) => return Err(e.into()),
    Ok(out) => println!("Success: {:?}", out.body),
}
VariantHTTP StatusDescription
Request400Invalid request parameters
GuardrailBlocked400Blocked by configured guardrail
Authentication401Invalid or revoked virtual key
Credit402Balance exhausted or budget limit exceeded
NotFound404Model not found
RateLimit429Rate limit or TPM limit exceeded
Service503Gateway or upstream service error
TransportNetwork transport error

is_retryable() returns true for RateLimit, Service, and Transport errors.

Response Metadata & Cost Tracking

out.meta provides full access to gateway metadata:

  • request_id: Unique request identifier.
  • latency_ms: Edge-measured time from arrival until headers ready.
  • cost: Exact USD request cost.
  • cost_status: exact or unpriced.
  • input_tokens / output_tokens: Token accounting figures.

Source & Repository

The source code and runnable demonstrations are available on GitHub:

Was this page helpful?