Ktor: what is it, features of the asynchronous HTTP client

Author: IT Sectr Published: 2026-03-07 Reading time: 8 min

Ktor is an asynchronous HTTP client for Kotlin, developed by JetBrains as part of the eponymous framework for server and client development. Ktor is built on Kotlin coroutines and supports multiplatform development. According to JetBrains, 2025, Ktor provides native integration with the Kotlin ecosystem without reflection and additional dependencies.

Key Takeaways

  • Ktor — asynchronous HTTP client in Kotlin with multiplatform support
  • Coroutines — the foundation for executing requests without callbacks and reactive streams
  • Plugins — modular extension system for serialization, logging, and authorization
  • Multiplatform — one code for Android, iOS, Desktop and Server
  • Kotlinx Serialization — native serialization without reflection via @Serializable

What is Ktor?

Ktor is a framework for building asynchronous server and client applications in Kotlin, created by JetBrains. Ktor Client is the client-side part of the framework, providing an HTTP client with full support for Kotlin coroutines, multiplatform (JVM, Native, JS) and a modular plugin-based architecture.

Ktor emerged in 2018 as an alternative to Retrofit and OkHttp for Kotlin-first projects. Unlike Retrofit, which ported the Java approach with annotations, Ktor Client uses Kotlin DSL for request configuration — without annotations and reflection. This makes the code more readable and type-safe for Kotlin developers.

According to the 2024 Kotlin Multiplatform survey, Ktor Client is used in 35% of Kotlin Multiplatform Mobile (KMM) projects, making it the second most popular HTTP client after OkHttp in the Kotlin community. Ktor is preferred in projects where multiplatform support and native integration with the Kotlin ecosystem are important.

How Ktor Client Works

Ktor Client Architecture is based on a pipeline of plugins. Each request passes through a sequence of installed plugins that can modify the request, response, or perform side actions — logging, compression, serialization, authentication.

When creating an HTTP client via the HttpClient { } DSL block, you specify the engine (OkHttp, Android, CIO, Darwin) and install plugins. Each engine implements low-level request sending for a specific platform: on Android the OkHttp engine is used, on iOS — Darwin (URLSession), on Desktop — CIO (Coroutine-based I/O). HttpClient automatically selects the optimal engine for the current platform.

A request in Ktor Client is executed via a suspend function, which means full integration with coroutines. No Callbacks, no RxJava or LiveData — only sequential code with suspend that works asynchronously without blocking the thread.

Request processing pipeline

Ktor pipeline consists of phases: first the request passes through installed plugins (e.g., ContentNegotiation for JSON, Logging for logs), then the engine executes the HTTP request, and the response passes through plugins again for deserialization. Each plugin is a suspend function executing in the pipeline coroutine.

An important advantage of the Ktor pipeline is the ability to perform conditional processing. A plugin can check the URL or request headers and skip processing if the condition is not met. For example, ContentEncoding with gzip is only applied to responses containing the Content-Encoding: gzip header, and Auth only triggers for protected endpoints without affecting public APIs.

This pipeline approach allows you to combine plugins flexibly: you can install ContentNegotiation with JSON, add Auth with Bearer token, enable ContentEncoding compression and HttpTimeout — and all of them will work together in the correct order. The order of plugin installation matters: the first installed plugin will process the request earlier than the others.

Ktor Client Plugins

Plugins are Ktor's modular extension system, replacing Retrofit annotations and OkHttp interceptors. Each plugin solves a specific task and is installed via the install() function in the HttpClient block. Ktor provides built-in plugins as well as allows creating custom ones.

PluginPurpose
ContentNegotiationJSON, XML serialization and deserialization via Kotlinx Serialization
LoggingRequest and response logging with configurable level
AuthAuthentication: Basic, Bearer, Digest with automatic token refresh
HttpTimeoutConnection, read, and request timeout configuration
ContentEncodingTransparent gzip and deflate compression
DefaultRequestSetting default values for all requests

Custom plugins

For specific tasks, a custom plugin is created via createClientPlugin. The plugin can intercept the request (onRequest), response (onResponse), or handle errors (onError). This completely replaces OkHttp's Interceptor, but with a typed Kotlin API and suspend function support.

Custom plugins are convenient for adding metrics, automatic retry logic, request tracing, or A/B testing of endpoints. Unlike OkHttp interceptors, Ktor plugins are written in Kotlin and run in the coroutine context, simplifying error and timeout handling.

For request debugging, the Logging plugin is used with the ALL, HEADERS, or BODY level. Logging outputs the method, URL, status, headers, and body of the request and response. Unlike OkHttp's HttpLoggingInterceptor, Ktor Logging works asynchronously and can be configured to filter by log level (ERROR, WARN, INFO, DEBUG) without stopping the application to change configuration.

Ktor Client Code Examples in Kotlin

Let's look at a basic GET request using Ktor Client. An HttpClient is created with the ContentNegotiation plugin installed for JSON. The request is executed via the suspend function get(), and the result is automatically deserialized into a data class.

kotlin
data class User(
    val login: String,
    val id: Int,
    val avatarUrl: String
)

val client = HttpClient {
    install(ContentNegotiation) {
        json(Json {
            ignoreUnknownKeys = true
        })
    }
}

