SafetyNet reCAPTCHA Is Shut Down: How to Add reCAPTCHA to an Android App in 2026

Read this first: the SafetyNet reCAPTCHA API no longer works. Google began gradually turning it down in Q3 2025, and SafetyNet.getClient(this).verifyWithRecaptcha(SITE_KEY) is not something to build against in 2026. Every tutorial still showing that call — including the earlier version of this page — is describing a dead API.
The replacement is the standalone reCAPTCHA Android SDK (com.google.android.recaptcha:recaptcha), backed by reCAPTCHA Enterprise. It is a genuinely different model: instead of showing a checkbox or an image challenge, it scores the user silently in the background and hands you a token to verify server-side.
In a hurry? Jump to: what changed · get a site key · add the SDK · initialise the client · execute an action · verify on your server · migrating from SafetyNet · mistakes to avoid
What actually changed
| SafetyNet reCAPTCHA (dead) | reCAPTCHA Android SDK | |
|---|---|---|
| Status | Turndown began Q3 2025 | Current |
| Dependency | play-services-safetynet | com.google.android.recaptcha:recaptcha |
| User experience | Checkbox, sometimes an image challenge | Silent scoring, no challenge |
| Entry point | SafetyNet.getClient() | Recaptcha.fetchClient() |
| Result | Pass / fail token | Risk score, 0.0 to 1.0 |
| Keys | reCAPTCHA v2 site key | reCAPTCHA Enterprise key, per platform |
| Billing | Hard quota, no overage | Quota with optional billing beyond it |
The score is the significant difference. SafetyNet gave you a binary answer; the new SDK returns a number where 1.0 is almost certainly a human and 0.0 is almost certainly automated. You choose the threshold and what happens below it — Google no longer decides for you, and visual challenges are no longer recommended in mobile apps at all.
Step 1: Create a reCAPTCHA key
- Open the Google Cloud console and select or create a project.
- Enable the reCAPTCHA Enterprise API for that project.
- Under reCAPTCHA, create a key and choose the Android platform type.
- Add your app's package name to the key's allowed list. A key restricted to the wrong package fails at runtime with no useful error.
- Copy the key ID. This is the site key your app passes to the SDK.
An Android key will not work in a web page and vice versa — platform types are not interchangeable, which trips up anyone reusing an existing v2 key from a website.
Step 2: Add the SDK
In your module build.gradle:
dependencies {
implementation 'com.google.android.recaptcha:recaptcha:18.8.0'
}
And remove the old one if it is still there:
// Delete — the SafetyNet reCAPTCHA API is shut down:
// implementation 'com.google.android.gms:play-services-safetynet:18.0.1'
The SDK needs the internet permission, which most apps already declare:
<uses-permission android:name="android.permission.INTERNET" />
Step 3: Initialise the client once
Recaptcha.fetchClient() is a suspend function and does real network setup, so fetch the client early — application start or screen entry — and keep it. Do not call it on every button tap.
class MainViewModel(app: Application) : AndroidViewModel(app) {
private var recaptchaClient: RecaptchaClient? = null
init {
viewModelScope.launch {
recaptchaClient = Recaptcha
.fetchClient(getApplication(), "YOUR_KEY_ID")
.getOrNull() // returns Result<RecaptchaClient>
}
}
}
One client per site key: the SDK supports a single key, and passing a different one throws.
Step 4: Execute an action
Call execute() at the moment you want to protect — the login tap, the signup submit — not on screen load. The action name is what shows up in your reCAPTCHA analytics, so use the built-ins where they fit:
suspend fun login(email: String, password: String) {
val token = recaptchaClient
?.execute(RecaptchaAction.LOGIN, timeout = 10_000L)
?.getOrNull()
if (token == null) {
// Network failure or timeout. Decide your own policy:
// fail open (allow) or fail closed (block). Do not crash.
return
}
// Send the token with the request. Never trust it on the client.
api.login(email, password, recaptchaToken = token)
}
Google suggests a 10-second timeout, with 5 seconds as the practical minimum. Built-in actions include LOGIN and SIGNUP; for anything else use RecaptchaAction.custom("checkout").
Step 5: Verify the token on your server
This step is the entire point, and it is the one most often skipped. A token generated on the client means nothing until your backend asks Google to assess it. Client-side code can be modified; the assessment cannot.
Your server calls the reCAPTCHA Enterprise projects.assessments.create endpoint with the token, the site key and the expected action, then reads the response:
// Server-side pseudocode — never run this from the app.
val assessment = recaptchaEnterprise.createAssessment(
projectId = "your-project",
siteKey = "YOUR_KEY_ID",
token = tokenFromApp,
expectedAction = "LOGIN"
)
if (!assessment.tokenProperties.valid) {
reject("invalid token: " + assessment.tokenProperties.invalidReason)
}
if (assessment.tokenProperties.action != "LOGIN") {
reject("action mismatch — possible token replay")
}
when {
assessment.riskAnalysis.score >= 0.7 -> allow()
assessment.riskAnalysis.score >= 0.3 -> requireSecondFactor()
else -> block()
}
Checking that the returned action matches the one you requested is what stops an attacker harvesting a token from a low-value screen and replaying it against your login endpoint. Tokens are also short-lived — verify immediately rather than queueing them.
Migrating from SafetyNet reCAPTCHA
| SafetyNet | reCAPTCHA SDK |
|---|---|
SafetyNet.getClient(this) | Recaptcha.fetchClient(application, keyId) |
.verifyWithRecaptcha(SITE_KEY) | client.execute(RecaptchaAction.LOGIN) |
.addOnSuccessListener { it.tokenResult } | Result<String> from a suspend call |
siteverify endpoint | projects.assessments.create |
success: true/false | riskAnalysis.score (0.0–1.0) |
Budget for the server change as the real work. The app-side swap is an afternoon; moving from a boolean success to a score means deciding your thresholds and what a middling score should do — usually step-up verification rather than an outright block.
Mistakes to avoid
- Trusting the token in the app. A token that never reaches your server protects nothing. The score only exists in the assessment response.
- Reusing a web site key. reCAPTCHA keys are platform-specific. An Android key is created as an Android key.
- Calling
fetchClient()per request. It is expensive setup. Fetch once, hold the client, reuse it. - Not checking the returned action. Without that check, tokens from anywhere in your app can be replayed against your most sensitive endpoint.
- Hard-blocking on a low score. Scores are probabilistic. A 0.3 is a reason to ask for a second factor, not to lock a real customer out.
- Assuming a visual challenge appears. SDK 16 and higher have no image challenges. If your UX copy says "complete the CAPTCHA", rewrite it.
Summary
SafetyNet reCAPTCHA is gone and cannot be brought back. Replace play-services-safetynet with com.google.android.recaptcha:recaptcha, fetch a client once with your Android reCAPTCHA Enterprise key, call execute() at the action you care about, and — the part that actually provides the protection — verify the token and its action server-side before you act on the score.
Frequently Asked Questions
Is the SafetyNet reCAPTCHA API still available?
No. Google began gradually turning down the SafetyNet reCAPTCHA API in the third quarter of 2025. New integrations should use the standalone reCAPTCHA Android SDK backed by reCAPTCHA Enterprise instead.
What replaces SafetyNet reCAPTCHA on Android?
The reCAPTCHA Android SDK, added with implementation 'com.google.android.recaptcha:recaptcha'. You fetch a RecaptchaClient with Recaptcha.fetchClient(application, keyId), then call execute() with a RecaptchaAction to get a token.
Does the new reCAPTCHA SDK show a checkbox or image challenge?
No. reCAPTCHA SDK version 16 and higher have no visual challenges. The SDK scores the user silently in the background and returns a token, and Google no longer recommends visual challenges in mobile apps.
What reCAPTCHA score should I treat as a bot?
The score runs from 0.0 to 1.0, where higher is more likely human, and you choose the thresholds. A common pattern is to allow above 0.7, require a second factor between 0.3 and 0.7, and block below 0.3 — tuned against your own traffic rather than used as a default.
Can I verify the reCAPTCHA token inside the Android app?
No, and doing so provides no protection. The token must be sent to your server, which calls the reCAPTCHA Enterprise projects.assessments.create endpoint. Only that response contains the risk score, and only your server can be trusted to act on it.
Can I reuse my website's reCAPTCHA site key in an Android app?
No. reCAPTCHA keys are tied to a platform type, so you need a key created as an Android key with your app's package name added to it. A key restricted to the wrong package fails at runtime.
Why does Recaptcha.fetchClient() fail or time out?
The usual causes are a key created for the wrong platform, a package name not listed on the key, the reCAPTCHA Enterprise API not enabled on the Cloud project, or a missing INTERNET permission. Because fetchClient() does real network setup, call it once at startup rather than on each user action.




