The Android In-App Review API: asking for a rating without being annoying
A practical guide to Google's In-App Review API — how it actually works, where to trigger it, and why the usual 'rate us' popup is quietly hurting your Play Store rating.
Most “rate us” prompts on Android are a custom dialog with two buttons: one that opens the Play Store listing, one that dismisses. They show up on app launch, or after some arbitrary number of sessions, and they interrupt whatever the user was actually trying to do. The person who taps “rate” leaves your app, lands in the Play Store, and now has to write a review from scratch — friction that kills most of the intent you just captured. The person who taps “not now” has told you nothing, and you’ll probably ask again next week anyway.
Google’s In-App Review API fixes the mechanical half of this problem: a native rating sheet that appears over your app, lets the user pick stars (and optionally write a few words) without leaving, and returns them exactly where they were. It doesn’t fix the UX half — when you trigger it still matters more than how — but it removes the excuse for getting the “how” wrong.
How it actually works
The API is part of Play Core (com.google.android.play:review-ktx). You request a ReviewInfo object ahead of time, then launch the flow when you’re ready to show it:
val manager = ReviewManagerFactory.create(context)
manager.requestReviewFlow().addOnCompleteListener { request ->
if (request.isSuccessful) {
val reviewInfo = request.result
manager.launchReviewFlow(activity, reviewInfo)
.addOnCompleteListener {
// The flow is finished — you don't get a signal on
// whether the user actually rated. Treat this as "done,"
// not "succeeded."
}
}
}
Two details catch people out. First, requestReviewFlow() can fail silently or return without ever showing a dialog — Google throttles how often the same user sees the sheet (roughly once a year per app, undocumented precisely and out of your control), so most calls in testing will complete without displaying anything. Second, the completion callback fires whether the user rated, skipped, or the quota blocked the dialog entirely. There is no onRatingSubmitted — by design, so apps can’t gate features or nag based on the result.
The part that actually matters: timing
The API being frictionless doesn’t help if you trigger it at a bad moment. The failure mode I see most often is calling requestReviewFlow() right after onCreate(), on the theory that more impressions means more reviews. It means the opposite — you’re asking before the user has any opinion, and you’re doing it while they’re mid-task on whatever brought them into the app that day.
The moment that works is right after the app has demonstrably done its job. Building Mintly, that’s the beat right after a focus session completes and the user sees their streak tick up — not the first session, but one where the app has already earned a little trust. A few concrete rules that hold across app categories:
- Trigger on a completed positive outcome, not a fixed session count. A budgeting app after the user reconciles a month cleanly, a habit tracker after a streak milestone, a reminder app after a genuinely useful save — not “session #5.”
- Never trigger on an error path. If something just failed or the user backed out of a flow, that’s the worst possible time to ask.
- Rate-limit yourself even though the API rate-limits you. Google’s quota means most requests are silently no-ops, but your own trigger logic should still avoid firing on every qualifying event — once the flow has completed for a user, don’t call it again for months, even if your own tracking can’t confirm whether it displayed.
- Never combine it with your own pre-prompt. A custom “enjoying the app?” dialog that gates the real one just reintroduces the two-tap friction the API exists to remove, and it lets you filter who sees the real prompt — which is exactly the gaming Google’s review guidelines prohibit.
What it can’t do, and what people try anyway
You cannot detect whether the user actually left a rating, read the star value, or redirect unhappy users away from the flow and happy users toward it. That last one is a common ask and it’s explicitly against Play’s developer policy — the whole point of the API is that everyone who triggers it gets the same unfiltered sheet. If you want a “having a rough time?” support channel, build it as a separate, honest feedback link — not a fork in front of the review prompt.
The other limitation worth knowing: it’s Play-only. There’s no equivalent guarantee on other Android app stores, and on those you’re back to a manual “rate us” link or the store’s own mechanism.
Testing it without burning your quota
Because real quota is opaque, test through the Play Core testing tooling rather than your production build — the com.google.android.play:review-ktx testing artifact and internal app-sharing tracks let you trigger the flow repeatedly without waiting out Google’s real-world throttle. Wire the trigger into a debug menu during development so you can fire it on demand, then remove that shortcut before release. In production, treat every call as fire-and-forget: request, launch, move on, and let Google’s own logic decide who actually sees the sheet.
// Related reading
More from the journal
Accessibility on Android in 2026: a TalkBack and Compose semantics checklist that ships
A practical Android accessibility checklist for 2026 — TalkBack, Compose semantics, touch targets, and the testing pass I run on every screen before release.
Android app shortcuts and Quick Settings Tiles: logging water without opening the app
A practical guide to Android's dynamic ShortcutManager API and TileService — how to let a one-tap action skip the app entirely, with the pitfalls that trip most implementations.
Foreground service types on Android in 2026: picking the one your feature actually qualifies for
A practical guide to Android's foreground service type restrictions — dataSync, mediaPlayback, specialUse, shortService — and how to pick the right one without getting killed or rejected.