Ktor 3.2.3 Help

Client engines

The Ktor HTTP client is multiplatform and runs on JVM, Android, JavaScript (including WebAssembly), and Native targets. Each platform requires a specific engine to process network requests. For example, you can use Apache or Jetty for JVM applications, OkHttp or Android for Android, Curl for desktop applications targeting Kotlin/Native. Every engine differs slightly in features and configuration, so you can choose the one that best meets your platform and use-case needs.

Supported platforms

The table below lists the platforms supported by each engine:

Engine

Platforms

Apache5

JVM

Java

JVM

Jetty

JVM

Android

JVM, Android

OkHttp

JVM, Android

Darwin

Native

WinHttp

Native

Curl

Native

CIO

JVM, Android, Native, JavaScript, WasmJs

Js

JavaScript

Supported Android/Java versions

Client engines targeting JVM or both JVM and Android support the following Android/Java versions:

Engine

Android version

Java version

Apache5

8+

Java

11+

Jetty

11+

CIO

7.0+ *

8+

Android

1.x+

8+

OkHttp

5.0+

8+

* To use the CIO engine on older Android versions, you need to enable Java 8 API desugaring.

Add an engine dependency

In addition to the ktor-client-core artifact, the Ktor client requires a dependency for a specific engine. Each supported platform has a set of available engines, described in the corresponding sections:

Specify an engine

To use a specific engine, pass the engine class to the HttpClient constructor. The following example creates a client with the CIO engine:

import io.ktor.client.* import io.ktor.client.engine.cio.* val client = HttpClient(CIO)

Default engine

If you omit the engine argument, the client will choose an engine automatically based on the dependencies in your build script.

import io.ktor.client.* val client = HttpClient()

This is especially useful in multiplatform projects. For example, for a project targeting both Android and iOS, you can add the Android dependency to the androidMain source set and the Darwin dependency to the iosMain source set. The appropriate engine is selected at run time upon HttpClient creation.

Configure an engine

To configure an engine, use the engine {} function. All engines can be configured using the common options from HttpClientEngineConfig:

