Using Ktor HTTP client content‑negotiation with Kotlinx Serialization
Learn how to enable automatic JSON serialization and deserialization in Ktor’s HTTP client using the ContentNegotiation feature and Kotlinx Serialization.
20 Aug 2026, 01:27 UTC

Automatic JSON conversion with ContentNegotiation
Ktor’s HTTP client can serialize request bodies and deserialize responses without manual parsing when the ContentNegotiation feature is installed with a Kotlinx Json serializer. The feature looks at the declared type of the call and the Accept header to decide how to encode or decode the payload.
Configuration example
import io.ktor.client.*
import io.ktor.client.content.*
import io.ktor.client.features.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.http.content.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
// A data class that Kotlinx can serialize
@Serializable
data class User(val id: Int, val name: String)
fun main() = runBlocking {
val client = HttpClient {
// Install content‑negotiation with a Json instance
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true; prettyPrint = true })
}
}
// Example GET – the Accept header is set automatically
val user: User = client.get("https://api.example.com/user/42") {
// Accept header can be omitted; the feature adds application/json
}
println(user)
}
The install block registers a single Json instance. When the client executes get, it:
- Serializes any request body (if present) using that
Jsoninstance. - Sets the
Acceptheader toapplication/jsonunless you override it. - Deserializes the response body into the declared return type (
Userin the example).
Limits and prerequisites
- Only types annotated with
@Serializable(or with a registered custom serializer) are supported. Attempting to deserialize a non‑serializable type results in aSerializationExceptionat runtime. - The same
Jsonconfiguration should be used on client and server if you rely on custom settings such asignoreUnknownKeysorencodeDefaults. Mismatched configurations can cause missing fields or unexpected name changes. - Content‑negotiation does not automatically add custom headers; you must set them explicitly if you need a non‑default
Acceptvalue.
Common pitfalls and how to avoid them
- Forgotten
@Serializable: If you see aSerializationExceptionstating that a class lacks a serializer, verify that the class (or its properties) is marked with@Serializableor provide a custom serializer viaserializersModule. - Multiple
Jsoninstances: Registering more than one JSON serializer for the same media type overwrites the previous registration and can lead to runtime conflicts. Keep a singleJsoninstance per client (or share it via a variable). - Missing
Acceptheader in tests: When writing unit tests with a mock server, explicitly setaccept(ContentType.Application.Json)if you rely on the default behavior, or assert that the client adds the header automatically.
Practical verification
To confirm that the setup works, run the client against a known endpoint that returns JSON (for example, a local echo service). After the call, inspect the returned object's properties; they should match the JSON fields. If a field is missing or extra, check the ignoreUnknownKeys setting and the server’s JSON format.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.