Browse documentation

Kotlin SDK

The official nRouter Kotlin SDK with coroutines support, cold Flow streaming, typed errors, and automatic response metadata extraction.

Last updated

The official nRouter Kotlin SDK (ai.nrouter:nrouter-sdk-kotlin) provides native Kotlin coroutines support for JVM and Android applications. Calling https://api.nrouter.ai/v1, it handles standard OpenAI-compatible requests, manages streaming via reactive Kotlin Flows, and exposes typed error classes and response metadata.

Source Installation

Kotlin is currently a source preview rather than a Maven Central release. From a local clone of the SDK repository, compile and publish the artifact to your local Maven cache:

cd sdks/kotlin
./gradlew clean check publishToMavenLocal

Add the dependency to your project's build.gradle.kts:

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation("ai.nrouter:nrouter-sdk-kotlin:3.1.2")
}

Android Note: For Android applications, use the nrouter-sdk-android package, which wraps this client with Android lifecycle and main-thread safety.

Setup & Authentication

Configure your virtual API key in your environment:

export NROUTER_API_KEY="sk-nrouter-your-key-here"

The client defaults to https://api.nrouter.ai/v1:

import ai.nrouter.sdk.NRouter

// Reads NROUTER_API_KEY from the environment
val client = NRouter()

You can also pass explicit credentials or custom network configuration:

val client = NRouter(
    apiKey = "sk-nrouter-your-key-here",
    baseURL = "https://api.nrouter.ai/v1",
)

Quickstart

Execute non-blocking chat completions with standard JSON payloads:

import ai.nrouter.sdk.NRouter
import org.json.JSONObject

suspend fun main() {
    val client = NRouter()

    val result = client.chatCompletions(
        JSONObject()
            .put("model", "gpt-5.4-mini")
            .put("messages", listOf(mapOf("role" to "user", "content" to "Hello!")))
    )

    val content = result.body
        .getJSONArray("choices")
        .getJSONObject(0)
        .getJSONObject("message")
        .getString("content")

    println(content)

    // Inspect metadata
    val meta = result.meta
    println("Request ID: ${meta.requestId}")
    if (meta.isPriced) {
        println("Cost (USD): $${meta.cost}")
    }
}

SDK methods are suspend functions and automatically switch to Dispatchers.IO internally.

Streaming

The SDK exposes cold, cancellable Flow streams for real-time token delivery:

import ai.nrouter.sdk.NRouter
import kotlinx.coroutines.flow.collect
import org.json.JSONObject

suspend fun streamCompletion(client: NRouter) {
    client.messagesStream(
        JSONObject()
            .put("model", "claude-haiku-4-5-20251001")
            .put("max_tokens", 256)
            .put("messages", listOf(mapOf("role" to "user", "content" to "Hello!")))
    ).collect { chunk ->
        print(chunk.delta)
    }
}

Cancelling the collection job cancels the underlying OkHttp call immediately.

Error Handling

Every refusal maps to a typed subclass of NRouterError:

import ai.nrouter.sdk.NRouterError

try {
    val response = client.chatCompletions(body)
} catch (e: NRouterError.GuardrailBlocked) {
    // Guardrail policy blocked the request
    println("Request blocked: ${e.message}")
} catch (e: NRouterError.Credit) {
    // Insufficient credits or budget ceiling reached
    println("Billing limit reached: ${e.message}")
} catch (e: NRouterError.RateLimit) {
    // Rate limit or TPM quota exceeded
    if (e.isRetryable) {
        println("Throttled; retryable with backoff")
    }
}
ClassHTTP StatusDescription
NRouterError.Request400Malformed request or validation error
NRouterError.GuardrailBlocked400Request blocked by guardrail checks
NRouterError.Authentication401Invalid or revoked virtual API key
NRouterError.Credit402Account balance exhausted or ceiling exceeded
NRouterError.NotFound404Unknown model or disabled deployment
NRouterError.RateLimit429Rate limit or token quota exceeded
NRouterError.Service503Upstream service or gateway error
NRouterError.TransportNetwork transport error

isRetryable evaluates to true for RateLimit, Service, and Transport errors.

Response Metadata & Cost Tracking

Every response provides parsed x-nr-* gateway headers via result.meta:

val meta = result.meta
println("Request ID: ${meta.requestId}")
println("Latency: ${meta.latencyMs}ms")
println("Tokens: ${meta.inputTokens} prompt / ${meta.outputTokens} completion")

if (meta.isPriced) {
    println("Cost: $${meta.cost}")
} else {
    println("Cost: unpriced")
}

Source & Repository

The source code and runnable demonstrations are available on GitHub:

Was this page helpful?