suspend fun getUser(): User {
    return client.get("https://api.github.com/users/octocat").body()
}

For a POST request with a body, the post() function is used with contentType() and body(). Ktor automatically serializes the object to JSON via the installed ContentNegotiation. The DSL style makes the code sequential and readable.

kotlin
data class CreateRepo(
    val name: String,
    val description: String,
    val private: Boolean
)

suspend fun createRepo(): Unit {
    val repo = CreateRepo(
        name = "my-project",
        description = "Sample project",
        private = false
    )
    client.post("https://api.github.com/user/repos") {
        contentType(ContentType.Application.Json)
        setBody(repo)
    }
}

Configuring timeouts and headers

HttpTimeout and DefaultRequest are two key plugins for configuration. HttpTimeout sets time limits, and DefaultRequest specifies headers and URL parameters for all requests, eliminating code duplication in each call.

kotlin
val client = HttpClient {
    install(HttpTimeout) {
        connectTimeoutMillis = 15000
        requestTimeoutMillis = 30000
    }
    install(DefaultRequest) {
        url("https://api.github.com/")
        header("Accept", "application/json")
    }
}

Ktor Multiplatform Support

Multiplatform is the main advantage of Ktor over OkHttp and Retrofit. Ktor Client runs on JVM (Android, Server), Native (iOS, macOS, Windows, Linux), and JS (Browser). The same HTTP client code runs on all platforms without changes, which is especially valuable for Kotlin Multiplatform projects.

For each platform, Ktor uses its own engine. On Android, the OkHttp engine is used by default, providing full compatibility with the OkHttp ecosystem. On iOS, DarwinEngine is used, based on URLSession. For Server — CIOEngine (Coroutine I/O). The engine can be specified explicitly: HttpClient(OkHttp) { } or HttpClient(Darwin) { }.

When choosing an engine, consider its capabilities: the OkHttp engine supports HTTP/2 and connection pooling, DarwinEngine provides native iOS network integration and background URLSession sessions, CIOEngine is a pure coroutine implementation without external dependencies. For Web targets, JsEngine or BrowserEngine is used, working through the fetch API.

Thanks to a unified API across all platforms, the code for loading data looks the same on Android, iOS, and Desktop. This reduces code duplication by 60–80% in KMM projects compared to separate implementations on Retrofit (Android) and URLSession (iOS). Plugins also work on all platforms without changes.

Common Mistakes When Working with Ktor

Ignoring HttpClient closure is a common mistake in Ktor. HttpClient implements Closeable, and it must be closed when the application finishes via client.close(). In Android, this is done in the onDestroy() of Activity or ViewModel.onCleared(). An unclosed client leads to coroutine and engine thread leaks.

Incorrect plugin order can break request processing. For example, ContentNegotiation should be installed before DefaultRequest so that the content type is applied correctly. Logging is recommended to be installed last to log the final version of the request after all modifications. Experiment with the order if plugins behave unexpectedly.

Missing exception handling in suspend functions. Ktor throws IOException for network errors and ClientRequestException for HTTP 4xx statuses. A try-catch block is mandatory for every call to get(), post(), and other methods. Use HttpResponseValidator in the HttpClient block for global error handling without duplicating try-catch in each method.

Frequently Asked Questions

How is Ktor different from Retrofit?

Ktor uses Kotlin DSL and plugins without annotations and reflection. Retrofit is built on Java annotations and reflection. Ktor supports multiplatform, Retrofit only JVM/Android. Ktor natively works with coroutines, Retrofit added suspend through a wrapper.

Which Ktor engine is best for Android?

For Android, the OkHttp engine is optimal — it provides compatibility with the OkHttp ecosystem, connection pooling, caching, and HTTP/2. Choose it via HttpClient(OkHttp) { }. The alternative is CIOEngine, built into Ktor, but it is less stable on Android.

Does Ktor support HTTP/2?

Yes, Ktor supports HTTP/2 through the appropriate engine. The OkHttp engine inherits HTTP/2 support from OkHttp. DarwinEngine on iOS supports HTTP/2 via URLSession. CIOEngine supports HTTP/2 on the server side. The choice of engine determines the level of protocol support.

How to configure authorization in Ktor Client?

Use the Auth plugin with bearer { } setup. The plugin automatically adds the Authorization header to each request and can refresh the token on a 401 response via refreshTokens. Example: install(Auth) { bearer { loadTokens { BearerTokens(token, refreshToken) } } }.

Can I use Ktor Client on iOS?

Yes, Ktor Client fully works on iOS through DarwinEngine, which uses URLSession. All plugins, serialization, and coroutines work on iOS the same as on Android. This makes Ktor the primary HTTP client for Kotlin Multiplatform Mobile (KMM) projects.

Summary

  • Ktor — asynchronous HTTP client from JetBrains with multiplatform support
  • Kotlin DSL replaces annotations — configuration through programmatic blocks without reflection
  • Plugins ContentNegotiation, Auth, Logging and HttpTimeout modularly extend functionality
  • Coroutines — execution foundation: all suspend methods without callbacks and reactive streams
  • Multiplatform — one code for Android, iOS, Desktop, Server and JS
  • Engines OkHttp, Darwin, CIO adapt Ktor to the specific platform
  • HttpResponseValidator centralizes HTTP error handling without try-catch duplication

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also