API Level Android is an integer identifier that uniquely corresponds to a specific release of the Android platform. Each OS version has its own unique number: Android 14 = API 34, Android 15 = API 35. The developer manages three parameters in build.gradle — minSdkVersion, targetSdkVersion and compileSdkVersion — to control compatibility and access to new features. According to Android Developers, choosing the right API Level is critical for security and audience coverage.
Key Takeaways
API Level Android is an integer identifier assigned to each public release of the Android Framework API. The first release Android 1.0 had API Level 1, Android 1.5 — API Level 3, Android 2.2 — API Level 8, Android 4.0 — API Level 14, Android 8.0 — API Level 26, Android 12 — API Level 31, Android 14 — API Level 34, Android 15 — API Level 35, Android 16 (2025) — API Level 36. Each new API Level may add new classes, methods, constants, permissions and change the behavior of existing ones.
API Level does not strictly increment by 1 with each release. For example, Android 4.4W (Wear) has API 20, while Android 5.0 — API 21. Gaps are related to internal iterations and Wear OS devices. It is important for the developer to know not the version name (KitKat, Lollipop, Tiramisu), but its API Level — it is what is used in code for compatibility checks.
The key purpose of API Level is backward compatibility. An app compiled against API 34 can run on devices with API 34 and below (if it does not use new APIs without checking). Android Runtime (ART) checks API calls at the system level and applies behavioural changes depending on the app's targetSdkVersion.
When installing an app, PackageManager checks that the device's API Level >= minSdkVersion from AndroidManifest.xml. If the condition is not met — installation is blocked with the message "App not installed". During execution, Android Runtime monitors API calls that require a higher API Level and generates NoSuchMethodError or UnsatisfiedLinkError if the method is absent in the current version.
| Component | Role in API Level handling |
|---|---|
| PackageManager | Checks minSdkVersion during installation |
| Android Runtime (ART) | Performs API compatibility checks at runtime |
| Google Play Store | Filters apps by device API Level |
| SDK Manager | Downloads platforms for compilation under the required API Level |
| lint | Static analyzer that warns about using APIs above minSdk |
In the build.gradle file (Module: app), the developer specifies three API Level parameters: minSdkVersion, targetSdkVersion and compileSdkVersion. Confusing them is one of the most common mistakes among beginner Android developers. Each parameter is responsible for a different aspect of compatibility, and their values must be consistent.
minSdkVersion is the minimum API Level at which the app can be installed and run. Devices with API Level below minSdk do not see the app in Google Play and cannot install it. The value is chosen based on the target audience: minSdk 21 (Android 5.0) covers 97% of devices, minSdk 26 (Android 8.0) — about 85%, minSdk 31 (Android 12) — about 55% (data from Android Studio Distribution Dashboard, 2026). The lower the minSdk, the greater the coverage, but the more backward compatibility code is needed.
targetSdkVersion is the API Level against which the app was tested. Android uses targetSdk to apply behavioural changes: if the app specifies targetSdk 33, the system enables all behavioral changes introduced in API 33. If targetSdk is 31, the system does not apply API 32-33 changes, preserving compatibility with old behavior. This is the most important parameter for security: Google Play requires targetSdk no older than 1 year from the current API Level.
compileSdkVersion is the Android SDK version against which the code is compiled. It determines which APIs are available at compile time. compileSdk must be >= targetSdk and, ideally, equal to the latest stable API Level. Increasing compileSdk does not affect runtime behavior — only the availability of new APIs for the compiler. After raising compileSdk, you need to check the code for deprecated APIs and new permission requirements.
// build.gradle.kts — API Level configuration example
plugins {
id("com.android.application") version "8.7.0"
id("org.jetbrains.kotlin.android") version "2.1.0"
}
android {
namespace = "com.example.myapp"
compileSdk = 36 // Android 16
defaultConfig {
applicationId = "com.example.myapp"
minSdk = 26 // Android 8.0
targetSdk = 36 // Android 16
versionCode = 1
versionName = "1.0.0"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.activity:activity-ktx:1.9.3")
}In the build.gradle.kts example, compileSdk = 36 (latest at the time of writing), targetSdk = 36, minSdk = 26 (Android 8.0). compileSdk 36 provides access to all Android 16 APIs. targetSdk 36 enables all Android 16 behavioural changes. minSdk 26 covers ~85% of devices. AndroidX Activity KTX and AppCompat provide backward compatibility for fragments and themes.
The minSdk and targetSdk parameters can also be specified in AndroidManifest.xml, but modern projects use build.gradle — values from Gradle override the manifest. In the manifest, it may be useful to specify
Behavioural changes are modifications to how the Android system works that are applied only to apps with targetSdk >= a certain API Level. Each new Android release introduces behavioural changes that can break existing apps if they are not updated. This is a key Android security mechanism: old apps continue to work as before, new ones follow current rules.
Android 10 (API 29) — Scoped Storage: apps with targetSdk 29+ do not have direct access to the shared file system, only via MediaStore, SAF or their own storage. Android 11 (API 30) — Package Visibility: package filter, apps only see installed packages they interact with. Android 12 (API 31) — Foreground Service Notification: all foreground services must show a notification within 10 seconds of starting. Android 13 (API 33) — POST_NOTIFICATIONS: runtime permission for push notifications. Android 14 (API 34) — Foreground Service Types: mandatory declaration of the foreground service type in the manifest.
// Handling Android 13 (API 33) behavioural changes: POST_NOTIFICATIONS
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
class NotificationHelper {
fun requestNotificationPermission(activity: MainActivity) {
// POST_NOTIFICATIONS permission only works with API 33+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return // Below API 33 no permission is required
}
when {
ContextCompat.checkSelfPermission(
activity,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED -> {
// Permission already granted, can send notifications
showNotification(activity)
}
activity.shouldShowRequestPermissionRationale(
Manifest.permission.POST_NOTIFICATIONS
) -> {
// Show explanation why permission is needed
activity.showRationale()
}
else -> {
// Request permission
activity.requestPermissionLauncher.launch(
Manifest.permission.POST_NOTIFICATIONS
)
}
}
}
private fun showNotification(context: Context) {
// Create and display notification
val notification = android.app.Notification.Builder(context, "default_channel")
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle("Notification")
.setContentText("New message")
.build()
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE)
as android.app.NotificationManager
manager.notify(1, notification)
}
}
// Register requestPermissionLauncher in Activity
class MainActivity : ComponentActivity() {
val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// Permission granted
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}Example of handling POST_NOTIFICATIONS in Kotlin: check Build.VERSION.SDK_INT >= TIRAMISU, request runtime permission via ActivityResultContracts.RequestPermission, handle the result in a callback. Without this permission, an app with targetSdk 33+ cannot show push notifications. Below API 33 the permission is not required — the check code prevents calling unavailable APIs.
Scoped Storage is one of the most significant behavioural changes. Starting from API 29 (targetSdk 29+), the app cannot get direct File access to Pictures, Downloads, Music and Documents directories. Instead, MediaStore is used for media, SAF (Storage Access Framework) for arbitrary files, and getExternalFilesDir() for its own storage. The exception is apps with the MANAGE_EXTERNAL_STORAGE permission, which requires Google Play approval.
Google Play sets mandatory targetSdkVersion requirements for publishing apps. Since August 2024, Google Play requires targetSdkVersion >= API 33 (Android 13). Each year the threshold rises: new apps and updates must specify targetSdk no older than 1 year from the current major API Level. Violating the requirement leads to publication blocking and removal of the app from the store.
The main reason is security. Each new Android API Level introduces behavioural changes that close attack vectors: Scoped Storage (API 29) prevents file theft, POST_NOTIFICATIONS (API 33) protects against spam notifications, Foreground Service Types (API 34) restricts hidden background services. Apps with a low targetSdk do not receive these protections and become a threat to users. Google Play cannot allow outdated apps on modern devices.
Google Play Console checks targetSdkVersion when uploading APK/AAB. If targetSdk is below the requirement — the console blocks publication with the message: "Your app currently targets API level X and must target at least API level Y". The developer must update build.gradle, recompile the app, test behavioural changes and re-upload. AAB format is recommended for all new publications (mandatory since August 2021).
| Date | Minimum targetSdk | Android Version |
|---|---|---|
| August 2022 | 31 | Android 12 |
| August 2023 | 33 | Android 13 |
| August 2024 | 33 | Android 13 |
| August 2025 | 34 | Android 14 |
| August 2026 (planned) | 35 | Android 15 |
Build.VERSION.SDK_INT is a static integer constant containing the API Level of the device on which the app is running. It is the primary tool for runtime Android version checks. Build.VERSION_CODES contains named constants for each API Level: VERSION_CODES.TIRAMISU (33), VERSION_CODES.UPSIDE_DOWN_CAKE (34), VERSION_CODES.VANILLA_ICE_CREAM (35). Comparison using if (SDK_INT >= VERSION_CODES.TIRAMISU) is the standard pattern.
// Examples of checking API Level in Android code
import android.os.Build
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.graphics.drawable.AdaptiveIconDrawable
class ApiLevelHelper {
// 1. Basic API Level check
fun isAtLeastTiramisu(): Boolean {
return VERSION.SDK_INT >= VERSION_CODES.TIRAMISU // 33
}
// 2. Adaptive API call with check
fun getAdaptiveIcon(drawable: android.graphics.drawable.Drawable):
android.graphics.drawable.Drawable? {
// AdaptiveIconDrawable is only available with API 26 (Android 8)
if (VERSION.SDK_INT >= VERSION_CODES.O) {
return AdaptiveIconDrawable(drawable, null)
}
return drawable // fallback for old devices
}
// 3. Checking POST_NOTIFICATIONS permission (API 33+ only)
fun canRequestNotificationPermission(): Boolean {
return VERSION.SDK_INT >= VERSION_CODES.TIRAMISU
}
// 4. Selecting image provider by API Level
fun getImagePickerProvider(): String {
return when {
VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE -> {
// API 34+ uses PhotoPicker
"photo_picker"
}
VERSION.SDK_INT >= VERSION_CODES.KITKAT -> {
// API 19+ uses Intent ACTION_OPEN_DOCUMENT
"open_document"
}
else -> {
// Legacy: ACTION_GET_CONTENT (all versions)
"get_content"
}
}
}
// 5. Java-style check via @TargetApi (for backward compatibility)
@Suppress("DEPRECATION")
fun checkLegacyStorage(): Boolean {
// Scoped Storage behavior depends on targetSdk, not SDK_INT
return VERSION.SDK_INT < VERSION_CODES.Q // Android 10
}
// 6. Build info for analytics
fun getDeviceApiInfo(): Map<String, Any> {
return mapOf(
"sdk_int" to VERSION.SDK_INT,
"release" to VERSION.RELEASE,
"codename" to VERSION.CODENAME,
"incremental" to VERSION.INCREMENTAL,
"preview_sdk" to VERSION.PREVIEW_SDK_INT
)
}
}
// Testing
fun main() {
val helper = ApiLevelHelper()
println("API Level: ${VERSION.SDK_INT}")
println("Is Tiramisu+: ${helper.isAtLeastTiramisu()}")
}The ApiLevelHelper class demonstrates all the main API Level checking patterns: isAtLeastTiramisu with SDK_INT >= VERSION_CODES, getAdaptiveIcon with fallback for old versions, getImagePickerProvider with when multi-branching, getDeviceApiInfo for analytics. The key rule is not to call new APIs without checking SDK_INT, otherwise the app will crash with NoSuchMethodError on old devices.
Android Studio includes the lint static analyzer, which warns about using APIs above minSdkVersion. If a method is called without checking SDK_INT, lint highlights it as an error: "Call requires API level 34 (current min is 26)". Solutions: add @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) to the method or an if-check of SDK_INT. @TargetApi is a deprecated annotation, @RequiresApi is recommended.
API Level Table is a reference tool for the developer. Knowing the device API Level, you can determine the Android version and available features. The table lists all major Android releases from API Level 1 (2008) to API Level 36 (2025). Code names (Cupcake, Donut, Tiramisu, VanillaIceCream) are used inside Google and in VERSION_CODES.
| API Level | Android Version | Code Name | Year |
|---|---|---|---|
| 1 | 1.0 | — | 2008 |
| 3 | 1.5 | Cupcake | 2009 |
| 8 | 2.2 | Froyo | 2010 |
| 14 | 4.0 | Ice Cream Sandwich | 2011 |
| 19 | 4.4 | KitKat | 2013 |
| 21 | 5.0 | Lollipop | 2014 |
| 23 | 6.0 | Marshmallow | 2015 |
| 26 | 8.0 | Oreo | 2017 |
| 28 | 9 | Pie | 2018 |
| 29 | 10 | Quince Tart (10) | 2019 |
| 30 | 11 | Red Velvet Cake | 2020 |
| 31 | 12 | Snow Cone | 2021 |
| 33 | 13 | Tiramisu | 2022 |
| 34 | 14 | Upside Down Cake | 2023 |
| 35 | 15 | Vanilla Ice Cream | 2024 |
| 36 | 16 | Baklava | 2025 |
The following table shows key API Levels that introduce behavioural changes which break backward compatibility when raising targetSdk:
| API Level | Behavioural Change | Impact on App |
|---|---|---|
| 29 | Scoped Storage | No direct File access to Pictures/Downloads/Music |
| 30 | Package Visibility | queryIntentActivities() only sees interacting packages |
| 31 | Foreground Service Notification | Mandatory notification within 10 seconds |
| 33 | POST_NOTIFICATIONS | Runtime permission for notifications |
| 34 | Foreground Service Types | Foreground service type declaration in manifest |
| 35 | Privacy Sandbox | Advertising identifier restrictions |
Frequently Asked Questions
API Level Android is an integer identifier of the Android API version. Each release has a unique number: Android 13 = API 33, Android 14 = API 34, Android 15 = API 35, Android 16 = API 36. The developer specifies minSdkVersion, targetSdkVersion and compileSdkVersion in build.gradle to manage compatibility. API Level determines available classes, methods and behavioral changes.
minSdkVersion — the minimum Android version for installing the app. targetSdkVersion — the version against which the app was tested, includes behavioural changes. compileSdkVersion — the SDK version for compiling code. minSdk is the lowest, targetSdk should preferably be the latest, compileSdk must be at least targetSdk. All three are specified in build.gradle.
If targetSdkVersion is lower than the device API Level, Android disables behavioural changes introduced after targetSdk. For example, with targetSdk = 28 on Android 14 (API 34), Scoped Storage, POST_NOTIFICATIONS, Foreground Service Types are not applied. Google Play requires targetSdkVersion no older than 1 year from the current API Level for user safety.
The device API Level is available via the constant Build.VERSION.SDK_INT (e.g., 34 for Android 14). For comparison, use named constants from Build.VERSION_CODES: if (SDK_INT >= VERSION_CODES.TIRAMISU). Build.VERSION.RELEASE returns the version string ("14"). The SDK_INT value is cached when the class is loaded and is accessible from any thread.
Google Play raises targetSdkVersion requirements annually to implement security behavioural changes. Each new API Level introduces Scoped Storage, POST_NOTIFICATIONS, Privacy Sandbox and other protections. Apps with a low targetSdk bypass these protections and create risks for users. The requirement ensures all apps in the store have been tested against current rules.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also