All about retrofit
Last Updated: August 2026
If you are looking for the Retrofit dependency in Android, this guide will show you exactly how to add Retrofit to an Android Kotlin project and use it to call a REST API.
Retrofit is one of the most popular networking libraries used in Android development. It allows developers to define API endpoints using simple Kotlin interfaces and annotations such as @GET, @POST, @Path, and @Query.
In this tutorial, you’ll learn how to:
- Add the Retrofit dependency
- Add the Gson converter
- Configure Internet permission
- Create a Kotlin data class
- Create an API interface
- Build a Retrofit client
- Make a GET API request
- Use Retrofit with Kotlin Coroutines
- Handle HTTP responses
- Structure Retrofit with MVVM
- Avoid common Retrofit mistakes
Let’s start with the dependency itself.
Retrofit Dependency for Android
As of 2026, Retrofit 3.0.0 is the current stable release.
If your Android project uses Kotlin DSL, open:
app/build.gradle.kts
Add:
dependencies {
implementation("com.squareup.retrofit2:retrofit:3.0.0")
implementation("com.squareup.retrofit2:converter-gson:3.0.0")
}
The first dependency adds Retrofit itself.
The second dependency adds Gson support so Retrofit can convert JSON responses into Kotlin objects.
After adding the dependencies, sync your Gradle project.
What does the Retrofit dependency do?
This dependency:
implementation("com.squareup.retrofit2:retrofit:3.0.0")
provides the core Retrofit functionality.
It allows us to create API interfaces such as:
interface ApiService {
@GET("posts")
suspend fun getPosts(): List<Post>
}
Retrofit generates the implementation of this interface for us.
Retrofit Dependency Using Version Catalog
Many modern Android Studio projects use a Version Catalog.
If your project contains:
gradle/libs.versions.toml
you can manage the Retrofit version there instead.
Add:
[versions]
retrofit = "3.0.0"
[libraries]
retrofit = { module = “com.squareup.retrofit2:retrofit”, version.ref = “retrofit” } retrofit-converter-gson = { module = “com.squareup.retrofit2:converter-gson”, version.ref = “retrofit” }
Then inside app/build.gradle.kts:
dependencies {
implementation(libs.retrofit)
implementation(libs.retrofit.converter.gson)
}
Why use a Version Catalog?
Suppose your application has multiple modules.
Without a Version Catalog, you may repeat:
implementation("com.squareup.retrofit2:retrofit:3.0.0")
in several Gradle files.
A Version Catalog centralizes dependency versions, making larger projects easier to maintain.
What is Retrofit in Android?
Retrofit is a type-safe HTTP client used for Android and JVM applications.
It converts your API definition into callable Kotlin or Java objects.
For example, suppose an API provides:
Instead of manually creating and managing the entire HTTP request, we can define:
@GET("posts")
suspend fun getPosts(): List<Post>
Retrofit handles the networking integration for us.
The basic flow looks like this:
Android UI
↓
ViewModel
↓
Repository
↓
ApiService
↓
Retrofit
↓
HTTP Request
↓
REST API
↓
JSON Response
↓
Gson Converter
↓
Kotlin Object
This makes networking code significantly easier to organize.
Step 1: Add Internet Permission
An Android application needs Internet permission to communicate with an API.
Open:
AndroidManifest.xml
Add:
<uses-permission android:name="android.permission.INTERNET" />
For example:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:theme="@style/Theme.MyApplication">
</application>
</manifest>
You don’t need to request this permission from the user at runtime.
Step 2: Create a Kotlin Data Class
For this tutorial, we’ll use JSONPlaceholder.
Suppose the server returns:
{
"userId": 1,
"id": 1,
"title": "Example Post",
"body": "Example body"
}
Create:
Post.kt
data class Post(
val userId: Int,
val id: Int,
val title: String,
val body: String
)
The Gson converter can deserialize the JSON response into this Kotlin class.
Conceptually:
JSON Response
↓
Gson Converter
↓
Post Object
Instead of manually extracting individual JSON fields, we work directly with Kotlin objects.
Step 3: Create the Retrofit API Interface
Create:
ApiService.kt
Add:
import retrofit2.http.GET
interface ApiService {
@GET("posts")
suspend fun getPosts(): List<Post>
}
There are two important parts here.
What does @GET mean?
@GET("posts")
tells Retrofit that this endpoint uses the HTTP GET method.
If our base URL is:
https://jsonplaceholder.typicode.com/
Retrofit combines it with:
posts
to call:
https://jsonplaceholder.typicode.com/posts
Why is the function suspend?
We declared:
suspend fun getPosts()
because we’re using Kotlin Coroutines.
Network operations shouldn’t block Android’s main thread.
A suspend API function integrates naturally with coroutine-based application architecture.
Step 4: Create the Retrofit Instance
Create:
RetrofitClient.kt
Add:
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
private const val BASE_URL =
"https://jsonplaceholder.typicode.com/"
val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(
GsonConverterFactory.create()
)
.build()
.create(ApiService::class.java)
}
}
Let’s understand each part.
Retrofit.Builder()
Retrofit.Builder()
starts building a Retrofit configuration.
baseUrl()
.baseUrl(BASE_URL)
defines the base URL used by our API.
A very common mistake is forgetting the trailing slash.
Correct:
https://example.com/api/
Avoid:
https://example.com/api
addConverterFactory()
.addConverterFactory(
GsonConverterFactory.create()
)
tells Retrofit to use Gson for conversion.
For example:
API JSON
↓
GsonConverterFactory
↓
Kotlin Object
build()
.build()
creates the configured Retrofit instance.
create()
.create(ApiService::class.java)
creates an implementation of our ApiService.
This is an important Retrofit concept:
You define the API interface; Retrofit creates its implementation.
Step 5: Make a GET Request Using Kotlin Coroutines
For a simple Activity example:
lifecycleScope.launch {
try {
val posts =
RetrofitClient.apiService.getPosts()
posts.forEach { post ->
println(post.title)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
The flow is:
lifecycleScope.launch
↓
getPosts()
↓
Retrofit
↓
GET /posts
↓
Server
↓
JSON
↓
Gson
↓
List<Post>
Retrofit therefore removes much of the repetitive networking code we’d otherwise need.
Using Response With Retrofit
Sometimes we need more than the response body.
For example, we may need the HTTP status code.
Change:
suspend fun getPosts(): List<Post>
to:
import retrofit2.Response
import retrofit2.http.GET
interface ApiService {
@GET("posts")
suspend fun getPosts(): Response<List<Post>>
}
Now we can write:
val response =
RetrofitClient.apiService.getPosts()
if (response.isSuccessful) {
val posts = response.body()
posts?.forEach { post ->
println(post.title)
}
} else {
println(
"HTTP Error: ${response.code()}"
)
}
This gives us access to HTTP information such as:
200 → Success
400 → Bad Request
401 → Unauthorized
403 → Forbidden
404 → Not Found
500 → Internal Server Error
In a real application, you should design explicit loading, success and error states instead of simply printing errors.
Retrofit Annotations Explained
Retrofit uses annotations to describe API operations.
Some of the most important annotations are:
@GET@POST@PUT@DELETE@Path@Query@Body@Header
Let’s understand them.
@GET
Used to retrieve data.
@GET("users")
suspend fun getUsers(): List<User>
This could call:
GET /users
@POST
Usually used to create or submit data.
@POST("users")
suspend fun createUser(
@Body user: User
): Response<User>
@PUT
Commonly used to update a resource.
@PUT("users/{id}")
suspend fun updateUser(
@Path("id") id: Int,
@Body user: User
): Response<User>
@DELETE
Used to delete a resource.
@DELETE("users/{id}")
suspend fun deleteUser(
@Path("id") id: Int
): Response<Unit>
@Path
@Path replaces a dynamic part of an endpoint.
For example:
@GET("users/{id}")
suspend fun getUser(
@Path("id") id: Int
): User
Calling:
getUser(10)
results in an endpoint similar to:
/users/10
@Query
@Query adds a query parameter to the request URL.
@GET("users")
suspend fun getUsers(
@Query("page") page: Int
): List<User>
Calling:
getUsers(2)
creates a request similar to:
/users?page=2
@Body
@Body sends an object as the request body.
@POST("users")
suspend fun createUser(
@Body user: User
): Response<User>
The converter serializes the compatible Kotlin object into the request representation.
@Header
Headers are commonly required for authentication or other request metadata.
For example:
@GET("profile")
suspend fun getProfile(
@Header("Authorization")
token: String
): Response<User>
A production application may instead add common headers centrally using an OkHttp interceptor.
Retrofit With MVVM Architecture
The previous Activity example is useful for understanding Retrofit, but production networking logic generally shouldn’t live directly inside an Activity or Fragment.
A better structure is:
Activity / Fragment / Compose
↓
ViewModel
↓
Repository
↓
ApiService
↓
Retrofit
↓
Server
For example:
class PostRepository(
private val apiService: ApiService
) {
suspend fun getPosts(): List<Post> {
return apiService.getPosts()
}
}
The ViewModel can depend on the repository:
class PostViewModel(
private val repository: PostRepository
) : ViewModel() {
fun loadPosts() {
viewModelScope.launch {
try {
val posts =
repository.getPosts()
// Update UI state here
} catch (e: Exception) {
// Update error state here
}
}
}
}
Now the ViewModel doesn’t need to know the details of how Retrofit is configured.
This separation becomes increasingly important as the application grows.
Retrofit + Repository + ViewModel Flow
Think of each layer as having a specific responsibility.
UI
│
│ User requests data
↓
ViewModel
│
│ Requests business/application data
↓
Repository
│
│ Selects data source
↓
ApiService
│
│ Defines endpoint
↓
Retrofit
│
│ Executes network integration
↓
REST API
The response then travels back through the layers:
REST API
↓
Retrofit
↓
Repository
↓
ViewModel
↓
UI State
↓
Screen
This structure is much easier to maintain than putting networking, business logic and UI logic in one Activity.
Common Retrofit Errors and Solutions
1. Internet Permission Missing
Make sure your manifest contains:
<uses-permission android:name="android.permission.INTERNET" />
Without Internet permission, your application cannot normally perform the required network communication.
2. Base URL Doesn’t End With /
Incorrect:
private const val BASE_URL =
"https://example.com/api"
Use:
private const val BASE_URL =
"https://example.com/api/"
Retrofit requires a properly structured base URL.
3. Wrong API Endpoint
Suppose:
BASE_URL =
"https://example.com/api/"
and:
@GET("users")
Retrofit will request:
https://example.com/api/users
Check the server’s API documentation carefully if you receive a 404.
4. Kotlin Model Doesn’t Match JSON
Suppose the server returns:
{
"id": 10,
"username": "androiddev"
}
Your model needs to correctly represent the response contract.
For example:
data class User(
val id: Int,
val username: String
)
When API field names and Kotlin property names differ, configure the serialization mapping appropriately.
5. HTTP Instead of HTTPS
Modern Android applications generally should communicate over HTTPS.
Prefer:
https://example.com
rather than:
http://example.com
Cleartext HTTP traffic may also be restricted by Android’s network security configuration.
6. Doing Too Much Inside Activity
Avoid building your entire networking architecture inside:
MainActivity.kt
A better project might contain:
data/
remote/
ApiService.kt
RetrofitClient.kt
repository/
PostRepository.kt
model/
Post.kt
ui/
PostViewModel.kt
MainActivity.kt
As the application grows, you can evolve this into a fuller Clean Architecture structure.
Retrofit Best Practices for Production Android Apps
A tutorial project can call Retrofit directly.
Production applications usually need more structure.
Consider the following practices.
Use Repository Pattern
Keep networking implementation outside the UI layer.
UI → ViewModel → Repository → API
Use Dependency Injection
Instead of manually constructing Retrofit everywhere, use a dependency-injection solution such as Hilt or Dagger when appropriate for your architecture.
Handle Errors Properly
Don’t rely only on:
e.printStackTrace()
Your UI should be able to represent states such as:
Loading
Success
Network Error
Server Error
Authentication Error
Empty Result
Keep the Base URL Centralized
Don’t repeat API URLs throughout your project.
Use Interceptors for Common Request Logic
Authentication headers, logging and other shared request concerns can often be implemented with OkHttp interceptors.
Don’t Expose Network Models Everywhere
In larger applications, consider separating API DTOs from domain models.
For example:
API Response
↓
PostDto
↓
Mapper
↓
Post
↓
Domain / UI
This prevents API changes from unnecessarily affecting the rest of your application.
Retrofit 3.0.0 vs Retrofit 2.x
Retrofit 3.0.0 is the current stable release.
One notable change in Retrofit 3 is the move from the older OkHttp 3.14 dependency to OkHttp 4.12.
Retrofit 3.x maintains forward binary compatibility with Retrofit 2.x, which helps reduce migration friction for existing libraries and applications.
For a new Android project in 2026, use the current stable version unless your project’s dependency requirements give you a specific reason to remain on an older release.
Frequently Asked Questions About Retrofit
What is the latest Retrofit dependency for Android?
As of August 2026, the current stable Retrofit release is 3.0.0.
The core dependency is:
implementation(
"com.squareup.retrofit2:retrofit:3.0.0"
)
For Gson:
implementation(
"com.squareup.retrofit2:converter-gson:3.0.0"
)
Always verify dependency versions before using them in a long-lived production project.
What is Retrofit used for in Android?
Retrofit is used to integrate HTTP APIs with Android/JVM applications.
Developers commonly use it for operations such as:
- Getting data from a REST API
- Sending form/application data
- User login APIs
- Registration APIs
- Loading product data
- Loading user profiles
- Updating server resources
- Deleting resources
- Working with authenticated APIs
Is Retrofit only for Kotlin?
No.
Retrofit can be used with both Kotlin and Java.
Kotlin Android projects commonly combine Retrofit with Coroutines.
Does Retrofit require Internet permission?
Yes.
Add:
<uses-permission android:name="android.permission.INTERNET" />
inside your Android manifest.
Does Retrofit support Kotlin Coroutines?
Yes.
You can define Retrofit endpoints using suspend functions:
@GET("posts")
suspend fun getPosts(): List<Post>
This works naturally with coroutine scopes such as viewModelScope.
What is GsonConverterFactory in Retrofit?
GsonConverterFactory provides Gson-based serialization/deserialization for Retrofit.
For a response:
{
"id": 1,
"name": "Android"
}
it can deserialize the JSON into an appropriate Kotlin object.
Retrofit vs OkHttp: What is the difference?
OkHttp is the underlying HTTP client commonly used by Retrofit.
A simplified way to understand the relationship is:
Your Kotlin Code
↓
Retrofit
↓
OkHttp
↓
HTTP
↓
Server
Retrofit provides the higher-level API-interface and conversion abstraction, while OkHttp handles the underlying HTTP communication.
Should I use Retrofit with MVVM?
Retrofit works very well with MVVM.
A typical architecture is:
Compose / Activity / Fragment
↓
ViewModel
↓
Repository
↓
ApiService
↓
Retrofit
This keeps network implementation separate from UI logic.
Conclusion
Adding the Retrofit dependency in Android is easy, but understanding where Retrofit belongs in an Android architecture is more important than simply copying a Gradle dependency.
For a basic Kotlin project, you need:
Retrofit
+
JSON Converter
+
Internet Permission
+
Data Class
+
ApiService
+
Retrofit Client
+
Coroutines
For a production Android application, the architecture typically grows toward:
UI
↓
ViewModel
↓
Repository
↓
ApiService
↓
Retrofit / OkHttp
↓
REST API
Once you’re comfortable with this setup, the next topics to learn are Retrofit annotations, POST requests, error handling, OkHttp interceptors, authentication, MVVM integration, Kotlin Coroutines, StateFlow and dependency injection.
