Devesh Rx Logo
Devesh Rx Blog

OkHttp in Kotlin: Complete Guide to GET & POST Requests

June 16, 2026

OkHttp in Kotlin: Complete Guide to GET & POST Requests

Complete OkHttp Android Tutorial: Mastering Network Calls in Kotlin

Sending and receiving data from a backend server is the backbone of almost every modern Android application. While higher-level libraries like Retrofit often get the spotlight, understanding OkHttp—the powerful HTTP client that actually executes those requests under the hood—is a critical skill for any Android developer.

In this comprehensive OkHttp Android tutorial, you will learn the exact steps to implement OkHttp in Kotlin. We will cover everything from setting up dependencies and making asynchronous GET/POST requests, to sending JSON payloads and debugging with logging interceptors.


What is OkHttp and Why Use It?

Developed by Square, OkHttp is the industry-standard HTTP client for Android and Java. It is designed to be highly efficient by default, offering:

  • Connection Pooling: Reuses HTTP/2 connections to reduce request latency.
  • Transparent GZIP: Automatically compresses data to save bandwidth.
  • Response Caching: Prevents repeating network requests for unchanged data.
  • Robust Error Handling: Silently recovers from common connection problems.

Let’s dive into the implementation.


Step 1: Add OkHttp Dependencies

To use OkHttp, you must add the library to your project. In modern Android development, it is highly recommended to use the Bill of Materials (BOM) inside your Version Catalog to ensure all OkHttp modules stay synchronized on the same version.

In your libs.versions.toml:

[versions]
okhttp = "5.0.0-alpha.14" # Check for the latest stable release

[libraries]
okhttp-bom = { group = "com.squareup.okhttp3", name = "okhttp-bom", version.ref = "okhttp" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp" }
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor" }

In your app/build.gradle.kts:

dependencies {
    implementation(platform(libs.okhttp.bom))
    implementation(libs.okhttp)
    implementation(libs.okhttp.logging)
}

Step 2: Configure Android Internet Permissions

By default, Android apps cannot access the internet. You must explicitly grant this permission inside your AndroidManifest file.

In AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Required to make HTTP requests -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:usesCleartextTraffic="true"  <!-- Allows non-HTTPS traffic for local testing -->
        ...>
        ...
    </application>
</manifest>

Pro Tip: In a production app, you should set usesCleartextTraffic="false" and enforce secure HTTPS connections.


Step 3: Initialize a Singleton OkHttpClient

A common mistake beginners make is instantiating a new OkHttpClient for every network call. You should create a single, shared instance and reuse it throughout your app. This allows OkHttp to pool connections and save memory.

We can also configure custom timeouts to prevent our app from hanging endlessly on poor networks.

import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit

val client = OkHttpClient.Builder()
    .connectTimeout(15, TimeUnit.SECONDS)
    .readTimeout(15, TimeUnit.SECONDS)
    .writeTimeout(15, TimeUnit.SECONDS)
    .build()

Step 4: Making an Asynchronous GET Request

A GET request retrieves data from a REST API. Because network calls take time, Android strictly prohibits running them on the Main (UI) thread.

To avoid the dreaded NetworkOnMainThreadException, we use OkHttp’s .enqueue() method. This executes the request asynchronously on a background thread.

import okhttp3.*
import java.io.IOException

fun fetchData(url: String) {
    val request = Request.Builder()
        .url(url)
        .build()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            // Handle network errors here
            e.printStackTrace()
        }

        override fun onResponse(call: Call, response: Response) {
            response.use { // .use automatically closes the response body to prevent memory leaks
                if (!response.isSuccessful) throw IOException("Unexpected code $response")

                val responseData = response.body?.string()

                // Switch back to the Main Thread to update the UI
                runOnUiThread {
                    println("Server Response: $responseData")
                }
            }
        }
    })
}

Step 5: Sending a POST Request with JSON

A POST request sends data to the server (e.g., submitting a login form). To send JSON, you must create a RequestBody and declare the Content-Type as application/json.

OkHttp provides convenient Kotlin extension functions like .toMediaType() and .toRequestBody() to make this clean and readable.

import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

