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),
}| Variant | HTTP Status | Description |
|---|---|---|
Request | 400 | Invalid request parameters |
GuardrailBlocked | 400 | Blocked by configured guardrail |
Authentication | 401 | Invalid or revoked virtual key |
Credit | 402 | Balance exhausted or budget limit exceeded |
NotFound | 404 | Model not found |
RateLimit | 429 | Rate limit or TPM limit exceeded |
Service | 503 | Gateway or upstream service error |
Transport | — | Network 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:exactorunpriced.input_tokens/output_tokens: Token accounting figures.
Source & Repository
The source code and runnable demonstrations are available on GitHub: