# How the Ktor HTTP Client is Configured in the ElevenLabs SDK for API Calls

> Discover how the ElevenLabs SDK configures the Ktor HTTP client for API calls. Learn about platform specific engines and shared settings for efficient development.

- Repository: [Ko Shin/muse](https://github.com/kkoshin/muse)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The ElevenLabs SDK configures a multiplatform `HttpClient` using Ktor's `expect`/`actual` pattern, with platform-specific engines (Darwin for iOS, OkHttp for Android) that share logging, JSON serialization, and default request settings across all API calls.**

The `kkoshin/muse` repository implements a Kotlin Multiplatform SDK for the ElevenLabs API, where all network operations route through a centrally configured Ktor HTTP client. This architecture ensures consistent request handling, response parsing, and authentication regardless of whether the code runs on iOS or Android.

## Multiplatform Client Architecture

At the core of the SDK, [`KtorClient.kt`](https://github.com/kkoshin/muse/blob/main/KtorClient.kt) in the common source set declares an `expect` property that each platform must fulfill:

```kotlin
// elevenlabs/src/commonMain/kotlin/io/github/kkoshin/elevenlabs/KtorClient.kt
expect val ktorClient: HttpClient

```

This `ktorClient` serves as the single transport layer consumed by `ElevenLabsClient` for all API interactions. The **expect/actual** mechanism allows the SDK to define a unified interface while delegating engine-specific implementations to platform source sets.

## iOS Configuration with the Darwin Engine

The iOS implementation resides in [`KtorClient.ios.kt`](https://github.com/kkoshin/muse/blob/main/KtorClient.ios.kt) and configures a native `Darwin` engine with comprehensive logging and serialization plugins:

### Network Engine and Logging

The client uses the `Darwin` engine and installs the `Logging` plugin at `LogLevel.ALL`, routing messages to `NSLog` with a custom prefix:

```kotlin
// elevenlabs/src/iosMain/kotlin/io/github/kkoshin/elevenlabs/KtorClient.ios.kt
actual val ktorClient = HttpClient(Darwin) {
    install(Logging) {
        level = LogLevel.ALL
        logger = object : Logger {
            override fun log(message: String) {
                NSLog("Network/Ktor: $message")
            }
        }
    }
}

```

### Serialization and Default Requests

Both platforms share identical JSON and request configurations. The iOS client installs `ContentNegotiation` with a `kotlinx.serialization.json.Json` instance configured with `ignoreUnknownKeys = true` and `explicitNulls = false`. The `defaultRequest` block sets `Content-Type: application/json` and prefixes all URLs with `BASE_URL = "https://api.elevenlabs.io/v1/"`:

```kotlin
install(ContentNegotiation) {
    json(Json {
        ignoreUnknownKeys = true
        explicitNulls = false
    })
}

install(Resources)

defaultRequest {
    header(HttpHeaders.ContentType, ContentType.Application.Json)
    url(BASE_URL)
}

```

## Android Configuration with the OkHttp Engine

The Android implementation in [`KtorClient.android.kt`](https://github.com/kkoshin/muse/blob/main/KtorClient.android.kt) mirrors the iOS configuration but substitutes the `OkHttp` engine and routes logs through Android's native `Log.d`:

```kotlin
// elevenlabs/src/androidMain/kotlin/io/github/kkoshin/elevenlabs/KtorClient.android.kt
actual val ktorClient = HttpClient(OkHttp) {
    install(Logging) {
        level = LogLevel.ALL
        logger = object : Logger {
            override fun log(message: String) {
                Log.d("Network", message)
            }
        }
    }
    
    // ContentNegotiation, Resources, and defaultRequest 
    // identical to iOS implementation
}

```

This parity ensures that JSON serialization settings, base URL constants, and resource routing remain consistent across platforms, while each utilizes its native transport mechanism for optimal performance.

## Consuming the Client in ElevenLabsClient

The `ElevenLabsClient` class consumes this pre-configured `ktorClient` and automatically injects authentication headers. Every request receives the `xi-api-key` header via a custom request pipeline:

```kotlin
// elevenlabs/src/commonMain/kotlin/io/github/kkoshin/elevenlabs/ElevenLabsClient.kt
class ElevenLabsClient(private val apiKey: String) {
    // Uses the shared ktorClient with automatic auth
}

```

### Performing API Calls

The configured client supports type-safe resource routes enabled by the **Resources** plugin. For example, listing available voices uses the generated `Voices` resource class:

```kotlin
suspend fun listVoices(): Result<List<Voice>> {
    return elevenLabsClient.get<Voices, List<Voice>>(Voices())
}

```

For text-to-speech synthesis, the client serializes the request body using the shared JSON configuration:

```kotlin
suspend fun synthesize(text: String, voiceId: String): Result<TtsResponse> {
    val request = TextToSpeechRequest(text = text, voice = voiceId)
    return elevenLabsClient.post<TextToSpeechRequest, TtsResponse, TextToSpeech>(
        TextToSpeech(voiceId), 
        request
    )
}

```

Multipart uploads, such as voice verification, leverage the same client instance with platform-specific engine capabilities:

```kotlin
suspend fun uploadVerification(filePath: String): Result<VerificationResponse> {
    return elevenLabsClient.postForm<VerificationResponse>(Verification()) {
        append("audio_file", File(filePath).readBytes(), Headers.build {
            append(HttpHeaders.ContentType, "audio/wav")
        })
    }
}

```

## Summary

- The SDK declares an **`expect val ktorClient: HttpClient`** in the common source, implemented separately for iOS and Android.
- **iOS** uses the `Darwin` engine with `NSLog` logging, while **Android** uses `OkHttp` with Android's `Log.d`.
- Both platforms share identical configurations: `BASE_URL = "https://api.elevenlabs.io/v1/"`, JSON serialization with `ignoreUnknownKeys = true`, and automatic `Content-Type: application/json` headers.
- The **Resources** plugin enables type-safe API routes, and the **Logging** plugin captures all network traffic at `LogLevel.ALL`.
- `ElevenLabsClient` automatically adds the `xi-api-key` header to every request made through the shared client.

## Frequently Asked Questions

### What HTTP engine does the ElevenLabs SDK use on iOS?

According to the source code in [`KtorClient.ios.kt`](https://github.com/kkoshin/muse/blob/main/KtorClient.ios.kt), the iOS implementation uses the **`Darwin`** engine, which provides native URLSession-based networking on Apple platforms. This engine is preferred for iOS as it respects system proxy settings and TLS configurations.

### How is the ElevenLabs API key added to requests?

The `ElevenLabsClient` class injects the API key via a `headers` block that appends `xi-api-key` to every outgoing request. This happens automatically when using the client wrapper, ensuring developers don't need to manually configure authentication headers for each API call.

### What JSON serialization settings are configured in the Ktor client?

Both platforms configure a `kotlinx.serialization.json.Json` instance with `ignoreUnknownKeys = true` (to prevent crashes when the API adds new fields) and `explicitNulls = false` (to omit null values from requests). These settings are applied through the `ContentNegotiation` plugin in the client configuration.

### Why does the SDK use the `expect`/`actual` pattern for the HTTP client?

The `expect`/`actual` pattern allows the SDK to maintain a single interface (`ktorClient`) in the common code while using platform-optimal engines—`OkHttp` for Android and `Darwin` for iOS. This approach maximizes compatibility with platform-specific networking features while keeping the API surface consistent across targets.