Skip to main content

nRouter Python SDK

PyPI Python Versions CI PyPI publish License: MIT

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="anthropic/claude-sonnet-4-5-20250929",
        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="anthropic/claude-sonnet-4-5-20250929",
            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="anthropic/claude-sonnet-4-5-20250929",
        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="anthropic/claude-sonnet-4-5-20250929",
        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="anthropic/claude-sonnet-4-5-20250929",
        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

Note on Cost Accuracy: Unpriced models return cost=None and cost_status="unpriced". Never treat None as $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="claude-sonnet-4-5-20250929",
            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="anthropic/claude-sonnet-4-5-20250929",
        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 2.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nrouter-sdk 2.2.1
File Size Uploaded
nrouter_sdk-2.2.1.tar.gz 30.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nrouter-sdk 2.2.1
File Interpreter ABI Platform
nrouter_sdk-2.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 55.0 kB

Release files / nrouter_sdk-2.2.1.tar.gz

Download URL nrouter_sdk-2.2.1.tar.gz
Size 30.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f130d477b7804b84e30888f9866190425f44034fcf414cbd63a1c96f081943d1
BLAKE2b-256 checksum
How to use checksums
b08d341e3c48b426bb6d95575da8b2f13df37a16b3559eef0b222e6fe5e5b9e2
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-2.2.1-py3-none-any.whl

Download URL nrouter_sdk-2.2.1-py3-none-any.whl
Size 24.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
edd67426d0afa83a2384b69590154878958306ed421533e7aa76f270f85233de
BLAKE2b-256 checksum
How to use checksums
a01084a210530285876fe7fc3060573768cba0457b03c5ea34bfc57576f9e76c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.21

Release history Release notifications | RSS feed

3.1.3

2 release files

3.1.2

2 release files

3.1.1

2 release files

3.1.0

2 release files

3.0.0

2 release files

This release

2.2.1 This release

2 release files

2.2.0

2 release files

2.1.3

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page