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 publishToMavenLocalAdd 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-androidpackage, 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")
}
}| Class | HTTP Status | Description |
|---|---|---|
NRouterError.Request | 400 | Malformed request or validation error |
NRouterError.GuardrailBlocked | 400 | Request blocked by guardrail checks |
NRouterError.Authentication | 401 | Invalid or revoked virtual API key |
NRouterError.Credit | 402 | Account balance exhausted or ceiling exceeded |
NRouterError.NotFound | 404 | Unknown model or disabled deployment |
NRouterError.RateLimit | 429 | Rate limit or token quota exceeded |
NRouterError.Service | 503 | Upstream service or gateway error |
NRouterError.Transport | — | Network 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: