Ktor 2.3.11 Help

Making requests

After setting up the client, you can make HTTP requests. The main way of making HTTP requests is the request function that can take a URL as a parameter. Inside this function, you can configure various request parameters:

  • Specify an HTTP method, such as GET, POST, PUT, DELETE, HEAD, OPTION, or PATCH.

  • Specify a URL as a string or configure URL components (a domain, a path, query parameters, etc.) separately.

  • Add headers and cookies.

  • Set the body of a request, for example, a plain text, a data object, or form parameters.

These parameters are exposed by the HttpRequestBuilder class.

import io.ktor.client.request.* import io.ktor.client.statement.* val response: HttpResponse = client.request("https://ktor.io/") { // Configure request parameters exposed by [[[HttpRequestBuilder|https://api.ktor.io/ktor-client/ktor-client-core/io.ktor.client.request/-http-request-builder/index.html]]] }

Note that this function allows you to receive a response as an HttpResponse object. HttpResponse exposes the API required to get a response body in various ways (a string, a JSON object, etc.) and obtain response parameters, such as a status code, content type, headers, and so on. You can learn more from the Receiving responses topic.

Specify an HTTP method

When calling the request function, you can specify the desired HTTP method using the method property:

import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* val response: HttpResponse = client.request("https://ktor.io/") { method = HttpMethod.Get }

In addition to the request function, HttpClient provides specific functions for basic HTTP methods: get, post, put, and so on. For example, you can replace the above request with the following code:

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

In both examples, a request URL is specified as a string. You can also configure URL components separately using HttpRequestBuilder.

Specify a request URL

The Ktor client allows you to configure a request URL in the following ways:

  • Pass the entire URL string

    val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")
  • Configure URL components separately

    client.get { url { protocol = URLProtocol.HTTPS host = "ktor.io" path("docs/welcome.html") } }

    In this case, the url parameter exposed by HttpRequestBuilder is used. This parameter accepts URLBuilder and provides more flexibility in building URLs.

Path segments

In the previous example, we've specified the entire URL path using the URLBuilder.path property. You can also pass individual path segments using the appendPathSegments function.

client.get("https://ktor.io") { url { appendPathSegments("docs", "welcome.html") } }

Note that appendPathSegments encodes path segments. To disable encoding, use appendEncodedPathSegments.

Query parameters

To add query string parameters, use the URLBuilder.parameters property:

client.get("https://ktor.io") { url { parameters.append("token", "abc123") } }

Note that parameters encodes query parameters. To disable encoding, use encodedParameters.

URL fragment

A hash mark # introduces the optional fragment near the end of the URL. You can configure a URL fragment using the fragment property.

client.get("https://ktor.io") { url { fragment = "some_anchor" } }

Note that fragment encodes a URL fragment. To disable encoding, use encodedFragment.

Set request parameters

In this section, we'll see how to specify various request parameters, including an HTTP method, headers, and cookies. If you need to configure some default parameters for all requests of a specific client, use the DefaultRequest plugin.


To add headers to the request, you can use the following ways:

  • The headers function allows you to add several headers at once:

    client.get("https://ktor.io") { headers { append(HttpHeaders.Accept, "text/html") append(HttpHeaders.Authorization, "abc123") append(HttpHeaders.UserAgent, "ktor client") } }
  • The header function allows you to append a single header.

  • The basicAuth and bearerAuth functions add the Authorization header with a corresponding HTTP scheme.


To send cookies, use the cookie function:

client.get("https://ktor.io") { cookie(name = "user_name", value = "jetbrains", expires = GMTDate( seconds = 0, minutes = 0, hours = 10, dayOfMonth = 1, month = Month.APRIL, year = 2023 )) }

Ktor also provides the HttpCookies plugin that allows you to keep cookies between calls. If this plugin is installed, cookies added using the cookie function are ignored.

Set request body

To set the body of a request, you need to call the setBody function exposed by HttpRequestBuilder. This function accepts different types of payloads, including plain text, arbitrary class instances, form data, byte arrays, and so on. Below, we'll take a look at several examples.


Sending plain text as body can be implemented in the following way:

import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* val response: HttpResponse = client.post("http://localhost:8080/post") { setBody("Body content") }


With the enabled ContentNegotiation plugin, you can send a class instance within a request body as JSON. To do this, pass a class instance to the setBody function and set the content type to application/json using the contentType function:

val response: HttpResponse = client.post("http://localhost:8080/customer") { contentType(ContentType.Application.Json) setBody(Customer(3, "Jet", "Brains")) }

You can learn more from the Content negotiation and serialization help section.

Form parameters

The Ktor client provides the submitForm function for sending form parameters using both x-www-form-urlencoded and multipart/form-data types. The example below shows how to send form parameters encoded as multipart/form-data:

  • url specifies a URL for making a request.

  • formParameters a set of form parameters built using parameters.

val client = HttpClient(CIO) val response: HttpResponse = client.submitForm( url = "http://localhost:8080/signup", formParameters = parameters { append("username", "JetBrains") append("email", "example@jetbrains.com") append("password", "foobar") append("confirmation", "foobar") } )

You can find the full example here: client-submit-form.

Upload a file

If you need to send a file with a form, you can use the following approaches:

  • Use the submitFormWithBinaryData function. In this case, a boundary will be generated automatically.

  • Call the post function and pass the MultiPartFormDataContent instance to the setBody function. Note that the MultiPartFormDataContent constructor also allows you to pass a boundary value.

For both approaches, you need to build form data using the formData function.

val client = HttpClient(CIO) val response: HttpResponse = client.submitFormWithBinaryData( url = "http://localhost:8080/upload", formData = formData { append("description", "Ktor logo") append("image", File("ktor_logo.png").readBytes(), Headers.build { append(HttpHeaders.ContentType, "image/png") append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"") }) } )
val client = HttpClient(CIO) val response: HttpResponse = client.post("http://localhost:8080/upload") { setBody(MultiPartFormDataContent( formData { append("description", "Ktor logo") append("image", File("ktor_logo.png").readBytes(), Headers.build { append(HttpHeaders.ContentType, "image/png") append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"") }) }, boundary = "WebAppBoundary" ) ) onUpload { bytesSentTotal, contentLength -> println("Sent $bytesSentTotal bytes from $contentLength") } }