fun postData(url: String, jsonPayload: String) {
    val mediaType = "application/json; charset=utf-8".toMediaType()
    val body = jsonPayload.toRequestBody(mediaType)

    val request = Request.Builder()
        .url(url)
        .post(body)
        .addHeader("Authorization", "Bearer YOUR_TOKEN_HERE") // Example of adding a header
        .build()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            e.printStackTrace()
        }

        override fun onResponse(call: Call, response: Response) {
            response.use {
                if (response.isSuccessful) {
                    println("Upload Success: ${response.body?.string()}")
                } else {
                    println("Server Error: ${response.code}")
                }
            }
        }
    })
}

Step 6: Debugging with a Logging Interceptor

When developing network layers, you need to see exactly what your app is sending and receiving. Instead of manually logging every string, use the HttpLoggingInterceptor.

Update your client builder to include the interceptor:

import okhttp3.logging.HttpLoggingInterceptor

// 1. Create the interceptor
val logging = HttpLoggingInterceptor().apply {
    level = HttpLoggingInterceptor.Level.BODY // Logs headers, body, and status codes
}

// 2. Add it to your client
val debugClient = OkHttpClient.Builder()
    .addInterceptor(logging)
    .build()

Note: Make sure to disable Level.BODY in production releases to avoid leaking sensitive user data into device logs!

Best Practices

  1. Reuse the Client: Use a single OkHttpClient instance for the entire application lifecycle.
  2. Async is Key: Always use .enqueue() (or Kotlin Coroutines) to keep the UI thread smooth.
  3. Check Success: Always validate response.isSuccessful (status codes 200-299) before reading data.
  4. Resource Safety: Always wrap your response in response.use { ... } so the network socket closes properly.
  5. Thread Safety: Never update Android Views from the OkHttp callback. Use runOnUiThread, LiveData, or Coroutine Dispatchers.Main.

F.A.Q

What is the difference between OkHttp and Retrofit?

OkHttp is a low-level HTTP client responsible for making the actual network connections, sending requests, and reading responses. Retrofit is a high-level REST client built by the same company on top of OkHttp. Retrofit maps JSON responses directly to Kotlin data classes using annotations, making API integration faster.

Can I use OkHttp with Kotlin Coroutines?

Yes! While OkHttp uses callbacks for asynchronous requests (enqueue), you can run synchronous requests (execute()) safely inside a Coroutine using withContext(Dispatchers.IO), or wrap the callback into a suspendCancellableCoroutine for clean, modern Android architecture.

How do I fix the NetworkOnMainThreadException?

This exception crashes your app because you attempted to make a synchronous network call (execute()) on the main UI thread. To fix it, use OkHttp’s asynchronous .enqueue() method instead, which automatically runs the request on a background thread.

Why is my OkHttp request timing out?

Timeouts occur when the server takes too long to respond or the user has a poor internet connection. By default, OkHttp has a 10-second timeout. You can extend this by configuring .connectTimeout(), .readTimeout(), and .writeTimeout() on your OkHttpClient.Builder.


📌 Full Course Playlist https://www.youtube.com/playlist?list=PLO1OrQEU0vHNmD9Xqzs-qXwzzwrDvdhVu

#Tutorial
0 Introduction
1 Setting up Android Studio IDE
2 Mastering Android Studio: Navigating the IDE & Project Structure
3 Android Activity & Lifecycle Explained
4 Android Services: Background, Foreground, and Bound Services Explained
5 Android Broadcast Receivers: The Complete Guide to Listening and Sending Events
6 Android Content Provider API Tutorial: Access User Data Safely (Kotlin)
7 How to Build UI with Jetpack Compose: A Beginner’s Guide
8 Android Runtime Permissions in Kotlin and Jetpack Compose: Step-by-Step Guide
9 Android Intents Guide: Master Screen Navigation and Data Sharing
10 Android Room Database: Complete CRUD Tutorial with Kotlin
11 Android Internal Storage: File I/O Tutorial
12 Android MediaStore API Tutorial: How to Save and Read Files
13 Master Storage Access Framework in Jetpack Compose
14 How to Create Android Notifications with Jetpack Compose & Kotlin
15 How to Use SharedPreferences in Android (Kotlin & Compose)
16 OkHttp Android Tutorial: Complete Kotlin Guide

~ ~ THANK YOU FOR READING ~ ~

Share: