# Kotlin and Android with the CurrencyCore SDK

> Use the official Kotlin SDK on Android, keep the key out of source, call convert from coroutines, and handle ClientException.

Canonical: https://currency-core.com/docs/guides/kotlin-android/
Updated: 2026-11-11
Published: 2026-11-11

---
The official Kotlin SDK is generated from the OpenAPI contract and works on Android and the JVM. It configures bearer auth, returns typed models, and exposes `convert`, `ratesByBase`, `history`, and the analytics methods. Wrap its blocking calls in `Dispatchers.IO`.

The key belongs in `local.properties` during development and reaches code through `BuildConfig`. Add `buildFeatures { buildConfig = true }` for Android Gradle Plugin 8 and later, where build config generation is off by default. Do not commit `local.properties`.

```kotlin
// app/build.gradle.kts

val local = Properties().apply {
    val file = rootProject.file("local.properties")
    if (file.exists()) file.inputStream().use { load(it) }
}

android {
    defaultConfig {
        minSdk = 26
        buildConfigField(
            "String",
            "CURRENCYCORE_API_KEY",
            "\"${local.getProperty("CURRENCYCORE_API_KEY") ?: ""}\"",
        )
    }
    buildFeatures { buildConfig = true }
}

dependencies {
    implementation("com.github.CurrencyCore:currencycore-kotlin:0.1.2")
}
```

```kotlin

class CurrencyCoreException(
    val status: Int,
    val code: String,
    override val message: String,
) : Exception(message)

class CurrencyRepository(
    private val apiKey: String = BuildConfig.CURRENCYCORE_API_KEY,
) {
    private val api = CurrencyCore.client(apiKey = apiKey)

    suspend fun convert(from: String, to: String, amount: Double) =
        withContext(Dispatchers.IO) {
            require(apiKey.isNotBlank()) {
                "Set CURRENCYCORE_API_KEY in local.properties"
            }

            try {
                api.convert(
                    from = from,
                    to = to,
                    amount = BigDecimal.valueOf(amount),
                )
            } catch (error: ClientException) {
                throw CurrencyCoreException(
                    error.statusCode,
                    "client_error",
                    error.message ?: "Request failed",
                )
            } catch (error: ServerException) {
                throw CurrencyCoreException(
                    error.statusCode,
                    "server_error",
                    error.message ?: "Server error",
                )
            }
        }
}
```

The SDK returns the API shape, with one entry per target:

```json
{
  "from": "USD",
  "amount": 250,
  "date": "2026-11-10",
  "results": [{ "to": "JPY", "result": 38750.0, "rate": 155.0 }]
}
```

`ClientException` covers 4xx responses and `ServerException` covers 5xx. Both expose `statusCode`. Branch on the status: `400` is input, `401` is auth, `403` is a plan or history window, and `429` is a limit. On a `429`, read `Retry-After` before retrying.

## Production notes

- A shipped APK exposes compiled constants, including `BuildConfig` fields. Obfuscation raises the effort but does not make the key secret. Use a backend proxy when the key must stay private.
- Rates are central-bank reference rates polled hourly, and the underlying fixings are daily. They are reference values, not executable quotes.
- A `403` can mean a plan gate or a date outside the Free 30-day history window.
- Cache by `date` and base, and retry only `429` and `5xx` with backoff and jitter.

## Next steps

- [Official SDKs](/docs/sdks/) for TypeScript, Python, Dart and Swift
- [API reference](/docs/api/) and [error codes](/docs/errors/)
- [Retries and backoff](/docs/guides/retries/) for safe retry rules
- [All developer guides](/docs/guides/)
