# Retrofit User Guide

Retrofit turns your HTTP API into a type-safe Java or Kotlin interface. You describe each endpoint with an annotated method, and Retrofit generates a working client that builds requests, executes them over [OkHttp](https://square.github.io/okhttp/), and deserializes responses into your own objects. This guide shows you how to use every part of that surface — from your first request to multipart uploads, streaming downloads, RxJava and coroutine integration, and testing.

This guide covers Retrofit **3.0.0** (Maven coordinates `com.squareup.retrofit2:retrofit:3.0.0`). Written from the library source.

---

## 1. Quick Start

Define an interface that describes the API, ask Retrofit to implement it, then call a method.

```java
// 1. Describe the endpoint.
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

// 2. Build a Retrofit instance and create the implementation.
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

// 3. Make a call.
Call<List<Repo>> repos = service.listRepos("octocat");
List<Repo> result = repos.execute().body();
```

The `Retrofit` class generates an implementation of `GitHubService` at runtime. Each `Call` returned by the service can make a synchronous or asynchronous HTTP request to the remote server.

> [!NOTE]
> Retrofit only deserializes response bodies into OkHttp's `ResponseBody` type out of the box. To map a response onto your own class (such as `List<Repo>` above), you must add a **converter** like Gson. See [§5 Converting bodies](#5-converting-bodies-json-xml-and-more).

> [!TIP]
> Reuse a single `Retrofit` instance (and the single `OkHttpClient` behind it) across your whole application. They are expensive to create and hold the connection pool and thread pools that make repeated requests fast.

---

## 2. Installation

Retrofit requires at minimum **Java 8+** or **Android API 21+**.

### Add the dependency

=== "Gradle (Kotlin DSL)"

    ```kotlin
    implementation("com.squareup.retrofit2:retrofit:3.0.0")
    ```

=== "Gradle (Groovy DSL)"

    ```groovy
    implementation 'com.squareup.retrofit2:retrofit:3.0.0'
    ```

=== "Maven"

    ```xml
    <dependency>
      <groupId>com.squareup.retrofit2</groupId>
      <artifactId>retrofit</artifactId>
      <version>3.0.0</version>
    </dependency>
    ```

Retrofit depends on [OkHttp](https://square.github.io/okhttp/) and pulls it in transitively — you do not need to declare OkHttp yourself unless you want to pin a specific version. As of Retrofit 3.0, OkHttp is the Kotlin-based 4.12 release, so a small transitive Kotlin runtime dependency comes along with it.

> [!NOTE]
> Retrofit 3.x is **forward binary-compatible** with 2.x. Libraries compiled against Retrofit 2.x can still run against 3.x.

### Add a converter (almost always needed)

Pick the converter that matches your API's wire format and add it alongside the core dependency. For JSON with Gson:

=== "Gradle (Kotlin DSL)"

    ```kotlin
    implementation("com.squareup.retrofit2:converter-gson:3.0.0")
    ```

=== "Maven"

    ```xml
    <dependency>
      <groupId>com.squareup.retrofit2</groupId>
      <artifactId>converter-gson</artifactId>
      <version>3.0.0</version>
    </dependency>
    ```

The full list of converters and adapters is in [§5](#5-converting-bodies-json-xml-and-more) and [§7](#7-changing-the-return-type-call-adapters).

### Keep your versions aligned with the BOM (optional)

If you use several Retrofit modules (core + converters + adapters), import the **bill of materials** so every module stays on the same version:

```kotlin
implementation(platform("com.squareup.retrofit2:retrofit-bom:3.0.0"))
implementation("com.squareup.retrofit2:retrofit")
implementation("com.squareup.retrofit2:converter-moshi")
implementation("com.squareup.retrofit2:adapter-rxjava3")
```

With the BOM in place you omit version numbers on the individual modules.

> [!TIP]
> Want the bleeding edge? In-development snapshots are published to the Central Portal Snapshots repository at `https://central.sonatype.com/repository/maven-snapshots/`. Add that as a repository to use a `-SNAPSHOT` build.

---

## 3. Defining Your API Interface

An API in Retrofit is a plain Java (or Kotlin) **interface**. Every method maps to one HTTP request, and annotations on the method and its parameters describe exactly how that request is shaped.

```java
public interface GitHubService {
  @GET("/repos/{owner}/{repo}/contributors")
  Call<List<Contributor>> contributors(
      @Path("owner") String owner,
      @Path("repo") String repo);
}
```

> [!IMPORTANT]
> API declarations must be **interfaces** — Retrofit throws `IllegalArgumentException: API declarations must be interfaces.` if you pass a class. Interfaces also may not declare type parameters (generics on the interface itself are rejected).

You can spread methods across multiple interfaces (one per resource, for example) and create each with its own `retrofit.create(...)` call against the same `Retrofit` instance.

---

## 4. Request Declarations: The Annotation Reference

This is the heart of Retrofit. Every annotation below lives in the `retrofit2.http` package. They fall into five groups: **request method**, **URL & parameters**, **headers**, **request body**, and **form / multipart**.

### 4.1 Request method annotations

Every service method must carry exactly one HTTP method annotation. There are eight built in:

| Annotation | HTTP verb | Has request body? |
|---|---|---|
| `@GET` | GET | No |
| `@POST` | POST | Yes |
| `@PUT` | PUT | Yes |
| `@PATCH` | PATCH | Yes |
| `@DELETE` | DELETE | No (use `@HTTP` for a body) |
| `@HEAD` | HEAD | No |
| `@OPTIONS` | OPTIONS | No |
| `@HTTP` | *custom* | Configurable |

Each verb annotation takes the endpoint path as its value:

```java
@GET("users/list")
Call<List<User>> userList();
```

You can include static query parameters directly in the path:

```java
@GET("users/list?sort=desc")
Call<List<User>> sortedUserList();
```

#### Custom verbs and `DELETE` with a body — `@HTTP`

`@HTTP` lets you use a non-standard method, set the path, and declare whether the request carries a body:

```java
public interface Service {
  @HTTP(method = "CUSTOM", path = "custom/endpoint/")
  Call<ResponseBody> customEndpoint();

  // DELETE with a request body (which @DELETE alone cannot express):
  @HTTP(method = "DELETE", path = "remove/", hasBody = true)
  Call<ResponseBody> deleteObject(@Body RequestBody object);
}
```

| `@HTTP` element | Meaning | Default |
|---|---|---|
| `method` | The HTTP verb to send (required) | — |
| `path` | Relative/absolute path or full URL | `""` |
| `hasBody` | Whether the request sends a body | `false` |

### 4.2 URL manipulation

#### `@Path` — fill in URL segments

A replacement block is an alphanumeric string wrapped in `{` and `}`. A parameter annotated with `@Path` using the same name fills it in. Values are converted to strings and URL-encoded.

```java
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);
```

Calling `groupList(42)` requests `group/42/users`.

By default the value is URL-encoded. Use `encoded = true` if your argument is already encoded:

```java
@GET("/user/{name}")
Call<ResponseBody> notEncoded(@Path(value = "name", encoded = true) String name);
```

`encoded("John%Doe")` yields `/user/John%25Doe`; `notEncoded("John%Doe")` yields `/user/John%Doe`.

> [!CAUTION]
> `@Path` parameters may **not** be `null`. Passing `null` throws an exception when the request is built.

#### `@Query` — append query parameters

```java
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
```

`@Query` values are URL-encoded and appended to the URL. `null` values are **ignored** (the parameter is omitted). Passing a `List` or array produces one query parameter per non-null item:

```java
@GET("/friends")
Call<ResponseBody> friends(@Query("group") String... groups);
```

`friends("coworker", "bowling")` yields `/friends?group=coworker&group=bowling`. Use `@Query(value = "group", encoded = true)` to skip encoding.

#### `@QueryMap` — many query parameters at once

```java
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
```

Every map entry becomes a query parameter. With `@QueryMap` the map itself, its keys, and its values may **not** be `null`.

#### `@QueryName` — value-less query parameters

For query parameters that are just a name with no `=value`:

```java
@GET("/friends")
Call<ResponseBody> friends(@QueryName String filter);
```

`friends("contains(Bob)")` yields `/friends?contains(Bob)`. Lists and arrays produce one entry each.

#### `@Url` — fully dynamic URLs

When the whole URL is computed at runtime, omit the path on the verb annotation and annotate the first parameter with `@Url`:

```java
@GET
Call<ResponseBody> list(@Url String url);
```

The `@Url` value is resolved against the base URL using the same rules as a relative link in a web page (see [§6.1](#61-base-url-rules)).

### 4.3 Header manipulation

#### `@Headers` — static headers on a method

```java
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

@Headers({
  "Accept: application/vnd.github.v3.full+json",
  "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
```

#### `@Header` — a header from a parameter

```java
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization);
```

If the value is `null`, the header is **omitted**. Otherwise `toString()` is called on the value. A `List` or array produces one header per non-null item.

#### `@HeaderMap` — many headers from a map

```java
@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers);
```

> [!WARNING]
> Headers do **not** overwrite each other. Every header with the same name is included in the request. If you need replacement semantics, manage it yourself.

> [!NOTE]
> Header parameter keys and values allow only ASCII by default. To send non-ASCII values (not strictly HTTP-compliant but often required), set `allowUnsafeNonAsciiValues = true` on `@Header`, `@Headers`, or `@HeaderMap`:
> ```java
> @GET("/")
> Call<ResponseBody> foo(@Header(value = "Accept-Language", allowUnsafeNonAsciiValues = true) String lang);
> ```

Headers that must be added to **every** request are best set with an [OkHttp interceptor](https://square.github.io/okhttp/features/interceptors/) rather than repeated on each method (see [§6.2](#62-customizing-the-http-client)).

### 4.4 The request body — `@Body`

Send any object as the request body with `@Body`. It is serialized by the `Retrofit` instance's converter.

```java
@POST("users/new")
Call<User> createUser(@Body User user);
```

> [!IMPORTANT]
> If no converter is installed, only OkHttp's `RequestBody` type can be used as a `@Body`. `@Body` parameters may not be `null`.

### 4.5 Form-encoded requests

Add `@FormUrlEncoded` to the method and annotate each pair with `@Field`. The request is sent with `application/x-www-form-urlencoded`.

```java
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
```

`updateUser("Bob Smith", "President")` sends the body `first_name=Bob+Smith&last_name=President`. `null` field values are ignored; passing a `List` or array produces a field pair per item.

For a variable set of fields, use `@FieldMap`:

```java
@FormUrlEncoded
@POST("/things")
Call<ResponseBody> things(@FieldMap Map<String, String> fields);
```

Both `@Field` and `@FieldMap` accept `encoded = true` if the names/values are already URL-encoded. A `null` map, key, or value is not allowed for `@FieldMap`.

### 4.6 Multipart requests and file upload

Add `@Multipart` to the method and declare each part with `@Part`.

```java
@Multipart
@PUT("user/photo")
Call<User> updateUser(
    @Part("photo") RequestBody photo,
    @Part("description") RequestBody description);
```

A `@Part` parameter is processed one of three ways depending on its type:

| Parameter type | Behavior | Name in annotation |
|---|---|---|
| `okhttp3.MultipartBody.Part` | Used directly | Omit the name: `@Part MultipartBody.Part part` |
| `okhttp3.RequestBody` | Used directly with its content type | Supply the name: `@Part("foo") RequestBody foo` |
| Any other type | Serialized by a converter | Supply the name: `@Part("foo") Image photo` |

```java
@Multipart
@POST("/")
Call<ResponseBody> example(
    @Part("description") String description,
    @Part(value = "image", encoding = "8-bit") RequestBody image);
```

The `encoding` element controls the part's `Content-Transfer-Encoding` (default `"binary"`). For a dynamic set of parts use `@PartMap Map<String, RequestBody>`. `@Part` values may be `null` (the part is omitted); a `@PartMap` map, key, or value may not be `null`.

### 4.7 Other parameter annotations

#### `@Streaming` — stream large response bodies

By default Retrofit buffers the entire response body into memory before handing it to you. For large downloads, add `@Streaming` so the `ResponseBody` is delivered as-is and you read it incrementally.

```java
@Streaming
@GET("download/{id}")
Call<ResponseBody> download(@Path("id") String id);
```

> [!CAUTION]
> With `@Streaming` you are responsible for closing the `ResponseBody` (e.g. with try-with-resources). The body must be consumed and closed on the calling thread.

#### `@Tag` — attach metadata for interceptors

`@Tag` attaches an object to the underlying OkHttp request using its type as the key. Interceptors can read it back to make per-request decisions (logging, metrics, auth scoping).

```java
@GET("/")
Call<ResponseBody> foo(@Tag String tag);
```

`null` tags are omitted. A parameterized type uses its raw type as the key (`List<String>` → `List.class`); primitives are boxed (`long` → `Long.class`). Duplicate tag types on one method are not allowed.

### 4.8 Annotation cheat sheet

| Annotation | Target | Purpose |
|---|---|---|
| `@GET` `@POST` `@PUT` `@PATCH` `@DELETE` `@HEAD` `@OPTIONS` | method | Standard HTTP verbs |
| `@HTTP` | method | Custom verb / `DELETE` with body |
| `@Path` | parameter | Fill a `{block}` in the URL |
| `@Query` | parameter | Append `?name=value` |
| `@QueryMap` | parameter | Append many query parameters |
| `@QueryName` | parameter | Append a value-less query parameter |
| `@Url` | parameter | Provide the whole URL at runtime |
| `@Header` | parameter | Set one header from an argument |
| `@HeaderMap` | parameter | Set many headers from a map |
| `@Headers` | method | Static headers |
| `@Body` | parameter | Object → request body via converter |
| `@FormUrlEncoded` | method | Send `application/x-www-form-urlencoded` |
| `@Field` / `@FieldMap` | parameter | Form fields |
| `@Multipart` | method | Send a multipart body |
| `@Part` / `@PartMap` | parameter | Multipart parts |
| `@Streaming` | method | Don't buffer the response body |
| `@Tag` | parameter | Attach a typed tag for interceptors |

---

## 5. Converting Bodies (JSON, XML, and more)

By default Retrofit can only read OkHttp `ResponseBody` and write `RequestBody`. **Converters** teach it to map your own types onto JSON, XML, protocol buffers, and so on. Add them with `addConverterFactory(...)`:

```java
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();
```

### 5.1 Built-in converters

| Library | Maven artifact | Notes |
|---|---|---|
| [Gson](https://github.com/google/gson) | `converter-gson` | JSON |
| [Jackson](https://github.com/FasterXML/jackson) | `converter-jackson` | JSON, XML, CBOR, YAML, etc. by supplying a different mapper + media type |
| [Moshi](https://github.com/square/moshi/) | `converter-moshi` | JSON |
| [Protobuf](https://developers.google.com/protocol-buffers/) | `converter-protobuf` | Protocol buffers |
| [Wire](https://github.com/square/wire) | `converter-wire` | Protocol buffers (Wire runtime) |
| [Simple XML](http://simple.sourceforge.net/) | `converter-simplexml` | XML |
| [JAXB](https://docs.oracle.com/javase/tutorial/jaxb/intro/index.html) | `converter-jaxb` | XML |
| JAXB 3 | `converter-jaxb3` | XML (Jakarta `jakarta.xml.bind`) |
| [Kotlin serialization](https://github.com/Kotlin/kotlinx.serialization/) | `converter-kotlinx-serialization` | Multiformat |
| Scalars | `converter-scalars` | `String` + primitives and their boxed types |

> [!TIP]
> The **scalars** converter (`converter-scalars`) handles plain `String`, `int`, `boolean`, and other primitives as request/response bodies. Add it when an endpoint returns or accepts a bare value rather than a JSON document.

### 5.2 Delegating (Optional) converters

These do not parse bytes themselves — they delegate to another converter and wrap a potentially-null result in an `Optional`:

| Wrapper type | Maven artifact |
|---|---|
| Guava `Optional<T>` | `converter-guava` |
| Java 8 `Optional<T>` | `converter-java8` |

Install the delegating converter **before** the real one so it wraps that converter's output.

### 5.3 Mixing converters

You can add more than one converter. Retrofit tries each in the order added until one handles the type. A common pattern is JSON for most endpoints plus the scalars converter for raw text:

```java
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(API_URL)
    .addConverterFactory(ScalarsConverterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build();
```

### 5.4 Custom converters

To support a format Retrofit doesn't ship (YAML, a bespoke text format, or a different library for an existing format), extend `Converter.Factory` and install an instance. The factory implements any of three methods — `responseBodyConverter`, `requestBodyConverter`, `stringConverter` — returning `null` for types it doesn't handle. See the [Admin Guide](/square/retrofit/admin-guide/) for the full SPI contract.

---

## 6. Configuring Retrofit

The `Retrofit.Builder` is where you assemble an instance. `baseUrl(...)` is the only required call.

```java
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")          // required
    .client(myOkHttpClient)                         // optional: custom HTTP client
    .addConverterFactory(MoshiConverterFactory.create())
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
    .validateEagerly(BuildConfig.DEBUG)             // optional: fail fast on bad declarations
    .build();
```

| Builder method | Purpose |
|---|---|
| `baseUrl(String / HttpUrl / URL)` | The API base URL (**required**) |
| `client(OkHttpClient)` | Use a configured OkHttp client (convenience for `callFactory`) |
| `callFactory(okhttp3.Call.Factory)` | Supply a custom call factory; `client(...)` sets this automatically |
| `addConverterFactory(Converter.Factory)` | Add a body/string converter |
| `addCallAdapterFactory(CallAdapter.Factory)` | Support return types other than `Call` |
| `callbackExecutor(Executor)` | Thread on which `Callback` methods run (for `Call` returns) |
| `validateEagerly(boolean)` | Validate every method at `create(...)` time instead of on first call |
| `build()` | Produce the `Retrofit` instance |

> [!TIP]
> Already have a `Retrofit` and want a variation? Call `retrofit.newBuilder()` to get a builder pre-populated with the existing configuration, change what you need, and `build()` a new instance that shares the same connection pool.

### 6.1 Base URL rules

The endpoint values on your annotations are resolved against the base URL exactly the way an `<a href="">` link resolves on a web page.

> [!IMPORTANT]
> **Base URLs should always end in `/`.** Retrofit enforces this — `baseUrl(...)` throws `IllegalArgumentException: baseUrl must end in /` otherwise. A trailing `/` ensures relative endpoint paths append correctly.

| Base URL | Endpoint | Result |
|---|---|---|
| `http://example.com/api/` | `foo/bar/` | `http://example.com/api/foo/bar/` |
| `http://example.com/api/` *(no `/`: `…/api`)* | `foo/bar/` | `http://example.com/foo/bar/` ⚠️ |
| `http://example.com/api/` | `/foo/bar/` (leading `/`) | `http://example.com/foo/bar/` (absolute) |
| `http://example.com/` | `https://github.com/square/retrofit/` | `https://github.com/square/retrofit/` (full URL wins) |

In short: a leading `/` on the endpoint makes it absolute (host kept, path replaced); a full URL on the endpoint replaces the host (and scheme, if present).

### 6.2 Customizing the HTTP client

Retrofit delegates all networking to OkHttp. To add logging, authentication, timeouts, caching, or any header that belongs on every request, configure an `OkHttpClient` and pass it with `.client(...)`:

```java
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new HttpLoggingInterceptor())
    .addInterceptor(chain -> {
      Request request = chain.request().newBuilder()
          .header("Authorization", "Bearer " + token)
          .build();
      return chain.proceed(request);
    })
    .build();

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(API_URL)
    .client(client)
    .addConverterFactory(GsonConverterFactory.create())
    .build();
```

> [!NOTE]
> If you don't call `client(...)` or `callFactory(...)`, Retrofit creates a default `OkHttpClient` for you. For production apps you almost always want to supply your own so you control timeouts, interceptors, and the connection pool.

---

## 7. Changing the Return Type (Call Adapters)

By default a service method returns `Call<T>`. **Call adapters** let it return something else — an RxJava `Observable`, a `CompletableFuture`, a Guava `ListenableFuture`, or (with no extra dependency) the value of a Kotlin `suspend` function. Add them with `addCallAdapterFactory(...)`.

### 7.1 Built-in call adapters

| Return types | Maven artifact |
|---|---|
| RxJava `Observable`, `Single` | `adapter-rxjava` |
| RxJava 2 `Observable`, `Flowable`, `Single`, `Completable`, `Maybe` | `adapter-rxjava2` |
| RxJava 3 `Observable`, `Flowable`, `Single`, `Completable`, `Maybe` | `adapter-rxjava3` |
| Guava `ListenableFuture` | `adapter-guava` |
| Java 8 `CompletableFuture` | `adapter-java8` |
| Kotlin `suspend` functions | *none needed — built in* |

```java
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(API_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
    .build();

public interface Service {
  @GET("users/{user}")
  Single<User> getUser(@Path("user") String user);
}
```

> [!NOTE]
> On the JVM, the Java 8 `CompletableFuture` adapter is available without any extra dependency on Java 8+ runtimes. The `adapter-java8` artifact exists for completeness; `suspend` and `CompletableFuture` support are built into the platform defaults.

### 7.2 Custom call adapters

To integrate a concurrency or task library Retrofit doesn't ship, extend `CallAdapter.Factory` and return an adapter that wraps the built-in `Call`. The Admin Guide documents the SPI in full.

---

## 8. Making Calls: Synchronous, Asynchronous, and Cancellation

A `Call<T>` represents a single request/response pair. **Each `Call` can be used only once.** To repeat a request (polling, retries), call `clone()` to get a fresh one.

### 8.1 Synchronous

```java
Call<List<Contributor>> call = service.contributors("square", "retrofit");
Response<List<Contributor>> response = call.execute();   // blocks
if (response.isSuccessful()) {
  List<Contributor> body = response.body();
}
```

> [!WARNING]
> `execute()` blocks the calling thread and throws `IOException` on a network problem. **Never call it on Android's main thread** — do it on a background thread or use the asynchronous API.

### 8.2 Asynchronous

```java
call.enqueue(new Callback<List<Contributor>>() {
  @Override public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
    if (response.isSuccessful()) {
      // response.body()
    } else {
      // response.errorBody() — server returned 4xx/5xx
    }
  }

  @Override public void onFailure(Call<List<Contributor>> call, Throwable t) {
    // network failure, or an error building the request / decoding the response
  }
});
```

> [!IMPORTANT]
> `onResponse` fires for **any** HTTP response, including 404 and 500. Always check `response.isSuccessful()` (true when the status is in `[200, 300)`) before reading `body()`. `onFailure` only fires for network errors or request/response processing errors — not for non-2xx statuses.

On Android, callbacks run on the main thread by default. On the plain JVM, callbacks run on the same background thread that performed the request.

### 8.3 Cancellation, state, and the request

```java
call.cancel();              // attempt to cancel an in-flight or not-yet-started call
boolean canceled = call.isCanceled();
boolean executed = call.isExecuted();   // already executed or enqueued?
Request original = call.request();      // the OkHttp request this call will send
Call<T> retry = call.clone();           // a fresh, re-usable copy
```

### 8.4 Working with `Response<T>`

| Method | Returns |
|---|---|
| `isSuccessful()` | `true` if the status code is in `[200, 300)` |
| `code()` | HTTP status code |
| `message()` | HTTP status message |
| `headers()` | Response headers |
| `body()` | Deserialized body of a **successful** response (may be `null`) |
| `errorBody()` | Raw `ResponseBody` of an **unsuccessful** response |
| `raw()` | The underlying `okhttp3.Response` |

> [!TIP]
> When the server returns an error status, the parsed `body()` is `null` and the unparsed error payload is in `errorBody()`. To map that error JSON onto your own error type, pull a converter from the `Retrofit` instance with `retrofit.responseBodyConverter(...)` and apply it to `errorBody()`.

---

## 9. Kotlin: Coroutines and `suspend` Functions

Retrofit understands Kotlin `suspend` functions natively — no adapter dependency required. A `suspend` method creates the call, executes it asynchronously, and suspends the coroutine until it completes.

Return a `Response<T>` to inspect status and headers yourself:

```kotlin
@GET("users")
suspend fun getUser(): Response<User>
```

Or return the body type directly. On a non-2xx response, an `HttpException` is thrown:

```kotlin
@GET("users")
suspend fun getUser(): User
```

```kotlin
val service = retrofit.create(GitHubService::class.java)

try {
  val user: User = service.getUser()
  // use user
} catch (e: HttpException) {
  // e.code(), e.message(), e.response()
} catch (e: IOException) {
  // network failure
}
```

> [!IMPORTANT]
> A `suspend` function must **not** declare a return type of `Call<T>` — suspend functions already execute asynchronously, and Retrofit rejects this with a clear error telling you to return the body type directly.

> [!NOTE]
> Use `Unit` as the body type (`suspend fun ping(): Unit`) when you don't care about the response payload — the body is discarded without deserialization. A `@HEAD` method must use `Void` (Java) or `Unit` (Kotlin) as its response type.

---

## 10. Testing Your API with `retrofit-mock`

The `retrofit-mock` module lets you stand in for the network so you can unit-test code that depends on your API interface, with realistic delays and a configurable failure rate.

```kotlin
implementation("com.squareup.retrofit2:retrofit-mock:3.0.0")
```

```java
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// Wrap it in a MockRetrofit with simulated network behavior.
NetworkBehavior behavior = NetworkBehavior.create();
MockRetrofit mockRetrofit = new MockRetrofit.Builder(retrofit)
    .networkBehavior(behavior)
    .build();

BehaviorDelegate<GitHubService> delegate = mockRetrofit.create(GitHubService.class);

// Provide a hand-written mock implementation that returns canned responses.
GitHubService mockService = new MockGitHubService(delegate);
```

Inside your mock implementation, hand canned data back through the delegate:

```java
public Call<List<Repo>> listRepos(String user) {
  List<Repo> repos = Arrays.asList(new Repo("octocat", "Hello-World"));
  return delegate.returningResponse(repos).listRepos(user);
}
```

`NetworkBehavior` lets you tune simulated latency, variance, and the probability of failures so tests exercise your error-handling paths. `Calls` (in the same module) provides helpers like `Calls.response(...)` and `Calls.failure(...)` to build standalone `Call` instances for tests.

> [!TIP]
> For integration-style tests that exercise real serialization and the HTTP stack, point Retrofit at OkHttp's `MockWebServer` instead. `retrofit-mock` is for unit tests where you want to bypass the network entirely.

---

## 11. ProGuard / R8

If you use **R8** (the default Android shrinker), Retrofit's shrinking and obfuscation rules are included automatically — there is nothing to do.

If you use **ProGuard**, add the rules from [`retrofit2.pro`](https://github.com/square/retrofit/blob/master/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro) manually. You will likely also need rules for [OkHttp](https://square.github.io/okhttp/r8_proguard/) and [Okio](https://square.github.io/okio/#r8-proguard), which are dependencies of Retrofit.

> [!CAUTION]
> Aggressive shrinking can strip the generic type information Retrofit reads at runtime, causing failures like *"Unable to create converter"* or *"Method return type must not include a type variable or wildcard"* in release builds only. If static keep rules aren't enough for your interfaces, the `retrofit-response-type-keeper` annotation processor generates the extra rules for every type referenced by your service interfaces.

---

## 12. Troubleshooting

### Error: `API declarations must be interfaces.`

**Cause:** You passed a class (or abstract class) to `retrofit.create(...)`.
**Solution:** Make your API declaration a Java/Kotlin `interface`.
**Prevention:** Retrofit only generates implementations for interfaces.

### Error: `baseUrl must end in /: <url>`

**Cause:** The base URL you supplied has no trailing slash.
**Solution:** Add a trailing `/` — e.g. `https://api.example.com/`, not `https://api.example.com`.
**Prevention:** Always end base URLs in `/`; see [§6.1](#61-base-url-rules).

### Error: `Could not locate ResponseBody converter for <type>`

**Cause:** No installed converter can deserialize the method's return type — most often you forgot to add a converter, or added it after a converter that consumes all types.
**Solution:** Add the matching `addConverterFactory(...)` (e.g. `GsonConverterFactory.create()`). If using an `Optional` converter, install it *before* the real one.
**Prevention:** Add a converter whenever a method returns anything other than `ResponseBody`.

### Error: `Could not locate call adapter for <type>`

**Cause:** The method returns a type (such as `Single` or `Observable`) with no matching call adapter installed.
**Solution:** Add the adapter factory — e.g. `addCallAdapterFactory(RxJava3CallAdapterFactory.create())`.
**Prevention:** Each non-`Call` return type needs its adapter registered on the builder.

### Error: `IllegalStateException: Base URL required.`

**Cause:** `build()` was called without `baseUrl(...)`.
**Solution:** Call `.baseUrl(...)` before `.build()`. It is the only required builder method.

### Error: `Suspend functions should not return Call`

**Cause:** A Kotlin `suspend` method declared a `Call<T>` return type.
**Solution:** Change the return type to the body type (e.g. `User`) or `Response<User>`.
**Prevention:** `suspend` functions execute asynchronously already; never wrap them in `Call`.

### Error: `'okhttp3.Response' is not a valid response body type. Did you mean ResponseBody?`

**Cause:** A method used `okhttp3.Response` (the raw HTTP response) as its body type.
**Solution:** Return `Call<ResponseBody>` for the raw body, or `Call<okhttp3.Response>` is not allowed — use Retrofit's `Response<T>` wrapper or `ResponseBody`.

### Error: `HEAD method must use Void or Unit as response type.`

**Cause:** A `@HEAD` method declared a body type other than `Void`/`Unit`.
**Solution:** Use `Call<Void>` (Java) or `suspend fun … : Unit` (Kotlin) — HEAD responses have no body.

### Calls succeed but `body()` is `null`

**Cause:** The response was not successful (status outside `[200, 300)`), so the parsed body is `null` and the payload is in `errorBody()`.
**Solution:** Check `response.isSuccessful()` first; read `response.errorBody()` for error responses.

### `@Path`/`@Body`/`@Part` throws on `null`

**Cause:** `@Path`, `@Body`, and named `@Part`/`@Field`-map values may not be `null`.
**Solution:** Use `@Query`/`@Header` (which silently omit `null`), or guarantee a non-null argument.

### Works in debug, fails in release (Android)

**Cause:** R8/ProGuard stripped generic type information or kept rules your interfaces need.
**Solution:** Ensure R8 is used (rules are automatic) or add `retrofit2.pro` for ProGuard; consider the `retrofit-response-type-keeper` processor. See [§11](#11-proguard--r8).

### `@XmlJavaTypeAdapter` ignored with the JAXB converter

**Cause:** A known open issue ([#3349](https://github.com/square/retrofit/issues/3349)): `JaxbConverterFactory` does not recognize `@XmlJavaTypeAdapter`.
**Solution:** Apply the type adapter at the field/package level your JAXB implementation honors, or pre/post-process the affected types until the issue is resolved.

---

## 13. FAQ

**Q: Do I need to add OkHttp as a dependency?**
No. Retrofit pulls OkHttp in transitively. Declare it explicitly only to pin a particular OkHttp version.

**Q: Should I create a new `Retrofit` instance per request?**
No. Create one `Retrofit` (and one `OkHttpClient`) and reuse them everywhere. They hold the connection pool and thread pools.

**Q: How do I add an `Authorization` header to every request?**
Use an OkHttp interceptor on the `OkHttpClient` you pass to `.client(...)`, not a per-method `@Header`. See [§6.2](#62-customizing-the-http-client).

**Q: Can one method's URL be completely dynamic?**
Yes — omit the path on the verb annotation and annotate the first parameter with `@Url`. See [§4.2](#42-url-manipulation).

**Q: How do I send a `DELETE` with a body?**
`@DELETE` has no body. Use `@HTTP(method = "DELETE", path = "...", hasBody = true)` with a `@Body` parameter.

**Q: How do I read the error body on a 4xx/5xx response?**
For `Call`, check `response.isSuccessful()` and read `response.errorBody()`. For `suspend` functions returning the body directly, catch `HttpException` and call `e.response()`.

**Q: What's the difference between `@Query` and `@Field`?**
`@Query` appends to the URL (`?name=value`). `@Field` builds a form-encoded request body and requires `@FormUrlEncoded` on the method.

**Q: How do I upload a file?**
Use a `@Multipart` method with a `@Part MultipartBody.Part` parameter (build the part from an OkHttp `RequestBody`). See [§4.6](#46-multipart-requests-and-file-upload).

**Q: Do `suspend` functions need the coroutines adapter?**
No. Coroutine support is built into Retrofit core — just declare a `suspend fun`.

**Q: My non-ASCII header value is rejected. What now?**
Set `allowUnsafeNonAsciiValues = true` on `@Header`/`@Headers`/`@HeaderMap`. Note this isn't strictly HTTP-compliant.

**Q: How do I test code that uses my API interface without hitting the network?**
Use `retrofit-mock` (`MockRetrofit` + `BehaviorDelegate` + `NetworkBehavior`) for unit tests, or OkHttp's `MockWebServer` for integration tests. See [§10](#10-testing-your-api-with-retrofit-mock).

**Q: Is Retrofit 3 a breaking change from 2?**
No. Retrofit 3.x maintains forward binary compatibility with 2.x. The main change in 3.0 is the upgrade to the Kotlin-based OkHttp 4.12.

**Q: How do I retry or poll a request?**
A `Call` is single-use. Call `call.clone()` to get a fresh, executable copy for each attempt.

**Q: Can I use multiple converters at once?**
Yes. Add them in priority order; Retrofit tries each until one handles the type. A common combo is scalars + a JSON converter.

---

_Generated June 2026 from square/retrofit at 8e83a24. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
