nRouter Python SDK
The official Python client library for the nRouter LLM gateway.
One API key for models across six provider clouds — OpenAI, Anthropic, AWS Bedrock, Google Vertex AI, Azure Foundry, and Alibaba Cloud. Features built-in automatic cost tracking (x-nr-* metadata capture), guardrails, response caching, prompt templates, and typed exception handling.
Installation
pip install nrouter-sdk
Authentication & Environment Variables
The SDK automatically resolves its configuration from environment variables.
1. Set your environment variables
# Required: Get your API key from https://nrouter.ai/dashboard/keys
export NROUTER_API_KEY="sk-nrouter-your-api-key-here"
# Optional: Override base URL (e.g. for staging or local proxy)
export NROUTER_BASE_URL="https://api.nrouter.ai/v1"
2. Configuration Matrix
| Environment Variable | Parameter Name | Default Value | Description |
|---|---|---|---|
NROUTER_API_KEY |
api_key |
None (Required) | API Key starting with sk-nrouter- |
NROUTER_BASE_URL |
base_url |
https://api.nrouter.ai/v1 |
nRouter Gateway Base URL |
You can also pass parameters explicitly in code:
from nroutersdk import nRouter
client = nRouter(
api_key="sk-nrouter-your-api-key-here",
base_url="https://api.nrouter.ai/v1",
)
Quick Start
Synchronous Client (nRouter)
from nroutersdk import nRouter
# Uses context manager to ensure clean connection pool shutdown
with nRouter() as client:
response = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello from Python!"},
],
)
# Output completion text
print(response.choices[0].message.content)
# Cost and Request Metadata (captured automatically from x-nr-* headers)
meta = client.last_response
print(f"Request ID: {meta.request_id}")
print(f"Model: {meta.model}")
if meta.cost is not None:
print(f"Cost: ${meta.cost:.6f}")
else:
print(f"Cost: unpriced ({meta.cost_status})")
Asynchronous Client (AsyncnRouter)
import asyncio
from nroutersdk import AsyncnRouter
async def main():
async with AsyncnRouter() as client:
response = await client.chat.completions.create(
model="gpt-5.4-mini",
messages=[{"role": "user", "content": "Explain quantum computing in one sentence."}],
)
print(response.choices[0].message.content)
print(f"Cost: ${client.last_response.cost}")
asyncio.run(main())
Core Capabilities & Examples
| Example Script | Topic | Description |
|---|---|---|
01_quickstart.py |
Quickstart | Basic chat completion & metadata extraction |
02_async_concurrency.py |
Async / Concurrency | Concurrent requests with asyncio.gather |
03_streaming.py |
Streaming | Server-Sent Events (SSE) token streaming |
04_anthropic_messages.py |
Anthropic Messages | Native Messages format & token counting |
05_metadata_cost_tracking.py |
Metadata & Cost | Deep dive into x-nr-* headers |
06_prompt_templates.py |
Prompt Templates | Server-side prompt templates & variables |
07_tool_calling.py |
Tool Calling | Function calling with JSON schema tools |
08_structured_outputs.py |
Structured Outputs | Strict JSON object output formatting |
09_error_handling.py |
Error Handling | Typed error handling & guardrail recovery |
10_conversation_memory.py |
Memory | Multi-turn memory without header leaks |
11_embeddings.py |
Embeddings | Vector embeddings generation |
12_multimodal_vision.py |
Multimodal Vision | Image inputs via URL or base64 |
📓 Interactive Jupyter Notebook
Try the SDK interactively with notebooks/quickstart.ipynb.
1. Streaming Responses
from nroutersdk import nRouter
with nRouter() as client:
stream = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[{"role": "user", "content": "Write a short poem about routers."}],
stream=True,
)
for chunk in stream:
if chunk.choices:
content = chunk.choices[0].delta.content
if content:
print(content, end="", flush=True)
print()
2. Anthropic Messages API
Use Anthropic's Messages format directly on the exact same sk-nrouter- API key:
from nroutersdk import nRouter
with nRouter() as client:
# Pre-call token counting (Free route)
count = client.messages.count_tokens(
model="anthropic/claude-sonnet-4-5-20250929",
messages=[{"role": "user", "content": "Hello!"}],
)
print(f"Token count: {count['input_tokens']}")
# Create message
message = client.messages.create(
model="anthropic/claude-sonnet-4-5-20250929",
messages=[{"role": "user", "content": "Hello via Anthropic Messages!"}],
max_tokens=256,
)
print(message["content"][0]["text"])
3. Function & Tool Calling
Define function schemas and let the model decide when to invoke external tools:
import json
from nroutersdk import nRouter
tools = [
{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "Get the current stock price for a symbol",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker symbol, e.g. AAPL"},
},
"required": ["symbol"],
},
},
}
]
with nRouter() as client:
response = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[{"role": "user", "content": "What is the stock price of AAPL?"}],
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
if msg.tool_calls:
for tool_call in msg.tool_calls:
print(f"Tool called: {tool_call.function.name} with args: {tool_call.function.arguments}")
4. Structured JSON Outputs
Enforce typed JSON object generation using response_format:
import json
from nroutersdk import nRouter
with nRouter() as client:
response = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": "Extract data into JSON with keys: name, title, company."},
{"role": "user", "content": "Sarah Connor is the Lead Security Engineer at Cyberdyne."}
],
response_format={"type": "json_object"},
)
data = json.loads(response.choices[0].message.content)
print(data) # {'name': 'Sarah Connor', 'title': 'Lead Security Engineer', 'company': 'Cyberdyne'}
5. Response Metadata & Cost Tracking
The gateway emits canonical x-nr-* headers with every response. client.last_response captures these automatically:
meta = client.last_response
print("Request ID: ", meta.request_id)
print("Exact Cost (USD): ", meta.cost)
print("Cost Status: ", meta.cost_status) # "exact" or "unpriced"
print("Served Model: ", meta.model)
print("Input Tokens: ", meta.input_tokens)
print("Output Tokens: ", meta.output_tokens)
print("Total Tokens: ", meta.total_tokens)
print("Cache Read Tokens: ", meta.cache_read_tokens)
print("Cache Write Tokens:", meta.cache_write_tokens)
print("Gateway Cache: ", meta.response_cache) # "hit", "miss", or None
print("Cache Age (s): ", meta.response_cache_age) # seconds on hits
print("Limit Source: ", meta.limit_source) # "key", "team", "org", or None
print("Budget Warning: ", meta.budget_warning) # "org soft_budget 80.00/100.00" when a soft budget was crossed
Note on Cost Accuracy: Unpriced models return
cost=Noneandcost_status="unpriced". Never treatNoneas$0.00— free routes (like/v1/messages/count_tokens) emit no cost header, while billable inferences always track usage.
Error Handling
All gateway refusals are converted into typed exceptions:
from nroutersdk import (
nRouter,
nRouterGuardrailBlockedError,
nRouterCreditError,
nRouterRateLimitError,
nRouterAuthenticationError,
nRouterNotFoundError,
nRouterRequestError,
nRouterServiceError,
nRouterError,
)
with nRouter() as client:
try:
response = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[{"role": "user", "content": "Analyze confidential data..."}],
)
except nRouterGuardrailBlockedError as e:
print(f"Blocked by guardrail: {e}")
except nRouterCreditError as e:
print(f"Insufficient credits: {e}")
except nRouterRateLimitError as e:
print(f"Rate limit exceeded: {e}")
except nRouterAuthenticationError as e:
print(f"Invalid API key: {e}")
except nRouterNotFoundError as e:
print(f"Model not found: {e}")
except nRouterServiceError as e:
print(f"Gateway service unavailable: {e}")
except nRouterError as e:
print(f"General nRouter error: {e}")
| Exception | HTTP Status | Meaning |
|---|---|---|
nRouterRequestError |
400 | Malformed request body or parameters |
nRouterGuardrailBlockedError |
400 | Content blocked by pre-call / post-call guardrail |
nRouterAuthenticationError |
401 | Invalid or missing sk-nrouter- API key |
nRouterCreditError |
402 | Account has insufficient credits |
nRouterBudgetExceededError |
402 / 429 | Key, team, or org spend ceiling exceeded |
nRouterNotFoundError |
404 | Model or endpoint not found |
nRouterRateLimitError |
429 | RPM or TPM rate limit exceeded |
nRouterServiceError |
503 | Gateway or upstream provider temporary outage |
Enterprise Configuration & Proxies
Custom Proxy, SSL Certificates & Timeouts
Pass custom httpx2.Client or timeout configurations for corporate networks:
import httpx2 as httpx
from nroutersdk import nRouter
# Configure proxy, custom SSL context, and timeouts
http_client = httpx.Client(
proxy="http://proxy.corporate.internal:8080",
verify="/path/to/corporate-ca.crt",
timeout=httpx.Timeout(60.0, connect=10.0),
)
with nRouter(http_client=http_client, max_retries=3) as client:
response = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[{"role": "user", "content": "Hello behind enterprise proxy!"}],
)
Supported Endpoints
All endpoints route through https://api.nrouter.ai/v1:
| Endpoint | Method | Description |
|---|---|---|
/v1/chat/completions |
client.chat.completions.create() |
OpenAI-compatible chat |
/v1/messages |
client.messages.create() |
Anthropic-compatible Messages |
/v1/messages/count_tokens |
client.messages.count_tokens() |
Pre-call token counting |
/v1/completions |
client.completions.create() |
Legacy text completions |
/v1/embeddings |
client.embeddings.create() |
Text embeddings |
/v1/images/generations |
client.images.generate() |
Image generation |
/v1/audio/speech |
client.audio.speech.create() |
Text-to-Speech (TTS) |
/v1/audio/transcriptions |
client.audio.transcriptions.create() |
Speech-to-Text (STT) |
/v1/audio/translations |
client.audio.translations.create() |
Audio translations |
/v1/responses |
client.responses.create() |
OpenAI Responses API |
/v1/models |
client.models.list() |
Organization model catalog |
/v1/videos |
client.videos.create() |
Video generation jobs |
Documentation & Resources
Metadata
Release files for nrouter-sdk 3.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nrouter_sdk-3.0.0.tar.gz | 56.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nrouter_sdk-3.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 93.5 kB
Release files / nrouter_sdk-3.0.0.tar.gz
| Download URL | nrouter_sdk-3.0.0.tar.gz |
|---|---|
| Size | 56.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7162b96fb85015aa3b979dd70e89402c3b265cdd247fd9ecfe0a1c0e06f5dc3c
|
|
BLAKE2b-256 checksum How to use checksums |
aa41319fa217e11919299359ff778e075a78bf73e1ce1f8448ae17777414b4b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.21
|
Release files / nrouter_sdk-3.0.0-py3-none-any.whl
| Download URL | nrouter_sdk-3.0.0-py3-none-any.whl |
|---|---|
| Size | 37.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
69e12396ae141f78d92216ee23b1896e9b97522113009d1fa0606ccba8e6a55d
|
|
BLAKE2b-256 checksum How to use checksums |
c10506b0b65d114be56e46d4c2b877ad7214a42e1f5235ed35e59abc7c6f7844
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.21
|