Ktor 3.1.1 Help

Receiving responses

All functions used to make an HTTP request (request, get, post, etc.) allow you to receive a response as an HttpResponse object.

HttpResponse exposes the API required to get a response body in various ways (raw bytes, JSON objects, etc.) and obtain response parameters, such as a status code, content type, and headers. For example, you can receive HttpResponse for a GET request without parameters in the following way:

val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")

Receive response parameters

The HttpResponse class allows you to get various response parameters, such as a status code, headers, HTTP version, and more.

Status code

To get the status code of a response, use the HttpResponse.status property:

import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.http.* val httpResponse: HttpResponse = client.get("https://ktor.io/") if (httpResponse.status.value in 200..299) { println("Successful response!") }

Headers

The HttpResponse.headers property allows you to get a Headers map containing all response headers. Additionally, HttpResponse exposes the following functions for receiving specific header values:

  • contentType for the Content-Type header value

  • charset for a charset from the Content-Type header value.

  • etag for the E-Tag header value.

  • setCookie for the Set-Cookie header value.

Receive response body

Raw body

To receive a raw body of a response, call the body function and pass the required type as a parameter. The code snippet below shows how to receive a raw body as a String:

val httpResponse: HttpResponse = client.get("https://ktor.io/") val stringBody: String = httpResponse.body()

Similarly, you can get a body as a ByteArray:

val httpResponse: HttpResponse = client.get("https://ktor.io/") val byteArrayBody: ByteArray = httpResponse.body()

A runnable example below shows how to get a response as a ByteArray and save it to a file:

val client = HttpClient() val file = File.createTempFile("files", "index") runBlocking { val httpResponse: HttpResponse = client.get("https://ktor.io/") { onDownload { bytesSentTotal, contentLength -> println("Received $bytesSentTotal bytes from $contentLength") } } val responseBody: ByteArray = httpResponse.body() file.writeBytes(responseBody) println("A file saved to ${file.path}") }

The onDownload() extension function in the example above is used to display download progress.

This approach loads the entire response into memory at once, which can be problematic for large files. To reduce memory usage, consider streaming the data in chunks.

JSON object

With the ContentNegotiation plugin installed, you can deserialize JSON data into a data class when receiving responses:

val customer: Customer = client.get("http://localhost:8080/customer/3").body()

To learn more, see Receive and send data.

Streaming data

When you call the HttpResponse.body function to get a body, Ktor processes a response in memory and returns a full response body. If you need to get chunks of a response sequentially instead of waiting for the entire response, use HttpStatement with scoped execute block. A runnable example below shows how to receive a response content in chunks (byte packets) and save them in a file:

val client = HttpClient(CIO) val file = File.createTempFile("files", "index") val stream = file.outputStream().asSink() val fileSize = 100 * 1024 * 1024 runBlocking { client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse -> val channel: ByteReadChannel = httpResponse.body() var count = 0L stream.use { while (!channel.exhausted()) { val chunk = channel.readRemaining() count += chunk.remaining chunk.transferTo(stream) println("Received $count bytes from ${httpResponse.contentLength()}") } } } println("A file saved to ${file.path}") }

In this example, ByteReadChannel is used to read data asynchronously. Using ByteReadChannel.readRemaining() retrieves all available bytes in the channel, while Source.transferTo() directly writes the data to the file, reducing unnecessary allocations.

To save a response body to a file without extra processing, you can use the ByteReadChannel.copyAndClose() function instead:

client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse -> val channel: ByteReadChannel = httpResponse.body() channel.copyAndClose(file.writeChannel()) println("A file saved to ${file.path}") }
Last modified: 18 March 2025