On this page
OpenAI's API (and similar provider APIs) is designed for server-to-server use with a secret key. Unlike Firebase AI Logic, which pairs with App Check for client apps, OpenAI keys must stay on your server. The Android app talks to your backend, which talks to OpenAI.
Not in code, BuildConfig, resources, native libraries or obfuscated strings. APKs can be decompiled and traffic inspected. See Securing API Keys.
The proxy architecture
What the backend does
- Authenticate the user (for example a Firebase Auth ID token) and optionally verify a Play Integrity token.
- Enforce quotas and rate limits per user and plan.
- Own the prompt: system instructions and model choice live on the server, so the client sends only user input.
- Validate and moderate input and output as your use case requires.
- Stream the provider response back to the client.
- Log usage (tokens, latency, errors) without storing sensitive content unless needed. See AI Data Retention.
The Android side: a provider-agnostic interface
/** The app depends on this, not on any provider SDK. */
interface AiChatService {
fun stream(conversationId: String, userMessage: String): Flow<ChatEvent>
}
sealed interface ChatEvent {
data class Delta(val text: String) : ChatEvent
data class Done(val messageId: String) : ChatEvent
data class Error(val kind: ErrorKind) : ChatEvent
}
enum class ErrorKind { RATE_LIMITED, QUOTA_EXCEEDED, UNAVAILABLE, BLOCKED, NETWORK }Streaming from your backend (SSE)
If your backend relays tokens as Server-Sent Events, OkHttp's SSE module consumes them as a Flow:
class BackendChatService(
private val client: OkHttpClient, // with an auth interceptor adding the user's token
private val baseUrl: String,
private val json: Json,
) : AiChatService {
override fun stream(conversationId: String, userMessage: String): Flow<ChatEvent> = callbackFlow {
val body = json.encodeToString(ChatRequest(conversationId, userMessage))
.toRequestBody("application/json".toMediaType())
val request = Request.Builder().url("$baseUrl/v1/chat/stream").post(body).build()
val source = EventSources.createFactory(client).newEventSource(request, object : EventSourceListener() {
override fun onEvent(es: EventSource, id: String?, type: String?, data: String) {
when (type) {
"delta" -> trySend(ChatEvent.Delta(json.decodeFromString<Delta>(data).text))
"done" -> { trySend(ChatEvent.Done(data)); close() }
}
}
override fun onFailure(es: EventSource, t: Throwable?, response: Response?) {
trySend(ChatEvent.Error(response?.code.toErrorKind()))
close()
}
})
awaitClose { source.cancel() } // cancels the HTTP call when the collector stops
}
}
private fun Int?.toErrorKind() = when (this) {
429 -> ErrorKind.RATE_LIMITED
402, 403 -> ErrorKind.QUOTA_EXCEEDED
null -> ErrorKind.NETWORK
else -> ErrorKind.UNAVAILABLE
}Collect this in a ViewModel and render it as in Streaming AI Responses in Compose.
Cost and rate limits
- Cap max output tokens and conversation history length on the server.
- Choose smaller models for simple tasks, and route by task.
- Return clear 429 or quota errors, and show helpful UI (wait, or upgrade).
- Track cost per user and feature. See Monitoring AI Costs.
OpenAI via proxy vs Firebase AI Logic
| OpenAI via your backend | Gemini via Firebase AI Logic | |
|---|---|---|
| Client security | Your auth + backend | App Check + Firebase |
| Backend needed | Yes | No (optional) |
| Prompt control | Fully server-side | Client (or server prompt templates) |
| Provider flexibility | High | Gemini models |
Frequently asked questions
Is there an official OpenAI Android SDK?
OpenAI publishes official SDKs for several server languages, including Java/Kotlin for the JVM, but they're intended for server use with secret keys. On Android, call your own backend.
Can I use ephemeral keys?
Some provider features (such as realtime sessions) support short-lived client tokens issued by your server. Your backend still mints them after authenticating the user.
Conclusion
Integrate OpenAI through a backend proxy that holds the key, authenticates users, owns prompts, enforces quotas and streams responses. On Android, depend on a provider-agnostic interface and stream with SSE.
Next step: render those streams beautifully with Streaming AI Responses in Jetpack Compose.
Official references
๐ฑ Apps built by Yunok Tech
Real Android apps on Google Play, free to download.
Walzi4K wallpapers for lock and home screenGet it โธ
NotiVaultNotification history: recover dismissed notificationsGet it โธ
Ready to build real Android applications?
Every AI feature still runs inside an Android app that has to be fast, reliable and well structured. Strengthen the Kotlin, Jetpack Compose and architecture skills that AI integrations depend on, then apply these AI concepts with confidence.
Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.
Explore the Android course and take your next step as an Android developer.