Skip to content
All posts
July 25, 20264 min read

Handling API Keys Safely in Android Apps

Any API key you ship in an APK can be extracted — full stop. Here's how I actually handle keys in Android: which ones can live in the app, which must go behind a backend, and how to keep them out of git either way.

SecurityAPI KeysAndroidSecretsBackend
Share:

The uncomfortable truth about API keys on mobile is that anything shipped in your APK can be extracted. Decompile the app, dump the strings, read the network config — the key is there for anyone who looks. So "how do I hide my API key in the app" is the wrong question. The right questions are which keys can tolerate being public, how to restrict the ones that must ship, and how to keep all of them out of version control.

First, Keep Keys Out of Git

Before worrying about the APK, worry about your repository. A key committed to git lives in the history forever, even after you delete it, and public repos get scraped by bots within minutes. Keys belong in a gitignored

code
local.properties
or a secrets file, read at build time, never hardcoded in a tracked file.

kotlin
// build.gradle.kts reads from local.properties (gitignored)
val mapsKey: String = localProps.getProperty("MAPS_API_KEY") ?: ""
kotlin
buildConfigField("String", "MAPS_API_KEY", "\"$mapsKey\"")

I also run a credential scan before commits so a stray key can't slip through. Getting a leaked key out of git history is painful; keeping it out in the first place is trivial.

Classify the Key by What It Can Do

Not all keys carry the same risk, and the handling depends on the blast radius. A key that's restricted to your app and can only do something cheap and bounded — like a Maps key locked to your package and signing certificate — can tolerate shipping, because even extracted, it's useless to anyone else and can't run up arbitrary cost. A key that grants broad, billable, or sensitive access cannot ship under any circumstances, because once extracted it's a blank check. The classification drives everything: restrictable-and-bounded can live in the app; powerful-and-unbounded must not.

Restrict the Keys That Must Ship

For the keys that genuinely have to be in the app, restriction is the real protection, not hiding. In the provider's console, lock the key to your app's package name and signing certificate fingerprint, and scope it to only the specific APIs it needs. A restricted key, even when extracted, can't be used by another app or for anything outside its scope. This turns "the key is public" from a disaster into a non-event, because the key only works in the context you allowed.

Put Powerful Keys Behind a Backend

For anything that can't be safely restricted — a key to a paid service, an LLM API, anything that bills per call or accesses sensitive data — the key lives on a server you control, and the app calls your server instead of the third party directly. Your backend holds the secret and the app never sees it. This is the only real protection for high-value keys, and it has a bonus: you can add rate limiting and abuse controls on your side, so a misbehaving or malicious client can't drain your account. The app authenticates to your backend; your backend authenticates to the third party.

Rotate and Monitor

Finally, assume keys eventually leak and plan for it. I keep keys rotatable so a compromised one can be replaced without re-architecting, and I watch usage for the spike that signals abuse. A key restricted, scoped, and monitored is a manageable risk; an unrestricted powerful key sitting in a shipped APK is an incident waiting to happen. The whole discipline comes down to never trusting the client with a secret it can't afford to leak, and treating every key as if it will someday be public — because if it ships, it will be.

If you take one habit from this, make it the reflex of asking, for every key you touch, "what happens when this is public?" That single question routes you to the right handling automatically: if the answer is "nothing bad, it's restricted and bounded," the key can ship; if the answer is "someone drains my account or reads user data," it belongs behind your backend. It reframes key handling from the impossible goal of hiding secrets in a client to the achievable goal of ensuring no shipped secret can do real harm. Combined with keeping keys out of git and rotating them when they leak, that reflex is most of what mobile key security actually is in practice.

Key Takeaways

  • Anything in the APK can be extracted; "hiding" a key in the app is not a real strategy.
  • Keep all keys out of git via gitignored properties and a pre-commit credential scan — history leaks are forever.
  • Classify keys by blast radius: restrictable-and-bounded can ship; powerful-and-unbounded cannot.
  • For keys that must ship, restrict them to your package and signing cert and scope them to needed APIs.
  • Put high-value keys behind a backend you control, so the app never sees them and you can rate-limit abuse.
  • Keep keys rotatable and monitor usage; assume every shipped key will eventually be public.
Share:
S

Sudarshan Chaudhari

AI Systems Builder / Product Engineer

Bangkok, Thailand

Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.

Stay updated

Get new posts on Android, Kotlin, and solo dev straight to your inbox.

Newsletter preferences

Related Apps

MyFamilyTracker

Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.

Building something? Available for Android dev and QA consulting.

Work with me

Comments — powered by Giscus