README
¶
nRouter SDK & Examples
SDK and code examples for the nRouter LLM gateway.
Supported today: JavaScript/TypeScript, Python, and Java
Three SDKs are supported. npm, PyPI and Maven Central carry those three. The rest are distributed but not supported: Kotlin and Android on Maven Central, Rust on crates.io, Dart / Flutter on pub.dev and R on R-universe are registry-distributed public previews, and Swift and Go resolve immutable git tags. Every SDK here is held to the same conformance and security gates. Distribution does not broaden the support commitment.
⚠️ Repository source is 3.0.0 for all ten, but four registry artifacts lag
that train — Kotlin, Android and Rust serve 2.1.0, Dart serves 2.1.1. The
install snippets below pin the version each registry actually serves, because
asking for 3.0.0 there fails to resolve. Every version on this page was read
from the registry on 2026-09-02; re-verify with the commands under
SDK Ecosystem & Status rather than trusting the
number.
| SDK | Registry | Registry URL | Package | Version |
|---|---|---|---|---|
| JavaScript / TypeScript | npm | npmjs.com/package/@nrouter_ai/sdk | @nrouter_ai/sdk |
3.0.0 |
| Python | PyPI | pypi.org/project/nrouter-sdk | nrouter-sdk |
3.0.0 |
| Java | Maven Central | central.sonatype.com | ai.nrouter:nrouter-sdk |
3.0.0 |
All ten SDKs are held to the same public wire contract. The conformance gate accounts for all 150 route-ownership cells (15 routes × 10 SDKs): seven first-party transports expose native helpers with the exact path and HTTP verb, Android delegates the exact surface to Kotlin, and the JS/Python hybrid clients explicitly partition native helpers from their bounded vendor-client inheritance seam. Python's native route cells require both sync and async implementations. For inherited routes, the source gate proves ownership and a compiled resource—not the vendor package's internal HTTP implementation. The complete local gate compiles and runs the package suites against their locked dependencies.
The conformance gate derives and compares every manifest to the canonical
version in spec/nrouter-sdk-spec.json; release metadata cannot drift silently.
One API key for models across six provider clouds — Alibaba US, OpenAI, AWS Bedrock, Azure Foundry, Google Vertex AI and Anthropic. nRouter serves the OpenAI wire format and Anthropic's Messages API natively, plus embeddings, audio, images and video.
One key. One bill. The live multi-provider catalog. Guardrails, prompt templates, and cost tracking built in. Browse the exact models available now at nrouter.ai/api/public/models.
The model decides the route — a Claude id is not callable on every endpoint
The gateway resolves a provider endpoint per wire, and a provider that
serves no endpoint for a wire answers 404 model_unavailable_on_route: the
model exists, just not on the route it was asked for. Anthropic serves
/v1/messages only — it has neither chat-completions nor Responses — so a
claude-* id posted to /v1/chat/completions fails for a customer holding a
valid key and a real model id.
| You are calling | Use a model from |
|---|---|
/v1/chat/completions, /v1/completions, /v1/responses |
OpenAI, Azure Foundry, Vertex AI or Alibaba (for example gpt-5.4-mini) |
/v1/messages, /v1/messages/count_tokens |
Anthropic (claude-sonnet-4-5-20250929) or any provider serving that wire |
The JS/TS SDK is the one exception: client.nr.chat() selects /v1/messages
itself for claude-* ids and translates the response back, so the examples
below pass a Claude id to it deliberately.
Every example here uses a model measured in the live catalogue on 2026-08-31. Confirm against your own key before spending — the catalogue is per-org:
curl -s https://api.nrouter.ai/v1/models -H "Authorization: Bearer $NROUTER_API_KEY"
gpt-5.4-mini is a reasoning model: give it a real token budget (~1024), or a
small max_tokens is spent on hidden reasoning and the reply comes back empty.
conformance/doc_wires.py gates every snippet in this repository against that
table.
Why Use the nRouter SDK?
Rather than juggling separate provider SDKs (OpenAI, Anthropic, Bedrock, Vertex AI, Azure Foundry), nRouter provides:
- One API Key Across 6 Provider Clouds: Call OpenAI, Claude, Vertex, Bedrock, and Azure models with a single client and unified billing.
- Built-in Spend & Observability: Every response captures exact latency, model ID, and request cost (
x-nr-request-cost) with no extra telemetry instrumentation needed. - Automated Guardrails & Compliance: PII redaction, prompt injection protection, and keyword scanning configured once in the dashboard apply automatically.
- Smart Routing & Automatic Failover: Route across provider clouds by real-time latency, price, or availability using router aliases.
- Universal Enterprise Features: Unified conversation memory, Jinja2 prompt variable injection, and RFC 9110 retry-after exponential backoff across all 10 SDKs.
Authentication, Environment & .env Setup
All nRouter SDKs automatically read your API key from the NROUTER_API_KEY environment variable.
1. Where to Get Your API Key
- Sign in to your dashboard: nrouter.ai/dashboard.
- Go to API Keys / Virtual Keys: nrouter.ai/dashboard/keys.
- Click Create Key. Virtual keys start with
sk-nrouter-. Assign key budgets, rate limits, and guardrails directly in the dashboard.
2. Configure Local .env
Copy .env.example to .env:
cp .env.example .env
Edit .env and set your key:
NROUTER_API_KEY="sk-nrouter-your-api-key-here"
NROUTER_BASE_URL="https://api.nrouter.ai/v1"
3. How to Source .env
- Shell (Bash / Zsh):
export $(grep -v '^#' .env | xargs) - Node.js / TypeScript:
npm install dotenvimport "dotenv/config"; import { nRouter } from "@nrouter_ai/sdk"; const client = new nRouter(); // reads process.env.NROUTER_API_KEY - Python:
pip install python-dotenvfrom dotenv import load_dotenv load_dotenv() from nroutersdk import nRouter client = nRouter() # reads os.environ["NROUTER_API_KEY"] - Go:
import "github.com/joho/godotenv" _ = godotenv.Load() client, err := nrouter.NewFromEnv()
All API keys must start with the sk-nrouter- prefix. You can also pass the key explicitly in code via apiKey / api_key in any SDK constructor.
Quick Start
TypeScript / JavaScript
npm install @nrouter_ai/sdk
import { nRouter } from "@nrouter_ai/sdk";
const client = new nRouter(); // reads NROUTER_API_KEY from environment
const res = await client.nr.chat({
model: "claude-sonnet-4-5-20250929",
prompt: "Hello from TypeScript!",
});
console.log(client.nr.text(res));
console.log(`Cost: $${res.meta.cost ?? "unpriced"}`);
Python
pip install nrouter-sdk
from nroutersdk import nRouter
client = nRouter() # reads NROUTER_API_KEY from env
response = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
print(f"Cost: ${client.last_response.cost}" if client.last_response.cost else "Cost: unpriced")
Java
<dependency>
<groupId>ai.nrouter</groupId>
<artifactId>nrouter-sdk</artifactId>
<version>3.0.0</version>
</dependency>
import ai.nrouter.sdk.NRouter;
import com.openai.client.OpenAIClient;
import com.openai.models.chat.completions.*;
OpenAIClient client = NRouter.create(); // reads NROUTER_API_KEY
ChatCompletion res = client.chat().completions().create(
ChatCompletionCreateParams.builder()
.model("gpt-5.4-mini")
.addMessage(ChatCompletionMessageParam.ofUser(
ChatCompletionUserMessageParam.builder().content("Hello!").build()
))
.build()
);
System.out.println(res.choices().get(0).message().content());
Swift
// Swift Package Manager
.package(url: "https://github.com/nRouterAI/nrouter-sdk.git", from: "3.0.0")
import NRouter
let client = try NRouter() // reads NROUTER_API_KEY
let res = try await client.chatCompletions([
"model": "gpt-5.4-mini",
"messages": [["role": "user", "content": "Hello!"]]
])
print(res.meta.isPriced ? "Cost: $\(res.meta.cost!)" : "Cost: unpriced")
Rust
# Cargo.toml
[dependencies]
nrouter = "2.1.0" # crates.io; 3.0.0 is not yet published there
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
use nrouter::http::Client;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_env()?; // reads NROUTER_API_KEY
let out = client.chat_completions(&json!({
"model": "gpt-5.4-mini",
"messages": [{"role": "user", "content": "Hello from Rust!"}]
})).await?;
println!("Response: {:?}", out.body);
Ok(())
}
Dart / Flutter
# pubspec.yaml
dependencies:
nrouter: ^2.1.1 # pub.dev; 3.0.0 is not yet published there
import 'package:nrouter/nrouter.dart';
final client = NRouter(apiKey: 'sk-nrouter-...');
final result = await client.chatCompletions({
'model': 'gpt-5.4-mini',
'messages': [{'role': 'user', 'content': 'Hello from Dart!'}],
});
print(result.body['choices']);
client.close();
Kotlin
// build.gradle.kts
repositories { mavenCentral() }
dependencies {
implementation("ai.nrouter:nrouter-sdk-kotlin:2.1.0") // latest on Central
}
import ai.nrouter.sdk.NRouter
import org.json.JSONObject
val client = NRouter() // reads NROUTER_API_KEY
val res = client.chatCompletions(
JSONObject()
.put("model", "gpt-5.4-mini")
.put("messages", listOf(mapOf("role" to "user", "content" to "Hello from Kotlin!")))
)
println("Cost: ${res.meta.cost?.let { "$$it" } ?: "unpriced"}")
Android
// app/build.gradle.kts
repositories { mavenCentral() }
dependencies {
implementation("ai.nrouter:nrouter-sdk-android:2.1.0") // latest on Central
}
SDK Ecosystem & Status
Ten branded packages, each pre-configured for nRouter. Every one validates the
sk-nrouter- prefix before any request and points at https://api.nrouter.ai/v1; all
but Dart also resolve NROUTER_API_KEY (Dart requires an explicit key — dart:io does
not exist in a Flutter web build, so an environment fallback would silently resolve to
nothing):
Distribution status is a fact, not an intention. Every row below was read from the registry itself on 2026-09-02, not from an intent to publish. Where a registry serves an older version than this repository's
3.0.0source, the row says which version it actually serves — that is the version that resolves.
| Language | Install | Registry URL | Registry status | Package | Typed errors | x-nr-* metadata |
|---|---|---|---|---|---|---|
| Python | pip install nrouter-sdk |
pypi.org/project/nrouter-sdk | ✅ PUBLISHED | sdks/python/ |
✅ typed wrappers | ✅ client.last_response |
| TypeScript / JS | npm install @nrouter_ai/sdk |
npmjs.com/package/@nrouter_ai/sdk | ✅ PUBLISHED | sdks/js/ |
✅ 9 codes | ✅ 14 headers |
| Java | Maven ai.nrouter:nrouter-sdk |
central.sonatype.com | ✅ PUBLISHED | sdks/java/ |
✅ 9 codes (native HTTP surface) | ✅ 14 headers (native HTTP surface) |
| Kotlin | Maven ai.nrouter:nrouter-sdk-kotlin:2.1.0 |
central.sonatype.com | 🧪 PUBLIC PREVIEW — serves 2.1.0 |
sdks/kotlin/ |
✅ 9 codes | ✅ 14 headers |
| Android | Maven ai.nrouter:nrouter-sdk-android:2.1.0 |
central.sonatype.com | 🧪 PUBLIC PREVIEW — serves 2.1.0 |
sdks/android/ |
✅ 9 codes | ✅ 14 headers |
| Rust | cargo add nrouter@2.1.0 |
crates.io/crates/nrouter | 🧪 PUBLIC PREVIEW — serves 2.1.0 |
sdks/rust/ |
✅ 9 codes | ✅ 14 headers |
| Dart / Flutter | dart pub add nrouter |
pub.dev/packages/nrouter | 🧪 PUBLIC PREVIEW — serves 2.1.1 |
sdks/dart/ |
✅ 9 codes | ✅ 14 headers |
| Swift | SwiftPM, this repo's URL | github.com/nRouterAI/nrouter-sdk | ✅ git tag 3.0.0 |
sdks/swift/ |
✅ 9 codes | ✅ 14 headers |
| R | install.packages("nrouter", repos = c(nrouterai = "https://nrouterai.r-universe.dev", CRAN = "https://cloud.r-project.org")) |
nrouterai.r-universe.dev/nrouter | 🧪 PUBLIC PREVIEW | sdks/r/ |
✅ 9 classed conditions | ✅ 14 headers |
| Go | go get github.com/nRouterAI/nrouter-sdk/sdks/go/v3@v3.0.0 |
pkg.go.dev/github.com/nRouterAI/nrouter-sdk/sdks/go/v3 | ✅ git tag sdks/go/v3.0.0 |
sdks/go/ |
✅ 9 codes | ✅ 14 headers |
Verify any row rather than trusting it:
# Ask each registry what it SERVES, not whether a page exists.
curl -s https://pypi.org/pypi/nrouter-sdk/json | python3 -c 'import sys,json;print("pypi",json.load(sys.stdin)["info"]["version"])'
curl -s https://registry.npmjs.org/@nrouter_ai%2Fsdk | python3 -c 'import sys,json;print("npm",json.load(sys.stdin)["dist-tags"]["latest"])'
# Maven Central. Use repo1 metadata, NOT search.maven.org — its solr index
# returns zero hits for ai.nrouter while the artifacts are demonstrably there,
# so an "empty" search page is an index gap, never proof of an absent release.
for a in nrouter-sdk nrouter-sdk-kotlin nrouter-sdk-android; do
echo -n "maven $a "
curl -s "https://repo1.maven.org/maven2/ai/nrouter/$a/maven-metadata.xml" | grep -o '<release>[^<]*'
done
curl -s -H 'User-Agent: nrouter-check' https://crates.io/api/v1/crates/nrouter | python3 -c 'import sys,json;print("crates.io",json.load(sys.stdin)["crate"]["max_version"])'
curl -s https://pub.dev/api/packages/nrouter | python3 -c 'import sys,json;print("pub.dev",json.load(sys.stdin)["latest"]["version"])'
# Go has no registry: proxy.golang.org serves whatever a git tag points at, and
# it case-encodes the path (each uppercase letter becomes '!' + lowercase).
curl -s https://proxy.golang.org/github.com/n!router!a!i/nrouter-sdk/sdks/go/v3/@v/list
curl -s https://nrouterai.r-universe.dev/src/contrib/PACKAGES | grep -A4 '^Package: nrouter$'
Java keeps its vendor-compatible OpenAI factory and adds a Java 11 native HTTP
surface for all 15 gateway operations, four incremental SSE wires, every
x-nr-* header and nine typed gateway errors.
JavaScript/TypeScript and the seven first-party native transports expose the
same contract. Android delegates those guarantees to Kotlin; Python adds the
same nRouter typing and metadata capture around its vendor client.
Every SDK is held to one contract. conformance/check_conformance.py reads
spec/nrouter-sdk-spec.json and fails if any SDK drifts on
the base URL, the environment variable, the key prefix, a response header, an error code,
one of the 150 route-ownership cells, or one of the four native streaming
helpers per first-party transport.
It needs no toolchains, and its --self-test proves it goes red rather than merely
printing green. See conformance/.
Run the complete local release gate—including all ten language suites, Android lint/AAR assembly, race/clippy/analyzer checks, and conformance mutation proof—with:
scripts/test-all.sh
The same command runs scripts/security-audit.sh and fails on known advisories
across npm, PyPI, Maven/Gradle, Cargo and Dart dependency graphs. Install
osv-scanner and pip-audit.
It also runs scripts/sast.sh, which is a different check and not a
substitute for either direction: security-audit.sh looks for known
vulnerabilities in third-party dependencies, sast.sh runs static analysis over
the code in this repository. It uses semgrep's p/default ruleset:
brew install semgrep # macOS
python3 -m pip install --user semgrep # any platform
Without it the lane is reported SKIPPED and named, never passed, and
scripts/sast.sh run directly exits 78 with that install command rather than
a bare command not found. The three exit states are deliberately distinct —
0 scanned and clean, 1 scanned and found something, 78 never ran — because
semgrep itself uses 0/1, so absence must be neither or "not installed" reads
as "clean". Prove it bites before trusting a green run:
scripts/sast.sh --self-test
That plants a git-tracked file containing a command injection and fails unless the scan reports it. Tracked is deliberate: semgrep scans git-tracked files only, so an untracked probe is skipped and the scan still exits 0.
This is a mirror, not parity. It stands in for the GitHub CodeQL default
setup, which is dormant while Actions is unavailable. Measured over this tree,
p/default is comparable to CodeQL on Python, TypeScript, JavaScript, Java
and Go; it is thin on Kotlin (18 rules) and Swift (2 rules), which nothing
local now replaces; Rust is thin in semgrep but genuinely covered by
cargo clippy -D warnings in the Rust lane. Dart and R are scanned by
nothing — CodeQL never covered them either, so that hole is pre-existing.
CodeQL's actions workflow analysis is not mirrored. scripts/sast.sh
carries the per-language rule counts and how to re-derive them.
Each language is an independent lane, so one absent toolchain no longer blocks
the rest. A lane whose prerequisites are missing is reported SKIPPED, named,
and counted separately — it is never a pass, and the summary says so in as many
words. A lane that FAILS exits the script non-zero. For a release, set
NROUTER_REQUIRE_ALL=1: a lane that did not run is then not evidence, and the
run reports INCOMPLETE and exits non-zero. scripts/test-all.sh --self-test
proves those three exit postures against the same engine the real run uses.
The opt-in live tests are intentionally excluded unless NROUTER_LIVE=1 is
set, because they make billed inference calls.
Publishing is PUBLISHING.md: bump the canonical version and
all manifests together, merge to main, publish the three supported registry
packages, verify every source preview, and create the source tags.
Swift and Go resolve git tags rather than registry uploads. SwiftPM reads
Package.swift from the repository ROOT. That is what
Package.swift here is for — this directory is the root of the
public nrouter-sdk repo, and the manifest uses path: to reach
sdks/swift/, so the Swift sources stay beside the other eight. Consumers use:
.package(url: "https://github.com/nRouterAI/nrouter-sdk.git", from: "3.0.0")
Any Other Language (OpenAI SDK)
base_url → https://api.nrouter.ai/v1
api_key → NROUTER_API_KEY
// Node.js — npm install openai
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.NROUTER_API_KEY,
baseURL: "https://api.nrouter.ai/v1",
});
Supported Endpoints
All endpoints are served by the nRouter gateway at https://api.nrouter.ai/v1, which
routes to the upstream providers. You never call a provider directly and you never need a
provider key. This table is derived from spec/nrouter-sdk-spec.json › supported_endpoints
(Rule #14) — edit the spec first, this table second.
| Endpoint | SDK Method | nRouter Features |
|---|---|---|
/v1/chat/completions |
chat.completions.create() |
Guardrails + Prompts + A/B Testing + Credits |
/v1/completions |
completions.create() |
Credits |
/v1/embeddings |
embeddings.create() |
Credits |
/v1/images/generations |
images.generate() |
Credits |
/v1/audio/speech |
audio.speech.create() |
Credits (TTS) |
/v1/audio/transcriptions |
audio.transcriptions.create() |
Credits (Whisper STT) |
/v1/audio/translations |
audio.translations.create() |
Credits |
/v1/messages |
client.messages.create() |
Anthropic-compatible buffered call; Credits |
/v1/messages/count_tokens |
POST /v1/messages/count_tokens |
Count before spending |
/v1/responses |
responses.create() |
OpenAI Responses API |
/v1/videos |
POST /v1/videos |
Start a video job (billed) |
/v1/videos/{id} |
GET /v1/videos/{id} |
Poll job status (free) |
/v1/videos/{id}/content |
GET /v1/videos/{id}/content |
Download the video (free) |
/v1/models |
models.list() |
Tenant-filtered model list |
/v1/models/{model_id} |
models.retrieve() |
Retrieve one model |
Routing strategies are selected by the model value
Routing strategy is a gateway concern, so there is no separate per-language
strategy API to drift. Put a Smart Router alias in model to activate its
configured strategy and fallback chain; put a concrete model id there to pin
the call to that model. Every runnable hello-world example accepts
NROUTER_MODEL so the same example demonstrates both modes without inventing
client-only routing behavior.
NROUTER_MODEL=my-production-router ./run-your-example # alias: strategy + fallback
NROUTER_MODEL=gpt-5.4-mini ./run-your-example # concrete: pinned
Not Served By The Gateway
spec/nrouter-sdk-spec.json › unsupported_endpoints marks these as never called: files,
fine-tuning, batches, beta/assistants-threads, vector stores, uploads, containers,
conversations, webhooks, image edits, moderations, rerank, OCR. Do not add a client method or
example for any of these without first adding the route to the gateway and the spec.
Examples by Language
SDKs (direct)
| Language | Install | Example |
|---|---|---|
| Python (branded) | pip install nrouter-sdk |
sdks/python/ · examples/python/, notebooks/quickstart.ipynb |
| TypeScript / JS (branded) | npm install @nrouter_ai/sdk |
sdks/js/ · examples/typescript/quickstart.ts, examples/javascript/quickstart.js |
| Java (branded) | ai.nrouter:nrouter-sdk |
sdks/java/ · examples/java/quickstart.java |
| Kotlin (branded) | Maven ai.nrouter:nrouter-sdk-kotlin:2.1.0 |
sdks/kotlin/ · examples/kotlin/quickstart.kt |
| Android (branded) | Maven ai.nrouter:nrouter-sdk-android:2.1.0 |
sdks/android/ |
| Rust (branded) | cargo add nrouter@2.1.0 |
sdks/rust/ · examples/rust/quickstart.rs |
| Dart / Flutter (branded) | dart pub add nrouter |
sdks/dart/ · examples/dart/quickstart.dart |
| R (branded) | install.packages("nrouter", repos = c(nrouterai = "https://nrouterai.r-universe.dev", CRAN = "https://cloud.r-project.org")) |
sdks/r/ · examples/r/quickstart.R |
| Node.js / TypeScript (plain openai) | npm install openai |
examples/typescript/node.ts |
| Go | go get github.com/nRouterAI/nrouter-sdk/sdks/go/v3@v3.0.0, or plain openai-go |
examples/go/quickstart.go |
| Java (plain openai-java) | com.openai:openai-java |
examples/java/quickstart.java |
| Ruby | gem install ruby-openai |
examples/ruby/quickstart.rb |
| PHP | composer require openai-php/client |
examples/php/quickstart.php |
| C# / .NET | dotnet add package OpenAI |
examples/dotnet/quickstart.cs |
| cURL | Built-in | examples/curl/quickstart.sh |
Every language under examples/ holds standalone, runnable starter scripts and framework integrations.
AI Frameworks
| Framework | Install | Example | What Changes |
|---|---|---|---|
| LangChain | pip install langchain-openai |
examples/python/frameworks/langchain.py |
base_url + api_key on ChatOpenAI |
| LlamaIndex | pip install llama-index-llms-openai |
examples/python/frameworks/llamaindex.py |
api_base + api_key on OpenAI |
| Vercel AI SDK | npm install ai @ai-sdk/openai |
examples/typescript/vercel_ai.ts |
baseURL on createOpenAI() |
| CrewAI | pip install crewai |
examples/python/frameworks/crewai.py |
OPENAI_API_BASE env var |
| AutoGen | pip install autogen-agentchat |
examples/python/frameworks/autogen.py |
base_url in config_list |
Every framework that supports OpenAI-compatible endpoints works with nRouter. Set base_url to https://api.nrouter.ai/v1 and api_key to your NROUTER_API_KEY. That's it.
Response Headers
The gateway's public x-nr-* response headers, and what each one means. Most
are conditional; x-nr-request-id is the one present on every response. The
authoritative set is
spec/gateway-response-headers.json,
derived from the gateway and held against all ten SDKs by
conformance/check_conformance.py — this table describes the headers, and is
not itself the register of which ones exist.
| Header | Type | Description |
|---|---|---|
x-nr-request-id |
string | Unique request ID (always present) |
x-nr-request-cost |
float | Exact cost in USD; absent when the model is unpriced |
x-nr-cost-status |
string | exact or unpriced when cost metadata is available |
x-nr-model |
string | Model that served the request |
x-nr-input-tokens |
integer | Input token count |
x-nr-output-tokens |
integer | Output token count |
x-nr-total-tokens |
integer | Total token count, including cache tokens |
x-nr-cache-read-tokens |
integer | Cache-read tokens; emitted only when nonzero |
x-nr-cache-write-tokens |
integer | Cache-write tokens; emitted only when nonzero |
x-nr-limit-source |
string | key, plan, team, user, or budget on 429 responses |
x-nr-budget-warning |
string | A soft budget you configured was crossed by this request, which still served; <scope> soft_budget <spend>/<ceiling>, e.g. org soft_budget 80.00/100.00 |
x-nr-guardrails |
string | Pre-call guardrail posture; absent means the response makes no guardrail claim, never none, which is an explicit token |
x-nr-auth-reason |
string | On a 401, the gateway's stable reason for refusing the key |
x-nr-response-cache |
string | hit or miss; absent when the response cache did not participate |
x-nr-response-cache-age |
integer | Age of a cache hit in seconds |
Python SDK captures these automatically in client.last_response. Other languages read them from HTTP response headers.
Structure
This is the standalone public nRouterAI/nrouter-sdk repository:
nrouter-sdk/
├── README.md ← You are here (single reference for all)
├── LANGUAGES.md ← every-language guide (any OpenAI-format client)
├── spec/nrouter-sdk-spec.json ← Source of truth (headers, errors, endpoints, Rule #14)
├── sdks/
│ ├── python/ ← Branded SDK → pip install nrouter-sdk
│ ├── js/ ← Branded SDK → npm install @nrouter_ai/sdk
│ ├── java/ ← Branded SDK → Maven ai.nrouter:nrouter-sdk
│ ├── kotlin/ ← Branded SDK → Maven ai.nrouter:nrouter-sdk-kotlin
│ ├── android/ ← Branded SDK → Maven ai.nrouter:nrouter-sdk-android
│ ├── swift/ ← SwiftPM package from the root git tag
│ ├── rust/ ← Branded SDK → crates.io nrouter
│ ├── dart/ ← Branded SDK → pub.dev nrouter
│ ├── go/ ← Branded SDK → tagged Go module
│ └── r/ ← Branded SDK → R-universe public preview
└── examples/
├── curl.sh ← cURL
├── node.ts ← Node.js / TypeScript (plain openai)
├── go.go ← Go (plain openai-go; sdks/go/ is branded)
├── java.java ← Java (plain openai-java)
├── ruby.rb ← Ruby
├── php.php ← PHP
├── dotnet.cs ← C# / .NET
├── langchain.py ← LangChain
├── llamaindex.py ← LlamaIndex
├── vercel_ai.ts ← Vercel AI SDK
├── crewai.py ← CrewAI
├── autogen.py ← AutoGen
└── hello-world/ ← one minimal script per non-Python branded SDK
├── typescript.ts
├── javascript.js
├── java.java
├── rust.rs
└── r.R
This is the single reference for all SDK/examples. The playground code generation and docs site pull from these examples.
Documentation
Every link below was checked live before it was written here; none is derived from the sitemap alone, because a URL can sit in a sitemap and still 404.
How each capability works, and where it is enforced — all of these are gateway-side, so they behave identically from every SDK in this repository:
| Capability | Guide | Product page |
|---|---|---|
| Guardrails — PII redaction, injection protection, pre- and post-call | docs/guides/guardrails | product/guardrails |
| Budgets and spend limits, per key / team / org | docs/guides/budget-controls | product/budgets |
| Routing, fallback chains, failover | docs/guides/router-settings | product/routing |
| Observability and cost tracking | docs/guides/observability | product/observability |
| Prompt templates and versioning | docs/guides/prompts | — |
| API keys — creation, rotation, scope | docs/guides/api-key-management | — |
None of this lives in the SDK. It is configured in the dashboard and
enforced at the gateway on the request path, so whatever you have enabled
applies to a raw curl exactly as it does to a branded SDK, and no client can
bypass it. That is the reason a thin client is the right shape here.
⚠️ Two things are conditional, and assuming otherwise is how you rely on protection you do not have:
- Which guardrails run is resolved per request. The organization's guardrail switch gates everything; below it the narrowest applicable assignment wins across key > team > org > default, and a winner disabled at that scope does not run. A guardrail you configured is not necessarily a guardrail this request gets — check the assignment, not just the switch.
- Routing is opt-in by what you put in
model, and applies to text wires only. An alias gets its strategy and fallback chain; a concrete model is never re-routed and inherits no hidden platform fallback. Audio, image and video take a single-provider route and are not cross-provider Smart Router wires.
Cost accounting covers every BILLABLE call. Some routes these SDKs expose are
deliberately free and emit no x-nr-request-cost at all —
/v1/messages/count_tokens, and video polling and content retrieval — because
they generate no completion. Absent is not zero (Rule #28): a missing cost
header means unpriced or free, never a $0 inference.
Per-language quickstarts: Python · Node.js / TypeScript · Go · Java · PHP · Ruby · curl · OpenAI SDK against nRouter
Reference: Quick start · API reference · Live model catalogue · Pricing · Changelog
Contributing, security, and support
| CONTRIBUTING.md | How to build and test each SDK, and the PR checklist. Read the version-field warning before you open a PR — merging main publishes immutably. |
| SECURITY.md | Report a vulnerability privately to security@nrouter.ai, never as an issue. Supported version lines per registry. |
| SUPPORT.md | SDK bugs go to issues; account, billing, and API-key questions go to support@nrouter.ai. |
| CODE_OF_CONDUCT.md | Adapted from the Contributor Covenant 2.1. |
| CHANGELOG.md | Coordinated release history for the shared SDK version train. |
| PUBLISHING.md | How a release actually ships, and which credential each registry needs. |
npm builds 1.1.1 and later carry provenance attestations
tying the tarball to the exact commit and workflow that produced it — verify
with npm audit signatures. 1.0.0 and 1.1.0 were published by hand and have
none; provenance cannot be added to a version after the fact. See
SECURITY.md.