๐Ÿง  Android AI Projects and Production

OpenAI API Integration Patterns for Android

Calling OpenAI directly from an Android app means shipping your secret API key to every user, and anyone can extract it. The production pattern is a backend proxy you control. Here's the architecture, the Android side, and how to stay provider-agnostic.

โœ๏ธ Yunok Tech ๐Ÿ“… Updated โฑ 3 min read ๐ŸŽฏ Intermediate
On this page
  1. The proxy architecture
  2. What the backend does
  3. The Android side: a provider-agnostic interface
  4. Streaming from your backend (SSE)
  5. Cost and rate limits
  6. OpenAI via proxy vs Firebase AI Logic
  7. Frequently asked questions
  8. Conclusion

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.

Never put the API key in the app

Not in code, BuildConfig, resources, native libraries or obfuscated strings. APKs can be decompiled and traffic inspected. See Securing API Keys.

The proxy architecture

Android app to backend proxy to OpenAI The Android app sends a request with the user's auth token and an integrity token to your backend. The backend verifies the user, enforces quotas, builds the prompt with server-side instructions, and calls OpenAI with the secret key. It streams the response back to the app and logs usage and cost. Android appuser token+ integrity token Your backendauth ยท quotas ยท promptmoderation ยท loggingsecret key here only OpenAI API(or other provider) The app never sees the provider key; the backend can switch providers freely.
The backend is your control point for security, cost and provider choice.

What the backend does

  1. Authenticate the user (for example a Firebase Auth ID token) and optionally verify a Play Integrity token.
  2. Enforce quotas and rate limits per user and plan.
  3. Own the prompt: system instructions and model choice live on the server, so the client sends only user input.
  4. Validate and moderate input and output as your use case requires.
  5. Stream the provider response back to the client.
  6. Log usage (tokens, latency, errors) without storing sensitive content unless needed. See AI Data Retention.

The Android side: a provider-agnostic interface

AiChatService.ktKotlin
/** 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:

BackendChatService.ktKotlin
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 backendGemini via Firebase AI Logic
Client securityYour auth + backendApp Check + Firebase
Backend neededYesNo (optional)
Prompt controlFully server-sideClient (or server prompt templates)
Provider flexibilityHighGemini 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.

See all our apps โ†’

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.

Share: LinkedIn X / Twitter WhatsApp