Calling an API in Android with Retrofit and Coroutines

    Atul Kabra4 min readUpdated

    Calling an API in Android with Retrofit and Coroutines

    To fetch data from a web API in Android, the standard approach is Retrofit (a type-safe HTTP client) combined with Kotlin coroutines so the network call runs off the main thread. You define an interface describing the endpoints, model the JSON response as Kotlin data classes, build a Retrofit instance, and call the endpoint from a suspend function. This article walks through each piece with working code.

    The Big Picture

    Networking on Android has three rules you must respect:

    1. Never call the network on the main thread. It will freeze the UI or crash. Use coroutines.
    2. Declare the INTERNET permission in your AndroidManifest.xml (it is a normal permission, granted at install).
    3. Always handle failure. Networks drop, servers return errors. Wrap calls in error handling.

    Add <uses-permission android:name="android.permission.INTERNET" /> to your manifest, then add the Retrofit and a JSON converter dependency in your module's build.gradle.

    Step 1: Model the JSON

    Suppose the API returns a list of courses like [{"id": 1, "title": "Android Basics"}]. Model one item as a data class.

    // Field names should match the JSON keys (or use @SerializedName to map them)
    data class Course(
        val id: Int,
        val title: String
    )
    

    Step 2: Declare the API Interface

    Retrofit turns an annotated interface into a working HTTP client. The suspend keyword lets the call run inside a coroutine.

    import retrofit2.http.GET
    
    interface CourseApi {
        // GET request to the "courses" path; returns a list once it completes
        @GET("courses")
        suspend fun getCourses(): List<Course>
    }
    

    Step 3: Build the Retrofit Instance

    Create Retrofit once with the base URL and a converter that turns JSON into your data classes.

    import retrofit2.Retrofit
    import retrofit2.converter.gson.GsonConverterFactory
    
    // Build once and reuse. The base URL must end with a slash.
    fun createApi(): CourseApi {
        return Retrofit.Builder()
            .baseUrl("https://example.com/api/")
            .addConverterFactory(GsonConverterFactory.create()) // JSON -> objects
            .build()
            .create(CourseApi::class.java)
    }
    

    Want to learn this properly?

    Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.

    Browse courses

    Step 4: Call It Safely

    Run the call in a coroutine on a background dispatcher and wrap it in try/catch so a network failure does not crash the app.

    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.withContext
    
    // A sealed result type makes success and failure explicit
    sealed interface CourseResult {
        data class Success(val courses: List<Course>) : CourseResult
        data class Failure(val message: String) : CourseResult
    }
    
    suspend fun loadCourses(api: CourseApi): CourseResult = withContext(Dispatchers.IO) {
        try {
            val courses = api.getCourses() // network happens here, off main thread
            CourseResult.Success(courses)
        } catch (e: Exception) {
            // catch network/parse errors and report them instead of crashing
            CourseResult.Failure(e.message ?: "Unknown error")
        }
    }
    

    Step 5: Show the Result in Compose

    Trigger the call from a coroutine scope tied to your screen and store the result as state. Typically you would put this logic in a ViewModel for a real app; the key idea is that the UI reacts to the loaded state and never blocks.

    Common Mistakes

    • Forgetting the INTERNET permission. Without it, every call fails with a security exception.
    • Calling the API on the main thread. This freezes the UI; modern Android will throw a network-on-main-thread error. Use coroutines and a background dispatcher.
    • Not handling errors. A dropped connection should show a message, not crash the app. Always try/catch or use a result type.
    • Mismatched JSON field names. If a data class field does not match the JSON key, it stays null or fails to parse. Use @SerializedName to map differing names.
    • Building Retrofit on every call. Build it once and reuse it; recreating it wastes resources.
    • Hard-coding a URL without a trailing slash. Retrofit's baseUrl must end with / or you will get confusing path errors.

    FAQ

    Do I have to use Retrofit? No, but it is the most common and well-supported choice. It handles JSON parsing and gives you a clean, type-safe interface.

    What about authentication tokens? You attach them via headers, often through an interceptor. Learn the basic call first, then add auth.

    How do I show a loading spinner? Keep a loading state in your UI: set it true before the call, false after, and show a progress indicator while it is true.

    Keep Going

    Once you have data from the internet, you often cache it on the device; see Local Storage in Android. Some APIs and features also need runtime permissions, covered in Permissions in Android. The full path is at the Android learning hub.


    Want to build an app that talks to a real API, with guidance? Join the waitlist for the Android Development course at Infoplanet in Jalgaon.

    Want to learn this properly?

    Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.

    Browse courses
    Atul Kabra

    Founder, Infoplanet

    Atul Kabra founded Infoplanet in 2001 and has spent over two decades teaching programming — C, C++, Java, databases and more — to students across Maharashtra.

    Related guides