Ktor 3.2.3 Help

Making requests

After configuring the client, you can start making HTTP requests. The primary way to do this is by using the .request() function that accepts 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, OPTIONS, or PATCH.

  • Configure a URL as a string or configure its components (such as domain,path, and query parameters) separately.

  • Use a Unix domain socket.

  • Add headers and cookies.

  • Include a request body – for example, 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]]] }

The .request() function returns a response as an HttpResponse object. HttpResponse exposes the API required to get a response body in various formats – such as a string, a JSON object, and more – as well as retrieving response parameters, such as a status code, content type, and headers. For more information, see Receiving responses.

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 .request(), HttpClient provides specific functions for basic HTTP methods, such as .get(), .post(), and .put(). The example above can be simplified using the .get() function:

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 multiple 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 provided by HttpRequestBuilder is used. It accepts an instance of URLBuilder, offering more flexibility for building complex URLs.

Path segments

In the previous example, the entire URL path was specified using the URLBuilder.path property. Alternatively, you can pass individual path segments using the appendPathSegments() function.

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

By default, appendPathSegments encodes path segments. To disable encoding, use appendEncodedPathSegments() instead.

Query parameters

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

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

By default, parameters encodes query parameters. To disable encoding, use encodedParameters() instead.

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" } }

By default, fragment encodes a URL fragment. To disable encoding, use encodedFragment() instead.

Specify a Unix domain socket

To send a request to a server listening to a Unix domain socket, call the unixSocket() function when using a CIO client:

val client = HttpClient(CIO) val response: HttpResponse = client.get("/") { unixSocket("/tmp/test-unix-socket-ktor.sock") }

You can also configure a Unix domain socket as a part of a default request.

Set request parameters

You can specify various request parameters, including an HTTP method, headers, and cookies. If you need to configure default parameters for all requests of a specific client, use the DefaultRequest plugin.

Headers

You can add headers to a request in several ways:

Add multiple headers

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") } }

Add a single header

The header function allows you to append a single header.

Use basicAuth or bearerAuth for authorization

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

Cookies

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 request body, call the setBody() function provided by HttpRequestBuilder. This function accepts different types of payloads, including plain text, arbitrary class instances, form data, and byte arrays.

Text

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") }

Objects

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/jsonusing the contentType() function:

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

For more information, see Content negotiation and serialization in Ktor Client.

Form parameters

The Ktor client provides the submitForm() function for sending form parameters with the application/x-www-form-urlencoded type. The following example demonstrates its usage:

val client = HttpClient(CIO) val response: HttpResponse = client.submitForm( url = "http://localhost:8080/signup", formParameters = parameters { append("username", "JetBrains") append("email", "[email protected]") append("password", "foobar") append("confirmation", "foobar") } )
  • url specifies a URL for making a request.

  • formParameters is a set of form parameters built using parameters.

For the full example, see 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. The MultiPartFormDataContent constructor also allows you to pass a boundary value.

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

Using .submitFormWithBinaryData()

The .submitFormWithBinaryData() function automatically generates a boundary and is suitable for simple use cases where the file content is small enough to be safely read into memory using .readBytes().

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\"") }) } )

For the full example, see client-upload.

Using MultiPartFormDataContent

To stream large or dynamic content efficiently, you can use MultiPartFormDataContent with an InputProvider. InputProvider allows you to supply file data as a buffered stream rather than loading it entirely into memory, making it well-suited for large files. With MultiPartFormDataContent, you can also monitor upload progress using the onUpload callback.

val client = HttpClient(CIO) val file = File("ktor_logo.png") val response: HttpResponse = client.post("http://localhost:8080/upload") { setBody( MultiPartFormDataContent( formData { append("description", "Ktor logo") append( "image", InputProvider { file.inputStream().asInput().buffered() }, 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") } }

In multiplatform projects, you can use SystemFileSystem.source() with InputProvider:

InputProvider { SystemFileSystem.source(Path("ktor_logo.png")).buffered() }

You can also construct a MultiPartFormDataContent with a custom boundary and content type manually:

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

For the full example, see client-upload-progress.

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:

val response = client.post("http://0.0.0.0:8080/upload") { setBody(File("ktor_logo.png").readChannel()) }

For the full example, see client-upload-binary-data.

Parallel requests

By default, when you send multiple requests sequentially, the client suspends each call until the previous one completes. To perform multiple requests concurrently, use the launch() or async() functions. The following example demonstrates how to execute two requests in parallel using async():

coroutineScope { // 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() }

For the full example, see client-parallel-requests.

Cancel a request

To cancel a request, cancel the coroutine running that 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()

For more details, see Cancellation and timeouts.

01 August 2025