HttpClient() { engine { // this: [[[HttpClientEngineConfig|https://api.ktor.io/ktor-client/ktor-client-core/io.ktor.client.engine/-http-client-engine-config/index.html]]] threadsCount = 4 pipelining = true } }

In the next sections, you can learn how to configure specific engines for different platforms.

JVM

The JVM target supports the Apache5, Java, and Jetty engines.

Apache5

The Apache5 engine supports both HTTP/1.1 and HTTP/2, with HTTP/2 enabled by default. This is the recommended Apache-based engine for new projects.

  1. Add the ktor-client-apache5 dependency:

    implementation("io.ktor:ktor-client-apache5:$ktor_version")
    implementation "io.ktor:ktor-client-apache5:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-apache5-jvm</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the Apache5 class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.apache5.* val client = HttpClient(Apache5)

  3. Use the engine {} block to access and set properties from Apache5EngineConfig:

    import io.ktor.client.* import io.ktor.client.engine.apache5.* import org.apache.hc.core5.http.* val client = HttpClient(Apache5) { engine { // this: Apache5EngineConfig followRedirects = true socketTimeout = 10_000 connectTimeout = 10_000 connectionRequestTimeout = 20_000 customizeClient { // this: HttpAsyncClientBuilder setProxy(HttpHost("127.0.0.1", 8080)) // ... } customizeRequest { // this: RequestConfig.Builder } } }

Java

The Java engine uses the Java HTTP Client introduced in Java 11. To use it, follow the steps below:

  1. Add the ktor-client-java dependency:

    implementation("io.ktor:ktor-client-java:$ktor_version")
    implementation "io.ktor:ktor-client-java:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-java-jvm</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the Java class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.java.* val client = HttpClient(Java)

  3. To configure the engine, set properties from JavaHttpConfig in the engine {} block:

    import io.ktor.client.* import io.ktor.client.engine.* import io.ktor.client.engine.java.* val client = HttpClient(Java) { engine { // this: [[[JavaHttpConfig|https://api.ktor.io/ktor-client/ktor-client-java/io.ktor.client.engine.java/-java-http-config/index.html]]] threadsCount = 8 pipelining = true proxy = ProxyBuilder.http("http://proxy-server.com/") protocolVersion = java.net.http.HttpClient.Version.HTTP_2 } }

Jetty

The Jetty engine supports only HTTP/2 and can be configured in the following way:

  1. Add the ktor-client-jetty-jakarta dependency:

    implementation("io.ktor:ktor-client-jetty-jakarta:$ktor_version")
    implementation "io.ktor:ktor-client-jetty-jakarta:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-jetty-jakarta-jvm</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the Jetty class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.jetty.jakarta.* val client = HttpClient(Jetty)

  3. To configure the engine, set properties from JettyEngineConfig in the engine {} block:

    import io.ktor.client.* import io.ktor.client.engine.jetty.jakarta.* import org.eclipse.jetty.util.ssl.SslContextFactory val client = HttpClient(Jetty) { engine { // this: [[[JettyEngineConfig|https://api.ktor.io/ktor-client/ktor-client-jetty-jakarta/io.ktor.client.engine.jetty.jakarta/-jetty-engine-config/index.html]]] sslContextFactory = SslContextFactory.Client() clientCacheSize = 12 } }

JVM and Android

In this section, we'll take a look at engines available for JVM/Android and their configurations.

Android

The Android engine targets Android and can be configured in the following way:

  1. Add the ktor-client-android dependency:

    implementation("io.ktor:ktor-client-android:$ktor_version")
    implementation "io.ktor:ktor-client-android:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-android-jvm</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the Android class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.android.* val client = HttpClient(Android)

  3. To configure an engine, set properties from AndroidEngineConfig in the engine {} block:

    import io.ktor.client.* import io.ktor.client.engine.android.* import java.net.Proxy import java.net.InetSocketAddress val client = HttpClient(Android) { engine { // this: [[[AndroidEngineConfig|https://api.ktor.io/ktor-client/ktor-client-android/io.ktor.client.engine.android/-android-engine-config/index.html]]] connectTimeout = 100_000 socketTimeout = 100_000 proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("localhost", 8080)) } }

OkHttp

The OkHttp engine is based on OkHttp and can be configured in the following way:

  1. Add the ktor-client-okhttp dependency:

    implementation("io.ktor:ktor-client-okhttp:$ktor_version")
    implementation "io.ktor:ktor-client-okhttp:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-okhttp-jvm</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the OkHttp class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.okhttp.* val client = HttpClient(OkHttp)

  3. To configure an engine, set properties from OkHttpConfig in the engine {} block:

    import io.ktor.client.* import io.ktor.client.engine.okhttp.* val client = HttpClient(OkHttp) { engine { // this: [[[OkHttpConfig|https://api.ktor.io/ktor-client/ktor-client-okhttp/io.ktor.client.engine.okhttp/-ok-http-config/index.html]]] config { // this: OkHttpClient.Builder followRedirects(true) // ... } addInterceptor(interceptor) addNetworkInterceptor(interceptor) preconfigured = okHttpClientInstance } }

Native

Ktor provides the Darwin, WinHttp, and Curl engines for Kotlin/Native targets.

Darwin

The Darwin engine targets Darwin-based operating systems, such as macOS, iOS, tvOS, and watchOS. It uses NSURLSession under the hood. To use the Darwin engine, follow the steps below:

  1. Add the ktor-client-darwin dependency:

    implementation("io.ktor:ktor-client-darwin:$ktor_version")
    implementation "io.ktor:ktor-client-darwin:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-darwin-macosx64</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the Darwin class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.darwin.* val client = HttpClient(Darwin)
  3. Configure the engine in the engine {} block using DarwinClientEngineConfig. For example, you can customize requests with configureRequest or sessions with configureSession:

    val client = HttpClient(Darwin) { engine { configureRequest { setAllowsCellularAccess(true) } } }

    For the full example, see client-engine-darwin.

WinHttp

The WinHttp engine targets Windows-based operating systems. To use the WinHttp engine, follow the steps below:

  1. Add the ktor-client-winhttp dependency:

    implementation("io.ktor:ktor-client-winhttp:$ktor_version")
    implementation "io.ktor:ktor-client-winhttp:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-winhttp-mingwx64</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the WinHttp class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.winhttp.* val client = HttpClient(WinHttp)
  3. Configure the engine in the engine {} block using WinHttpClientEngineConfig. For example, you can use the protocolVersion property to change the HTTP version:

    val client = HttpClient(WinHttp) { engine { protocolVersion = HttpProtocolVersion.HTTP_1_1 } }

    For the full example, see client-engine-winhttp.

Curl

For desktop platforms, Ktor provides the Curl engine. It is supported on linuxX64, linuxArm64, macosX64, macosArm64, and mingwX64. To use the Curl engine, follow the steps below:

  1. Add the ktor-client-curl dependency:

    implementation("io.ktor:ktor-client-curl:$ktor_version")
    implementation "io.ktor:ktor-client-curl:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-curl-macosx64</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the Curl class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.curl.* val client = HttpClient(Curl)
  3. Configure the engine in the engine {} block using CurlClientEngineConfig. For example, disable SSL verification for testing purposes:

    val client = HttpClient(Curl) { engine { sslVerify = false } }

    For the full example, see client-engine-curl.

JVM, Android, Native, JS and WasmJs

CIO

The CIO engine is a fully asynchronous coroutine-based engine available on JVM, Android, Native, JavaScript, and WebAssembly JavaScript (WasmJs) platforms. It currently supports HTTP/1.x only. To use it, follow the steps below:

  1. Add the ktor-client-cio dependency:

    implementation("io.ktor:ktor-client-cio:$ktor_version")
    implementation "io.ktor:ktor-client-cio:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-cio-jvm</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the CIO class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.cio.* val client = HttpClient(CIO)

  3. Configure the engine in the engine {} block using CIOEngineConfig:

    import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.network.tls.* val client = HttpClient(CIO) { engine { // this: [[[CIOEngineConfig|https://api.ktor.io/ktor-client/ktor-client-cio/io.ktor.client.engine.cio/-c-i-o-engine-config/index.html]]] maxConnectionsCount = 1000 endpoint { // this: [[[EndpointConfig|https://api.ktor.io/ktor-client/ktor-client-cio/io.ktor.client.engine.cio/-endpoint-config/index.html]]] maxConnectionsPerRoute = 100 pipelineMaxSize = 20 keepAliveTime = 5000 connectTimeout = 5000 connectAttempts = 5 } https { // this: [[[TLSConfigBuilder|https://api.ktor.io/ktor-network/ktor-network-tls/io.ktor.network.tls/-t-l-s-config-builder/index.html]]] serverName = "api.ktor.io" cipherSuites = CIOCipherSuites.SupportedSuites trustManager = myCustomTrustManager random = mySecureRandom addKeyStore(myKeyStore, myKeyStorePassword) } } }

JavaScript

The Js engine can be used for JavaScript projects. It uses fetch API for browser applications and node-fetch for Node.js. To use it, follow the steps below:

  1. Add the ktor-client-js dependency:

    implementation("io.ktor:ktor-client-js:$ktor_version")
    implementation "io.ktor:ktor-client-js:$ktor_version"
    <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-client-js</artifactId> <version>${ktor_version}</version> </dependency>
  2. Pass the Js class as an argument to the HttpClient constructor:

    import io.ktor.client.* import io.ktor.client.engine.js.* val client = HttpClient(Js)

    You can also call the JsClient() function to get the Js engine singleton:

    import io.ktor.client.engine.js.* val client = JsClient()

For the full example, see client-engine-js.

Limitations

HTTP/2 and WebSockets

Not all engines support the HTTP/2 protocol. If an engine supports HTTP/2, you can enable it in the engine's configuration. For example, with the Java engine.

The table below shows whether a specific engine supports HTTP/2 and WebSockets:

Engine

HTTP/2

WebSockets

Apache5

✅️

✖️

Java

✅️

Jetty

✖️

CIO

✖️

Android

✖️

✖️

OkHttp

Js

Darwin

WinHttp

Curl

Security

SSL must be configured per engine. Each engine provides its own SSL configuration options.

Proxy support

Some engines don't support proxies. For the complete list, see the proxy documentation.

Logging

The Logging plugin provides different logger types depending on the target platforms.

Timeout

The HttpTimeout plugin has some limitations on certain engines. For the complete list, see Timeout limitations.

Example: How to configure an engine in a multiplatform mobile project

When building a multiplatform project, you can use expected and actual declarations to select and configure engines for each target platform. This allows you to share most client configuration in common code while applying engine-specific options in platform code. We'll demonstrate how to achieve this using a project created in the Creating a cross-platform mobile application tutorial:

  1. Open the shared/src/commonMain/kotlin/com/example/kmmktor/Platform.kt file and add a top-level httpClient() function that accepts a configuration block and returns an HttpClient:

    expect fun httpClient(config: HttpClientConfig<*>.() -> Unit = {}): HttpClient
  2. Open shared/src/androidMain/kotlin/com/example/kmmktor/Platform.kt and add an actual declaration of the httpClient() function for the Android module:

    import io.ktor.client.* import io.ktor.client.engine.okhttp.* import java.util.concurrent.TimeUnit actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(OkHttp) { config(this) engine { config { retryOnConnectionFailure(true) connectTimeout(0, TimeUnit.SECONDS) } } }

  3. Open shared/src/iosMain/kotlin/com/example/kmmktor/Platform.kt and add an actual declaration of the httpClient() function for the iOS module:

    import io.ktor.client.* import io.ktor.client.engine.darwin.* actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Darwin) { config(this) engine { configureRequest { setAllowsCellularAccess(true) } } }

    You can now call httpClient() in shared code without worrying about which engine is used.

  4. To use the client in shared code, open shared/src/commonMain/kotlin/com/example/kmmktor/Greeting.kt and replace the HttpClient() constructor with the httpClient() function call:

    private val client = httpClient()
    22 August 2025