# AI API keys in frontend and mobile apps: what to do instead

URL: https://infro.io/blog/ai-api-keys-frontend-mobile-apps
Author: INFRO Editorial (https://infro.io/blog/editorial)
Publisher: INFRO
Published: 2026-10-21
Last modified: 2026-10-21
Last reviewed: 2026-10-21 (next review due 2026-11-20)
Topic: Security and governance
Format: Guide

> An API key in a browser bundle or a mobile binary can be read by anyone who has the app, so treat it as public. Keep provider keys on a server: route requests through your own backend that authenticates each user and caps what they can spend; for realtime voice, mint short-lived client secrets on the server (OpenAI Realtime client secrets, Gemini Live ephemeral tokens); for Gemini in mobile apps, Firebase AI Logic keeps the key server-side and App Check attests the app. Then cap spend per end user.

Key takeaways:
- Any key that ships to a browser or a phone is public: it can be read from the bundle, the binary or the network traffic, and obfuscation only slows that down.
- The OpenAI and Anthropic TypeScript SDKs refuse to run in a browser unless you set dangerouslyAllowBrowser, because it exposes the key.
- Put provider keys behind your own backend that authenticates each user, fixes the model and limits, and caps spend per user.
- For realtime voice, mint short-lived credentials on the server: OpenAI Realtime client secrets expire in 10 seconds to 2 hours, Gemini Live ephemeral tokens in 30 minutes by default.
- For Gemini in mobile apps, Firebase AI Logic keeps the key on Google's side and App Check attests the calling app; App Check enforcement becomes required on 2026-11-02.

**Can you put an AI API key in a frontend or mobile app?** No. A key in a browser bundle or a mobile binary is public: anyone with the app can read it from the code, the binary or the network traffic, and use it on your provider account until you revoke it. Keep provider keys on a server. Have the app call your backend with the user's own session, mint short-lived credentials on the server where the client must talk to the provider directly (realtime voice), use attestation such as Firebase App Check where the platform offers it, and cap what each user can spend.

This guide explains why hiding techniques fail, then walks through the four patterns that work, with what OpenAI, Google and Firebase document for each.

## Why a shipped key is a public key

A browser or a phone has to send the key with each request, so the key exists in plaintext on the device at that moment. Environment variables in a React, Vue or Next.js client build are inlined into the bundle at build time; the network tab shows the `Authorization` header; an Android package can be unpacked and searched for strings. Obfuscation moves the key around but cannot remove it.

The SDK authors agree. The OpenAI and Anthropic TypeScript SDKs disable browser use by default "to avoid exposing your secret API credentials", and only run there if you set `dangerouslyAllowBrowser` to `true`[1][2]. OpenAI's README says enabling it "exposes your secret API credentials in the client-side code"[1]. Google's Gemini key page says never to expose keys client-side in production or hardcode them in web or mobile apps, because they can be extracted, and to call the API through a backend[3]. OWASP's Mobile Top 10 lists hardcoded credentials in app code or configuration files as "a clear indicator of vulnerability" under M1, Improper Credential Usage, and recommends revocable access tokens instead[4].

This is not a hypothetical. In April 2026 CloudSEK scanned the top 10,000 Android apps and found 32 live Google API keys across 22 apps with a combined install base above 500 million that worked against the Gemini API; for one language-learning app, the key listed files in Gemini's Files API that included users' speech recordings[5]. The keys had been embedded for Maps or Firebase, where Google's documentation once said that was fine, and gained Gemini access when the Generative Language API was enabled in the same project[5]. [Google API keys and Gemini exposure](https://infro.io/blog/google-api-keys-gemini-exposure) covers that case in depth.

A key on a device exposes three things: **spend** (anyone can run inference on your account), **data** (stored files, responses or fine-tuning data the key can read), and **your standing with the provider** (abuse on your key is abuse by your account; see [why providers suspend accounts](https://infro.io/blog/ai-provider-account-suspended-abuse)).

## Four patterns that keep the key off the device

| Pattern | Key on device? | Best for | What you still build |
| --- | --- | --- | --- |
| Your backend as the proxy | No | Chat, completions, images, anything request/response | User auth, per-user limits, fixed model and parameters |
| Short-lived client secrets | A credential that expires in minutes | Realtime voice and video over WebRTC or WebSockets | A server endpoint that authenticates the user, then mints |
| Platform proxy plus attestation | No provider key; an attestation token | Gemini in mobile and web apps via Firebase | App Check setup, per-user rate limits |
| Per-user caps | Not applicable | Every pattern above | A user id on every request and a cap per user |

### 1. Your backend is the proxy

The app authenticates the user with your normal session (cookie, OAuth token, Firebase Auth), calls your endpoint, and your server calls the provider with a key that never leaves the server. This is the default for anything that is not realtime.

A backend that forwards whatever the client sends is only a slower way of publishing the key. Decide on the server:

- **Which model and parameters.** Fix the model, `max_tokens`, tools and system prompt server-side; never accept a model name or a raw message list from the client without checking it.
- **Who may call.** Refuse unauthenticated requests; rate-limit by user, not only by IP.
- **How much each user may spend.** A per-user daily ceiling turns one abusive account into a bounded cost. [Stopping your AI feature becoming a free LLM proxy](https://infro.io/blog/ai-feature-abuse-free-llm-proxy) covers the abuse patterns.
- **What reaches logs.** Prompts from users are user data; apply your [prompt logging policy](https://infro.io/blog/llm-prompt-logging-policy).

### 2. Short-lived client secrets for realtime

Realtime voice wants the client to connect to the provider directly, because relaying audio through your server adds latency. Both OpenAI and Google document the same shape: the server authenticates the user, mints a short-lived credential with its own key, and hands only that to the client.

**OpenAI Realtime.** The browser asks your server for a token; your server calls `POST https://api.openai.com/v1/realtime/client_secrets` with its standard API key and returns the result; the browser uses it to open a WebRTC session[6]. The returned `value` looks like `ek_…` and carries an `expires_at`; `expires_after.seconds` accepts 10 to 7,200 seconds and defaults to 600[7]. OpenAI recommends setting the `OpenAI-Safety-Identifier` header on the server request that creates the secret, so the identifier is bound to the token and the browser does not need to send it[6]. An alternative keeps the key and token entirely server-side: the browser sends its SDP offer to your server, which posts it to `/v1/realtime/calls` with the standard key[6].

```bash
curl -X POST https://api.openai.com/v1/realtime/client_secrets \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Safety-Identifier: $HASHED_USER_ID" \
  -H "Content-Type: application/json" \
  -d '{"expires_after": {"anchor": "created_at", "seconds": 60}}'
```

**Gemini Live.** Ephemeral tokens are in preview, work only with the Live API on `v1beta`, and by default allow one minute to start a session and 30 minutes to use it; `uses: 1` limits a token to one session, and `liveConnectConstraints` locks the model and configuration so the client cannot change the system instructions[8]. Google is direct about the limit: ephemeral tokens "can be extracted from client-side applications", and their value is that they expire quickly and can be restricted[8].

A short-lived credential is only as good as the check before it is minted. If your mint endpoint hands a token to anyone who asks, an attacker calls the endpoint instead of stealing the key.

### 3. A platform proxy with app attestation

For Gemini in mobile and web apps, Firebase AI Logic puts Google's proxy between the app and the Gemini API so that, in Firebase's words, "your Gemini API key stays on the server and is not embedded in your apps' codebase"[9]. It applies per-user rate limits by default, and they are configurable[9].

The proxy alone would still serve any client that copies the app's configuration. App Check closes that: with enforcement on, the proxy accepts only requests "verified to be from your actual app and/or an untampered device"[10]. Attestation comes from Play Integrity on Android, DeviceCheck or App Attest on Apple platforms, and reCAPTCHA Enterprise on the web; limited-use tokens with a fixed five-minute lifetime can be used once each for replay protection[10]. Two dates matter: since early July 2026 the console enforces App Check automatically for new setups, and from 2026-11-02 App Check enforcement is required to use Firebase AI Logic[10].

Attestation raises the cost of abuse; it does not identify your users. Pair it with signed-in users and per-user limits.

### 4. Cap spend per end user

Every pattern above still lets a legitimate-looking user spend. The control that bounds that is a cap per end user, checked before the provider is called, so one account, one script or one compromised session cannot run up the whole month. Pass a stable, opaque user id with every request (never an email address), and refuse requests over the user's cap with a clear error your app can show.

> **Example: A voice feature with three limits**
> A language-learning app adds a voice tutor. The app signs users in with its existing auth. Its server mints an OpenAI Realtime client secret that lasts 60 seconds, only after checking the session and the user's daily minutes. Text corrections go through the same server, which fixes the model and `max_tokens` and sends the user's opaque id with each call so a per-user daily cap applies. A copied client secret is useless a minute later; a scripted account hits its daily cap; the provider key never leaves the server.

## A pre-release checklist

- [ ] Search the built bundle and the compiled app for provider key prefixes (`sk-`, `sk-proj-`, `sk-ant-`, `AIza`) before every release.
- [ ] No `dangerouslyAllowBrowser: true` in production code.
- [ ] Every Google API key embedded in an app (Maps, Firebase) is restricted, and the Generative Language API is not enabled in that project unless intended.
- [ ] The backend fixes model, parameters and tools; the client cannot choose them freely.
- [ ] Every AI endpoint requires a signed-in user and has a per-user rate limit and spend cap.
- [ ] Ephemeral credential endpoints authenticate before minting and use the shortest lifetime that works.
- [ ] A provider-side spend limit exists on the project or workspace the server key belongs to.
- [ ] You know how to revoke the server key in minutes; see [secure LLM API keys](https://infro.io/blog/secure-llm-api-keys).

## How INFRO handles client-facing AI features

INFRO sits behind your backend, not in the client. An INFRO gateway key (`sk_infro_`) is a secret like any provider key and belongs on your server, never in a bundle or an app. With your backend calling INFRO:

- **[End-customer caps](https://infro.io/docs/org/end-customer-caps)** limit spend per end user, from the Team plan. Your server sends your opaque user id in `metadata.user_id` (or `X-INFRO-User-Id`), a default rule caps every user, overrides change it for one, and a request over its cap is refused with `402` before your provider is called.
- **[Model allowlists](https://infro.io/docs/org/model-allowlists)** stop a request for a model the feature should not use, with `403 model_not_allowed`.
- **Gateway key controls** limit the key to the providers and environment the feature needs, and an IP allowlist set to your backend's egress addresses refuses a copied key used elsewhere with `403 ip_not_allowed`.
- **[Budgets](https://infro.io/docs/org/spend-controls)** on the key and project give the feature a hard ceiling, and **[guardrails](https://infro.io/docs/features/guardrails)** on the Business plan can redact emails, card numbers and API keys users paste into prompts.

One limit to state plainly: INFRO governs requests that go through INFRO. If a provider key is ever shipped to a device or stolen from anywhere, it can be used directly against the provider and none of these controls see it. Keep a spend limit and usage monitoring at the provider as well, and revoke there first.

## Summary

There is no safe way to ship a provider key in a frontend or mobile app. Keep keys on a server; authenticate users and fix model and limits there; for realtime, mint short-lived client secrets after checking the user; for Gemini on mobile, use Firebase AI Logic with App Check enforced; and cap spend per user so a single account cannot become your largest customer.

## Questions

### Can I hide an OpenAI API key in a React app with environment variables?

No. Build tools inline environment variables into the JavaScript bundle, and the browser sends the key on every request, so anyone can read it in developer tools. Call OpenAI from your own server, and have the browser call your server with the user's session.

### Is it safe to put an API key in an Android or iOS app if I obfuscate it?

No. Obfuscation slows extraction down, but the app must present the real key at request time, so it can be recovered from the binary or the network traffic. OWASP lists hardcoded credentials as a clear sign of the top mobile risk. Use a backend, or for Gemini, Firebase AI Logic with App Check.

### How does the browser connect to the OpenAI Realtime API without exposing my key?

Your server uses its standard API key to call POST /v1/realtime/client_secrets and returns the short-lived client secret to the browser, which uses it to open the WebRTC session. The secret expires after 10 minutes by default, configurable from 10 seconds to 2 hours.

### Are ephemeral tokens safe to expose?

Safer, not safe. Google notes that ephemeral tokens can be extracted from browsers and apps just like keys; they limit the damage because they expire quickly and can be locked to one model and configuration. Your server must still authenticate the user before minting one.

## Sources

1. openai/openai-node README — GitHub. https://github.com/openai/openai-node (accessed 2026-10-21)
2. anthropics/anthropic-sdk-typescript README — GitHub. https://github.com/anthropics/anthropic-sdk-typescript (accessed 2026-10-21)
3. Using Gemini API keys — Google AI for Developers. https://ai.google.dev/gemini-api/docs/api-key (accessed 2026-10-21)
4. M1: Improper Credential Usage — OWASP Mobile Top 10. https://owasp.github.io/www-project-mobile-top-10/2023-risks/m1-improper-credential-usage (accessed 2026-10-21)
5. Hardcoded Google API Keys in Top Android Apps Now Expose Gemini AI — CloudSEK. https://www.cloudsek.com/blog/hardcoded-google-api-keys-in-top-android-apps-now-expose-gemini-ai (accessed 2026-10-21)
6. Realtime API with WebRTC — OpenAI. https://developers.openai.com/api/docs/guides/realtime-webrtc (accessed 2026-10-21)
7. Create client secret (Realtime) — OpenAI API reference. https://developers.openai.com/api/reference/resources/realtime/subresources/client_secrets/methods/create (accessed 2026-10-21)
8. Ephemeral tokens — Google AI for Developers. https://ai.google.dev/gemini-api/docs/ephemeral-tokens (accessed 2026-10-21)
9. Gemini API using Firebase AI Logic — Firebase. https://firebase.google.com/docs/ai-logic (accessed 2026-10-21)
10. Implement Firebase App Check for Firebase AI Logic — Firebase. https://firebase.google.com/docs/ai-logic/app-check (accessed 2026-10-21)
