> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fingerprint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Android SDK

> A simple guide to integrate Fingerprint's device intelligence platform in your native Android apps.

In this guide, you will learn to

* Include Android SDK in your mobile apps.
* Get a `visitorId`.
* Read the SDK response.
* Enable location data collection (optional; to start getting additional location-based proximity data).
* Configure the SDK as per your needs.
* Specify additional metadata in your identification request.
* Handle errors.

For a complete example of how to use this SDK in your app, please visit the [GitHub repo of our demo app](https://github.com/fingerprintjs/fingerprint-device-intelligence-android-demo). View our [Android quickstart](/docs/android-quickstart) for a step-by-step guide to get started.

## Prerequisites

[Sign up](https://dashboard.fingerprintjs.com/signup) for an account with Fingerprint to get your API key.

## Including the SDK in your app

1. Add the repositories to your **Project Settings** file. Depending on your build configuration language, this file can either be `settings.gradle.kts` or `settings.gradle`.

   <CodeGroup>
     ```kotlin settings.gradle.kts (Kotlin DSL) theme={"theme":"github-dark-dimmed"}
       ...
       dependencyResolutionManagement {
           repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
           repositories {
               google()
               mavenCentral()
               maven { url = uri("https://maven.fpregistry.io/releases") }
           }
       }
     ```

     ```groovy settings.gradle (Groovy DSL) theme={"theme":"github-dark-dimmed"}
       ...
       dependencyResolutionManagement {
           repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
           repositories {
               google()
               mavenCentral()
               maven { url 'https://maven.fpregistry.io/releases' }
           }
       }
     ```
   </CodeGroup>

   Alternatively, if your project is configured to [ignore the repositories declared in the Project Settings](https://docs.gradle.org/current/javadoc/org/gradle/api/initialization/resolve/RepositoriesMode.html) file, you can add the repositories to the **project-level** build file. Depending on your build configuration language, this file can either be `build.gradle.kts` or `build.gradle`.

   <CodeGroup>
     ```kotlin build.gradle.kts (Kotlin DSL) theme={"theme":"github-dark-dimmed"}
       allprojects {
           repositories {
               google()
               mavenCentral()
               maven { url = uri("https://maven.fpregistry.io/releases") }
           }
       }
     ```

     ```groovy build.gradle (Groovy DSL) theme={"theme":"github-dark-dimmed"}
       ...
       allprojects {
           repositories {
               google()
             	mavenCentral()
               maven { url 'https://maven.fpregistry.io/releases' }
           }
       }
     ```
   </CodeGroup>

2. Add the dependencies to your **module-level** build file. Depending on your build configuration language, this file can either be `build.gradle.kts` or `build.gradle`.

   <CodeGroup>
     ```kotlin build.gradle.kts (Kotlin DSL) theme={"theme":"github-dark-dimmed"}
     dependencies {
         implementation("com.fingerprint.android:pro:2.16.0")
     }
     ```

     ```groovy build.gradle (Groovy DSL) theme={"theme":"github-dark-dimmed"}
      dependencies {
        implementation "com.fingerprint.android:pro:2.16.0"
      }
     ```
   </CodeGroup>

3. Perform a Gradle sync to update all the dependencies.

## Getting a `visitorId`

To get a `visitorId`, you need the public API key that you obtained when signing up for an account with Fingerprint. You can find this API key in the **Dashboard** > [**API Keys**](https://dashboard.fingerprint.com/api-keys).

Here is an example that shows you how to get a `visitorId`:

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  val factory = FingerprintJSFactory(applicationContext)
  val configuration = Configuration(apiKey = "PUBLIC_API_KEY")
  val fpjsClient = factory.createInstance(configuration)

  scope.launch(Dispatchers.IO) {
      try {
          val response = fpjsClient.getVisitorId()
          val visitorId = response.visitorId
      } catch (e: FingerprintException) {
          // Handle error
      }
  }
  ```

  ```java Java theme={"theme":"github-dark-dimmed"}
  FingerprintJSFactory factory = new FingerprintJSFactory(getApplicationContext());
  Configuration configuration = new Configuration("PUBLIC_API_KEY");
  FingerprintJS fpjsClient = factory.createInstance(configuration);

  fpjsClient.getVisitorId(
      response -> {
          String visitorId = response.getVisitorId();
          return null;
      },
      error -> {
          // Handle error
          return null;
      }
  );
  ```
</CodeGroup>

<Note>
  The `getVisitorId()` suspend functions perform blocking I/O and should be called from a background
  dispatcher such as `Dispatchers.IO`. Cancelling the coroutine does not cancel the underlying
  signal collection or network request. The coroutine API is Kotlin-only; Java users should use the
  callback-based API.
</Note>

### Specifying a custom timeout

**Default timeout value:** `null`

The identification requests made from an Android SDK do not have a default timeout.

You can use `timeoutMillis` to provide a custom timeout value of your choice. If the `getVisitorId()` call does not complete within the specified timeout, you will receive a `ClientTimeout` error.

Here is an example that shows you how to specify a custom timeout:

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  scope.launch(Dispatchers.IO) {
      try {
          // Get a visitor ID, will timeout after 10s
          val response = fpjsClient.getVisitorId(
              timeoutMillis = 10_000
          )
          val visitorId = response.visitorId
      } catch (e: FingerprintException) {
          if (e.error is ClientTimeout) {
              // Handle timeout
          }
      }
  }
  ```

  ```java Java theme={"theme":"github-dark-dimmed"}
  fpjsClient.getVisitorId(
    // Pass a timeout value of 10 seconds
    10000,
    visitorIdResponse -> {
      // Get a 'visitorId', will timeout after 10s
      visitorId = visitorIdResponse.getVisitorId();
      return null;
    }
  );
  ```
</CodeGroup>

## Using Location Data for Proximity Detection

<Note>
  This is optional. Enable this only if you want to include the additional location-based [proximity detection](/docs/smart-signals-reference#proximity-detection) signal in your response.
</Note>

Starting with [Android SDK 2.10.0](/docs/changelog-android-sdk#v2-10-0), you can receive additional location-based signals (proximity ID, confidence, precision radius) by enabling [`allowUseOfLocationData`](#allowuseoflocationdata) and declaring the corresponding [location permissions](/docs/android-sdk#location-permissions).

### Location permissions

#### Declaring permissions

To calculate Proximity ID and related data (confidence score, precision radius), Fingerprint needs the following location permissions in the manifest:

<CodeGroup>
  ```xml XML theme={"theme":"github-dark-dimmed"}
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  ```
</CodeGroup>

At least coarse location permission is required. Please note that accuracy will be lower without fine location permission.

#### Asking for permissions

Your app is responsible for asking for permissions. Refer to [Android documentation](https://developer.android.com/training/permissions/requesting) or refer to the [Fingerprint demo app](https://github.com/fingerprintjs/fingerprint-device-intelligence-android-demo) if you need to implement this flow. If you already have this process set up, skip to the next section.

### Enabling location collection

Fingerprint can only collect location data if [`allowUseOfLocationData`](#allowuseoflocationdata) is set to `true`. Optionally, you can control the timeout by setting [`locationTimeoutMillis`](#locationtimeoutmillis) to the desired value. Note that the value is 5 seconds by default.

**Timeout Recommendation:**

Because location accuracy and performance can vary based on user settings, device models, and use cases, **we do not enforce a fixed default timeout.** We recommend that you explicitly configure a timeout value that aligns with the app's user experience and performance expectations.

## Reading the response

The function `FingerprintJS.getVisitorId()` returns the response in a `FingerprintJSProResponse` object. This object has the following fields, some of which can be empty/invalid when [Sealed Client Results](/docs/sealed-client-results) is enabled for your account.

| Field                 | Sealed Client Results is disabled                                                                                                                                      | Sealed Client Results is enabled                                                                                                                                            |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`requestId`**       | String. An identifier that uniquely identifies this particular request to `FingerprintJS.getVisitorID()`.                                                              | String. An identifier that uniquely identifies this particular request to `FingerprintJS.getVisitorID()`.                                                                   |
| **`visitorId`**       | String. An identifier that uniquely identifies the device.                                                                                                             | N/A                                                                                                                                                                         |
| **`confidenceScore`** | [ConfidenceScore](/docs/android-sdk#confidencescore). A score that indicates the probability of this device being accurately identified. The value ranges from 0 to 1. | N/A                                                                                                                                                                         |
| **`sealedResult`**    | `null`                                                                                                                                                                 | String?. An encrypted, binary, Base64-encoded String that contains the same response as you would receive with an [/events API](/reference/server-api-v4-get-event) request |

## Configuring the SDK

Using the [Configuration](/docs/android-sdk#configuration) object, it is possible to configure the SDK as per your requirements. As of now, the following options are supported:

### `region`

**Type:** [Region](/docs/android-sdk#region).

**Default value:** `Region.US`

This option allows you to specify a region where you want your data to be stored and processed. This region must be the same as the one you specified when registering your app with Fingerprint. See [region](/reference/js-agent-v4-start-function#region) for more information.

### `endpointUrl`

**Type:** String

**Default value:** `Region.US.getEndpointUrl()`

This option allows you to specify a custom endpoint, particularly when you have set up either a custom sub-domain or a proxy integration.

### `fallbackEndpointUrls`

**Type:** List\<String>

**Default value:** emptyList()

This option allows you to specify alternate, fallback endpoints to redirect failed requests.

### `extendedResponseFormat`

**Type:** Boolean

**Default value:** `false`

When set to true, in addition to those [fields returned by default](/docs/android-sdk#reading-the-response), the `FingerprintJSProResponse` object will also contain the following fields:

* **`visitorFound`** - Boolean. Indicates if this device has already been encountered by your app.
* **`ipAddress`** - String. The IPv4 address of the device.
* **`ipLocation`** - [IPLocation](/docs/android-sdk#iplocation). \[This field is **deprecated** and will not return a result for **applications created after January 23rd, 2024**. See [IP Geolocation](/docs/smart-signals-reference#ip-geolocation) for a replacement available in our Smart Signals product.] Indicates the location as deduced from the IP address.
* **`osName`** - String. Indicates the name of the underlying operating system.
* **`osVersion`** - String. Indicates the version of the underlying operating system of the device.
* **`firstSeenAt`**, **`lastSeenAt`** - [Timestamp](/docs/android-sdk#timestamp). See [Visitor Footprint Timestamps](/docs/useful-timestamps).

When Sealed Client Results is enabled for your account, several of these fields may not have valid values.

### `allowUseOfLocationData`

**Type:** Boolean

**Default value:** `false`

This option allows you to enable the location data collection needed to calculate the [location-based proximity detection signal](/docs/smart-signals-reference#proximity-detection).

### `locationTimeoutMillis`

**Type:** Long

**Default value:** `5_000L` (5 seconds)

This option allows you to configure the maximum timeout for location collection (controlled by [`allowUseOfLocationData`](#allowuseoflocationdata)).

The SDK will delay identification up to the specified timeout to collect the device location. If it cannot collect the location information within the specified time, identification continues without location information.

## Specifying `linkedID` and `tag`

Like the [JavaScript agent](/reference/js-agent-v4-get-function#linkedid), the Android SDK also supports providing custom metadata with your identification request. To learn more about this capability, please visit [Linking and tagging information](/docs/tagging-information).

Here is an example that shows you how to associate your identification request with an account ID and additional metadata:

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  scope.launch(Dispatchers.IO) {
      try {
          val response = fpjsClient.getVisitorId(
              // Associate additional metadata to this request
              tags = mapOf("orderID" to orderId, "zip" to zipCode),
              // Associate an account number to this request
              linkedId = accountID
          )
          val visitorId = response.visitorId
      } catch (e: FingerprintException) {
          // Handle error
      }
  }
  ```

  ```java Java theme={"theme":"github-dark-dimmed"}
  Map<String, String> tags = new HashMap<>();
  tags.put("orderID", orderId);
  tags.put("zip", zipCode);

  fpjsClient.getVisitorId(
    // Associate additional metadata to this request
    tags,
    // Associate an account number to this request
    accountId,
    // Get a 'visitorId'
    visitorIdResponse -> {
      // handle visitorId and other data
    },
  );
  ```
</CodeGroup>

The metadata you want to include may not be available when making the identification request. For such scenarios, you can [update that event](/reference/server-api-v4-update-event/) later when the required metadata becomes available. A typical workflow is explained in [`Update linkedId and tag on the server`](/docs/tagging-information#updating-linked_id-and-tag-on-the-server).

## Handling errors

The SDK provides an [Error](/docs/android-sdk#error) class that helps you identify the reasons behind an unsuccessful identification request. Here is an example that shows you how to handle errors in your app:

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  scope.launch(Dispatchers.IO) {
      try {
          val response = fpjsClient.getVisitorId()
          val visitorId = response.visitorId
      } catch (e: FingerprintException) {
          when (e.error) {
              is ApiKeyRequired -> {
                  val requestId = e.requestId
                  // Handle error
              }
              is NetworkError -> {
                  // Handle network error
              }
              is ClientTimeout -> {
                  // Handle timeout
              }
              else -> {
                  Log.e("Fingerprint", "${e.requestId}: ${e.error.description}")
              }
          }
      }
  }
  ```

  ```java Java theme={"theme":"github-dark-dimmed"}
  Map<String, String> tags = new HashMap<>();
  tags.put("orderID", orderId);
  tags.put("zip", zipCode);

  fpjsClient.getVisitorId(
    // Associate additional metadata to this request
    tags,
    // Associate an account number to this request
    accountID,
    // Get a 'visitorId'
    visitorIdResponse -> {
      // Handle visitorId and other data
      String visitorId = visitorIdResponse.getVisitorId();
    	return null;
    },
    // Handle errors
    error -> {
      // Handle errors as per their type
      if (error.getClass() == ApiKeyRequired.class) {
        Log.e(TAG, error.getRequestId() + error.getDescription());
    	}
    	return null;
    }
  );
  ```
</CodeGroup>

## Data Classes

### ConfidenceScore

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  data class ConfidenceScore(
    val score: Double
  )
  ```
</CodeGroup>

### Configuration

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  public class Configuration @JvmOverloads constructor(
      public val apiKey: String,
      public val region: Region = Region.US,
      public val endpointUrl: String = region.endpointUrl,
      public val extendedResponseFormat: Boolean = false,
      public val fallbackEndpointUrls: List<String> = emptyList(),
      public val integrationInfo: List<Pair<String, String>> = emptyList(),
      public val allowUseOfLocationData: Boolean = false,
      public val locationTimeoutMillis: Long = DEFAULT_LOCATION_TIMEOUT_MILLIS,
  )
  ```
</CodeGroup>

### Error

It's a sealed class, which can be one of:

* ApiKeyRequired
* ApiKeyNotFound
* ApiKeyExpired
* RequestCannotBeParsed
* Failed
* RequestTimeout
* TooManyRequest
* OriginNotAvailable
* PackageNotAuthorized
* HeaderRestricted
* NotAvailableForCrawlBots
* NotAvailableWithoutUA
* WrongRegion
* SubscriptionNotActive
* UnsupportedVersion
* InstallationMethodRestricted
* ResponseCannotBeParsed
* InvalidProxyIntegrationHeaders
* InvalidProxyIntegrationSecret
* NetworkError
* UnknownError
* ClientTimeout
* ProxyIntegrationSecretEnvironmentMismatch

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  sealed class Error(
    // The request ID of the identification request
    val requestId: String = UNKNOWN,
    // A self-explanatory description of the error
    val description: String? = UNKNOWN
  )
  ```
</CodeGroup>

### FingerprintException

Used with the coroutine-based `getVisitorId()` suspend functions. Wraps the [`Error`](/docs/android-sdk#error) sealed class and is thrown as an exception for use with `try/catch`.

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  class FingerprintException(
    // The error that caused the exception
    val error: Error
  ) : Exception(error.description) {
    // The request ID associated with this error
    val requestId: String get() = error.requestId
  }
  ```
</CodeGroup>

### IPLocation

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  data class IpLocation(
    val accuracyRadius: Int,
    val latitude: Double,
    val longitude: Double,
    val postalCode: String,
    val timezone: String,
    val city: City,
    val country: Country,
    val continent: Continent,
    val subdivisions: List<Subdivisions>
  ) {
    data class City(
      val name: String
    )
    data class Country(
      val code: String,
      val name: String
    )
    data class Continent(
      val code: String,
      val name: String
    )
    data class Subdivisions(
      val isoCode: String,
      val name: String
    )
  }
  ```
</CodeGroup>

### Region

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  public enum class Region(public val endpointUrl: String) {
    US("https://api.fpjs.io"),
    EU("https://eu.api.fpjs.io"),
    AP("https://ap.api.fpjs.io")
  }
  ```
</CodeGroup>

### Timestamp

<CodeGroup>
  ```kotlin Kotlin theme={"theme":"github-dark-dimmed"}
  data class Timestamp(
    val global: String,
    val subscription: String
  }
  ```
</CodeGroup>

***

[iOS SDK](/docs/ios-sdk)