MultiPartFormDataContent also allows you to override a boundary and content type as follows:

fun customMultiPartMixedDataContent(parts: List<PartData>): MultiPartFormDataContent { val boundary = "WebAppBoundary" val contentType = ContentType.MultiPart.Mixed.withParameter("boundary", boundary) return MultiPartFormDataContent(parts, boundary, contentType) }

You can find the full examples here:

Binary data

To send binary data with the application/octet-stream content type, pass the ByteReadChannel instance to the setBody function. For example, you can use the File.readChannel function to open a read channel for a file and fill it:

val response = client.post("") { setBody(File("ktor_logo.png").readChannel()) }

You can find the full example here: client-upload-binary-data.

Parallel requests

When sending two requests at once, the client suspends the second request execution until the first one is finished. If you need to perform several requests at once, you can use launch or async functions. The code snippet below shows how to perform two requests asynchronously:

runBlocking { // Parallel requests val firstRequest: Deferred<String> = async { client.get("http://localhost:8080/path1").bodyAsText() } val secondRequest: Deferred<String> = async { client.get("http://localhost:8080/path2").bodyAsText() } val firstRequestContent = firstRequest.await() val secondRequestContent = secondRequest.await() }

To see a full example, go to client-parallel-requests.

Cancel a request

If you need to cancel a request, you can cancel a coroutine that runs this request. The launch function returns a Job that can be used to cancel the running coroutine:

import kotlinx.coroutines.* val client = HttpClient(CIO) val job = launch { val requestContent: String = client.get("http://localhost:8080") } job.cancel()

Learn more from Cancellation and timeouts.

Last modified: 02 April 2024