Age verification in Android apps has historically been a mess. You either slap up a "are you 18?" checkbox that no one takes seriously, pay for a third-party identity verification service that adds friction and a new privacy surface, or — if you're thoughtful about it — build something custom that's still just a speed bump rather than a real signal. None of those options are good. Google announced on July 29, 2026 that the Play Age Signals API is expanding globally, and it's the first time the platform gives you a verified, privacy-preserving age signal you can actually build product decisions on.
The API is still in beta, but it's been live in Brazil since March 2026 and for Texas users since May. From what I can tell, the design is solid. Here's the full picture.
What the API Actually Provides
The core promise is simple: your app asks the Play runtime "what age range is this user?", and Play gives you back a verified answer — one it sourced either from a parent's Family Link settings or from the user's own age verification through the Google Play Store. You don't verify identity. You don't manage any personal data. You just call the API and act on the result.
The default age tiers are 0–12, 13–15, 16–17, and 18+. These map cleanly onto the major regulatory thresholds most laws care about: COPPA in the US at 13, GDPR-K in Europe at 16, standard adult thresholds at 18. Google also lets you configure custom age ranges if your specific use case demands finer resolution — but the defaults cover the vast majority of compliance needs.
What you receive isn't a birth date or a precise age. It's an open or closed range: ageLower() gives you the floor, ageUpper() gives you the ceiling (null for open-ended 18+ ranges). That's intentional — the API is designed to give you exactly enough to make a feature decision, and nothing more.
The Family Link connection
For users under parental supervision, parents share their child's age range directly via Family Link. They choose to share it; it's not auto-extracted from account data. When a kid opens your app on a Family Link-managed device, the age range the parent approved flows through the Play runtime into your call. Adults verify their own age through Play's existing identity infrastructure. The data stays on-device; the Play runtime is the broker, not your backend.
For Musist — where age-appropriate ad targeting is a real compliance question — this is the first signal I'd actually trust enough to branch product behavior on. Previously, age metadata from Play account data was both unreliable and off-limits for use as a gate. This is different: it's explicit, consent-based, and verified.
Integration: It's Three Steps
Add the dependency:
// build.gradle.kts (app)
dependencies {
implementation("com.google.android.play:age-signals:0.0.4")
}
Minimum requirement is Android 6.0 (API 23), which covers essentially your entire active user base at this point.
Step 1: Create the manager
val ageSignalsManager = AgeSignalsManagerFactory.create(applicationContext)
Step 2: Request access and check sharing status
Before calling checkAgeSignals(), you need to verify the user has agreed to share their age. This is where you handle the opt-out gracefully:
val accessRequest = AgeSignalsAccessRequest.builder()
.setActivity(this)
.build()
ageSignalsManager.requestAgeSignalsAccess(accessRequest)
.addOnSuccessListener { accessResult ->
when (accessResult.ageSignalsStatus()) {
AgeSignalsStatus.SHARED -> retrieveAgeSignals(ageSignalsManager)
AgeSignalsStatus.NOT_SHARED -> handleNoSignal()
AgeSignalsStatus.VERIFICATION_REQUIRED -> promptUserToVerify()
}
}
.addOnFailureListener { e -> handleError(e) }
VERIFICATION_REQUIRED appears in markets where age verification is mandatory by law — currently the regulated US states like Texas. When you get this status, you can deep-link the user to Play Store's built-in verification flow rather than building your own gate.
Step 3: Retrieve the age range
private fun retrieveAgeSignals(manager: AgeSignalsManager) {
manager.checkAgeSignals(AgeSignalsRequest.builder().build())
.addOnSuccessListener { result ->
val lower = result.ageLower()
val upper = result.ageUpper()
when {
lower == null -> handleUnknownAge()
lower < 13 -> applyChildExperience()
lower < 16 -> applyTeenExperience()
lower < 18 -> applyOlderTeenExperience()
else -> applyAdultExperience()
}
}
.addOnFailureListener { e -> handleError(e) }
}
The Task-based API is the same pattern as Play Integrity or Play Feature Delivery — no new mental model. If you prefer coroutines, a simple adapter works fine:
suspend fun AgeSignalsManager.checkAgeSignalsAsync(): AgeSignalsResult =
suspendCoroutine { cont ->
checkAgeSignals(AgeSignalsRequest.builder().build())
.addOnSuccessListener { cont.resume(it) }
.addOnFailureListener { cont.resumeWithException(it) }
}
Where to call this: First launch after onboarding, not at every screen entry. Cache the result for the session — in DataStore or in-memory — and refresh it at app start. Age signals don't change mid-session, and the Task overhead isn't free.
Testing with FakeAgeSignalsManager
The SDK ships a FakeAgeSignalsManager in a separate testing artifact that lets you simulate every scenario in unit and integration tests without a live Play Store runtime:
// build.gradle.kts
testImplementation("com.google.android.play:age-signals-testing:0.0.4")
// In your test
val fakeManager = FakeAgeSignalsManager()
fakeManager.setAgeSignals(
ageLower = 13,
ageUpper = 15,
status = AgeSignalsStatus.SHARED
)
// Inject fakeManager via DI — your ViewModel/repository doesn't change
This is exactly the testing surface I wanted when building OnlyArabs, where Agora stream access rules differ by user tier. Being able to simulate a 17-year-old or a 12-year-old in CI without needing a real device registered under a Family Link account is the difference between actually testing your age gate and just hoping it works in production.
The Policy Restrictions That Matter More Than the API
This is the part people skip, and it's the part that will get you in trouble. The Age Signals API comes with hard restrictions on data use, and they're enforced at the API access level:
- Age-appropriate experiences only. Adapt content, features, and UI. That's it.
- No advertising use. You cannot use the age signal to serve different ads, build targeting segments, or feed it into any ad SDK or analytics pipeline.
- No profiling or persistence. You cannot store the age range, send it to your backend, or use it to build a user profile beyond what's needed to render the current session's experience.
- App-scoped. The signal is for the requesting app only. You cannot share it across apps you own or pass it to third-party SDKs.
Violation consequence: Google terminates API access and can suspend or remove your app. These restrictions aren't advisory — they're the enforcement mechanism that makes the privacy promise meaningful to parents trusting Family Link with their children's data.
The clean mental model: use the age signal exactly the way you'd use a parental control flag from an MDM profile. It tells you who you're serving so you can serve them appropriately. It doesn't tell you anything you're allowed to monetize or persist.
The Rollout Timeline to Track
Here's where things stand as of the July 29 announcement:
- Brazil: Live since March 17, 2026 (Digital ECA compliance)
- Texas: Live for accounts created after May 28, 2026 (SB 2420)
- Utah: Live since May 7, 2026
- Louisiana: Live since July 1, 2026
- Australia and Canada: Mid-August 2026
- Global all markets: By end of 2026
If your app reaches Brazil, Texas, Utah, or Louisiana today, the API is already relevant to a real portion of your user base. If you ship a social, messaging, media, or user-generated content app — the legal pressure behind this rollout isn't going away. States and countries continue to pass similar legislation, and the direction of travel is clear.
The practical point: you don't need to wait for full global rollout to integrate. The API already returns useful data for millions of users, and the NOT_SHARED path handles users in markets where it's not yet active. Integrate now, test with FakeAgeSignalsManager, handle the NOT_SHARED case gracefully, and you're ready when each new region goes live — without a scramble.
How I'd Actually Wire This Into Production
In the Musist and HailUp pattern — apps with mixed content and ad integrations — I'd structure this as a session-scoped singleton injected via Hilt. The age tier becomes part of the session context that every content loader and playback controller reads from, rather than being checked ad-hoc at each screen boundary:
@Singleton
class AgeSignalsSession @Inject constructor(
@ApplicationContext private val context: Context
) {
private val _tier = MutableStateFlow<AgeTier>(AgeTier.Unknown)
val tier: StateFlow<AgeTier> = _tier.asStateFlow()
suspend fun initialize() {
val manager = AgeSignalsManagerFactory.create(context)
runCatching {
val result = manager.checkAgeSignalsAsync()
_tier.value = when {
result.ageLower() == null -> AgeTier.Unknown
result.ageLower()!! < 13 -> AgeTier.Child
result.ageLower()!! < 18 -> AgeTier.Teen
else -> AgeTier.Adult
}
}
}
}
enum class AgeTier { Unknown, Child, Teen, Adult }
Collecting tier in your ViewModels gives you reactive age-awareness without any screen knowing about the raw API call. Call initialize() in your Application subclass or in a startup Initializer. If the signal is unavailable or the user hasn't shared it, AgeTier.Unknown is the safe default — show the most conservative version of the experience, and if your legal team says a hard gate is required for Unknown in regulated markets, you add it in one place.
The wrapper also matters for another reason: the SDK is at version 0.0.4 and still in beta. APIs at this stage have changed between releases before. Keeping the boundary in one class means any future signature changes have a blast radius of one file rather than every screen that cares about age.
Should You Integrate Now?
For most apps — yes. If you're already in a regulated market, integrating is close to a requirement depending on your category. If you're building globally for a content or social audience, the signals expand to cover your users progressively through end of year, and the sooner you build the less fire-drill you'll have when your region goes live.
Beyond compliance, there's an actual product angle here. The apps I've shipped all had the same frustration: you want to serve a genuinely appropriate experience to different age groups — different UI complexity, different content warnings, different onboarding flows — but you have no reliable, trustworthy signal to branch on. The Play Age Signals API is the first time the platform gives you that signal with real verifiability behind it. Using it well isn't compliance theater. It's actually good product design, and the design happens to satisfy a regulation too.
No comments yet. Be the first to leave one!