DEV Community

nRouter
nRouter

Posted on

The Python SDK guide I wish more vendors wrote: notes on nRouter's

Disclosure: I work on nRouter. This post was drafted with AI assistance and reviewed by me before publishing.

Most SDK guides stop at "install it, call it, good luck." The parts that decide whether an integration survives production — what comes back with the response, how streaming behaves, what a failure looks like — get left for you to discover at 2 a.m. So I went through our own Python SDK guide the way I'd read a vendor's: looking for the operational details. Here's what it actually specifies.

Install, and one naming trap

pip install nrouter-sdk
Enter fullscreen mode Exit fullscreen mode

The trap: the package installs as nrouter-sdk, but you import it as nroutersdk — no hyphen. The client class is nRouter, and it reads your key from the environment:

export NROUTER_API_KEY="sk-nrouter-xxxxxxxxxxxx"
Enter fullscreen mode Exit fullscreen mode

No key handling in code, no config object to thread through your app. The client targets https://api.nrouter.ai/v1 by default.

The first call hands you the receipt

from nroutersdk import nRouter  # Reads NROUTER_API_KEY and targets https://api.nrouter.ai/v1

with nRouter() as client:
    response = client.chat.completions.create(
        model="gpt-5.4-mini",
        messages=[{"role": "user", "content": "Hello, nRouter!"}],
    )
    print(response.choices[0].message.content)

    if client.last_response:
        print(f"Request ID: {client.last_response.request_id}")
Enter fullscreen mode Exit fullscreen mode

That last_response handle is the detail I'd steal for any client library. The gateway parses its own x-nr-* response headers and exposes the metadata on the client — request ID, cost figure, cache status — instead of leaving you to dig through raw headers when support asks "which request was that?" Anyone who has grepped logs for a request ID that was never logged knows why this matters.

The client is fully wire-compatible with both OpenAI Chat Completions and the native Anthropic Messages format, so the model string in that call is the only real decision on the page.

Streaming is a loop, not a callback maze

with nRouter() as client:
    stream = client.chat.completions.create(
        model="gpt-5.4-mini",
        messages=[{"role": "user", "content": "Summarize distributed consensus."}],
        stream=True,
    )
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            print(delta, end="", flush=True)
Enter fullscreen mode Exit fullscreen mode

An iterator of chunks with a delta you print as it arrives. No event emitter, no handler registration. In a notebook or a CLI tool this is exactly the shape you want.

Errors are typed, and the order matters

This is the section most SDK docs skip entirely. The guide gives four exception types, and the handling order in its example is specific-before-generic:

from nroutersdk import (
    nRouter,
    nRouterError,
    nRouterAuthenticationError,
    nRouterRateLimitError,
    nRouterGuardrailBlockedError,
)

try:
    # ... a chat completion call ...
    pass
except nRouterGuardrailBlockedError as e:
    print(f"Blocked: {e.message}")
except nRouterRateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except nRouterAuthenticationError as e:
    print(f"Authentication failed: {e.message}")
except nRouterError as e:
    print(f"nRouter API error [{e.status_code}]: {e.message}")
Enter fullscreen mode Exit fullscreen mode

Three things worth noticing. A guardrail block is its own type, so policy rejections never get mistaken for outages and retried. The rate-limit error carries retry_after, so your backoff reads a number instead of parsing a header. And the generic nRouterError still gives you status_code and message, so the fallthrough case is debuggable rather than a bare exception. The guide frames this as recovering cleanly from network timeouts, quota limits and guardrail redactions — typed errors are how that stops being a slogan.

The guide

Full page, with the complete examples: https://dev.nrouter.ai/docs/sdks/python

If you write Python against LLM APIs: what does your current client hand back after a call — and how many of its failures can you actually tell apart in code? That's the gap this guide is aiming at, and I'm curious how other SDKs compare.

Top comments (0)