shipcheck

Play target API level, and what it blocks

Updated September 6, 2026 · published by Baker Ventures LLC · sources cited inline

This one does not get reviewed. It gets blocked. Google requires new apps and updates to target a recent Android API level by an annual deadline, and Play Console simply refuses the upload if you do not meet it. There is no Resolution Center conversation, no appeal, no reviewer to explain yourself to.

Check the current requirement in Play Console's target API level policy rather than in any article, including this one. It moves every year, and a stale number is worse than none.

The expensive part is not the one-line change. It is that raising the target level opts your app into that Android version's behaviour changes — background execution limits, storage access, permission semantics, foreground service type requirements. The build succeeds; the app behaves differently. Discovering that a week before a deadline is how teams lose a fortnight.

What actually breaks

Raising targetSdkVersion is a declaration that your app is ready for that platform version's rules. In recent Android versions the changes that most commonly bite React Native projects:

Background execution and foreground services. Restrictions on starting services from the background have tightened repeatedly, and foreground services must declare a type matching what they actually do. Any RN library doing background work — location tracking, audio, uploads — is exposed.

Storage. Scoped storage removed broad filesystem access. Libraries that assume they can write anywhere fail in ways that look like permission bugs.

Permissions. Runtime permission semantics have tightened repeatedly, notably around notifications, media access and background location.

Pending intents and exported components. Explicit mutability flags and explicit android:exported declarations became mandatory. These surface as build failures in libraries you did not write, which is a better failure than a silent one, but still a failure.

Package visibility. Querying which other apps are installed requires declaration. Anything doing app-to-app integration is affected.

The React Native and Expo shape of the problem

The setting lives in a generated file. In a bare project, android/build.gradle. In an Expo project using continuous native generation, android/ is regenerated on every prebuild, so editing it directly is overwritten.

Set it through the config plugin:

{
  "expo": {
    "plugins": [
      ["expo-build-properties", {
        "android": { "compileSdkVersion": 36, "targetSdkVersion": 36 }
      }]
    ]
  }
}

Or upgrade the Expo SDK, which moves the default — usually the better path, because it brings the rest of the toolchain along and libraries have been tested against it.

Your libraries have to follow. Raising your own target while a native module still assumes older behaviour produces runtime failures on new devices. Check that every native dependency has a release supporting the target level before you raise it — the ones that will hurt are the unmaintained ones, and finding that out in a deadline week is the worst possible time.

The check to run

# what is set, and what libraries request
grep -rn "targetSdkVersion\|compileSdkVersion" android/ app.json app.config.* 2>/dev/null

# permissions and service types in the merged manifest, which is what actually ships
find android -path '*intermediates*' -name 'AndroidManifest.xml' | head -1 | xargs grep -E 'uses-permission|foregroundServiceType'

The merged manifest is the one that matters. It contains what your libraries added, which is where surprises live.

Do this on a schedule, not at a deadline

The single most useful habit here is to raise the target level shortly after a new Android version ships, not shortly before the deadline. The work is identical and the conditions are not: early, you have months to find the library that has not caught up; late, you are choosing between shipping and replacing a dependency.

shipcheck reads your configured target level, the merged manifest, the permissions your libraries actually declare and the foreground service types they use, and flags the mismatches — locally, before you build, inside the Claude Code session you already have open. What it checks.

Do not take the specific level or date from this page. Google publishes both and updates them annually. This page explains what the requirement does and how it interacts with React Native and Expo; the number is in Play Console.

Questions and answers

What target API level does Google Play require?

Google sets an annual deadline requiring new apps and updates to target a recent Android API level, typically the one released the previous year. The current requirement and its date are published in Play Console's target API level policy - check there rather than relying on any secondary source, because it moves every year.

What happens if my app does not meet the target API level?

You cannot publish. It is not a review rejection you can argue with - Play Console blocks the upload. Existing installs continue to work, but you cannot ship an update until you raise the target.

How do I change the target SDK version in Expo?

Not by editing android/build.gradle, because continuous native generation regenerates it. Set it through the expo-build-properties config plugin in app.json or app.config.js, or upgrade the Expo SDK version, which moves the default.

Why does raising the target API level break things?

Because targeting a higher level opts your app into that version's behaviour changes - stricter background limits, scoped storage, tightened permission semantics, foreground service type requirements. The build succeeds and the behaviour differs, which is why raising it late is expensive.

Does this affect apps already installed?

No. Existing installs keep working. The requirement gates publishing new apps and updates.

Cite this pageshipcheck. “Play target API level, and what it blocks.” Baker Ventures LLC, September 6, 2026. https://shipcheck.bakerventuresstudio.com/rejections/play-target-api-level/