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:
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:
Headers
The HttpResponse.headers property allows you to get a Headers map containing all response headers.
Additionally, the HttpResponse class exposes the following functions for receiving specific header values:
contentType()for theContent-Typeheader value.charset()for a charset from theContent-Typeheader value.etag()for theE-Tagheader value.setCookie()for theSet-Cookieheader value.
Split header values
If a header can contain multiple comma — or semicolon — separated values, you can use the .getSplitValues() function to retrieve all split values from a header:
Using the usual get operator returns values without splitting:
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:
Similarly, you can get a body as a ByteArray:
A runnable example below shows how to get a response as a ByteArray and save it to a file:
The onDownload() extension function in the example above is used to display download progress.
For non-streaming requests, the response body is automatically loaded and cached in memory, allowing repeated access. While this is efficient for small payloads, it may lead to high memory usage with large responses.
To handle large responses efficiently, use a streaming approach, which processes the response incrementally without saving it in memory.
JSON object
With the ContentNegotiation plugin installed, you can deserialize JSON data into a data class when receiving responses:
To learn more, see Receive and send data.
Multipart form data
When receiving a response that contains multipart form data, you can read its body as a MultiPartData instance. This allows you to process form fields and files included in the response.
The example below demonstrates how to handle both text form fields and file uploads from a multipart response:
Form fields
PartData.FormItem represents a form field, which values can be accessed through the value property:
File uploads
PartData.FileItem represents a file item. You can handle file uploads as byte streams:
Resource cleanup
Once the form processing is complete, each part is disposed of using the .dispose() function to free resources.
Streaming data
By default, calling HttpResponse.body() loads the full response into memory. For large responses or file downloads, it’s often better to process data in chunks without waiting for the full body.
Ktor provides several ways to do this using ByteReadChannel and I/O utilities.
Sequential chunk processing
To process the response sequentially in chunks, use HttpStatement with a scoped execute block.
The following example demonstrates reading a response in chunks and saving it to a file:
Using ByteReadChannel.readRemaining() retrieves all available bytes in the channel, while Source.transferTo() directly writes the data to the file, reducing unnecessary allocations.
Writing the response directly to a file
For simple downloads where chunk-by-chunk processing is not needed, you can choose one of the following approaches:
Copy all bytes to a ByteWriteChannel and close
The ByteReadChannel.copyAndClose() function copies all remaining bytes from a ByteReadChannel to a ByteWriteChannel and then closes both channels automatically:
This is convenient for full file downloads where you don’t need to manually manage channels.
Copy to a RawSink
The ByteReadChannel.readTo() function writes bytes directly to a RawSink without intermediate buffers:
Unlike .copyAndClose(), the sink remains open after writing and it is only closed automatically if an error occurs during the transfer.