Device Data Retrieval Guide
Comprehensive guide for retrieving platform-specific device identifiers and parameters required for accurate S2S API attribution and campaign measurement.
Required Device Identifiers: Singular requires specific device identifiers in all API requests for accurate attribution.
Mobile Platforms:
- Android (Google Play): Google Advertising ID (GAID/AIFA) and App Set ID (ASID)
- Android (Amazon): Amazon Advertising ID (AMID) for Fire devices
- Android (Chinese OEMs): Open Advertising ID (OAID) for devices without Google Play Services
- Android (Fallback): Android ID (ANDI) only when no other identifiers available
- iOS: Identifier for Vendors (IDFV) and Identifier for Advertisers (IDFA) when available
Device Parameters: Locale, Device Make, Device Model, and Build version required for mobile platforms
Code examples below demonstrate retrieval methods for each platform and identifier type.
Sample Applications
Reference Implementations
Complete working examples for iOS and Android demonstrating device data retrieval patterns.
iOS Device Identifiers
iOS devices require IDFV (always) and IDFA (when user grants tracking permission) plus ATT authorization status for accurate attribution.
Required iOS Identifiers
Identifier Requirements:
- IDFV: Required on all S2S requests regardless of tracking permission
- IDFA: Should be provided if App Tracking Transparency consent granted by user
- ATT Status: Authorization status code required on all requests (0-3)
Implementation Guide
Identifier for Advertisers (IDFA)
The Identifier for Advertisers (IDFA) enables advertisers to track and attribute user actions (ad clicks, app installs) to specific campaigns for precise targeting and optimization.
Starting iOS 14.5, users must opt-in via App Tracking Transparency (ATT) framework before apps access IDFA. Without user consent, IDFA returns all zeros, limiting tracking capabilities.
Identifier for Vendors (IDFV)
The Identifier for Vendors (IDFV) is unique identifier assigned by Apple to device, specific to vendor/developer. Remains consistent across all apps from same vendor on device, enabling cross-app behavior tracking without personal identification.
Implementation Steps:
- Ensure ATT prompt shown and handled before attempting IDFA access
- Capture IDFA (if authorized) and pass to server for API requests
- Capture IDFV and pass to server for API requests (always required)
- Include ATT authorization status in all requests
Code Examples
Requesting ATT Authorization and Retrieving IDFA/IDFV
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <UIKit/UIKit.h>
- (void)retrieveIdentifiers {
// Request ATT authorization (iOS 14.5+)
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (status) {
case ATTrackingManagerAuthorizationStatusAuthorized: {
// ATT authorized, retrieve IDFA
NSUUID *idfa = [[ASIdentifierManager sharedManager] advertisingIdentifier];
NSLog(@"IDFA: %@", [idfa UUIDString]);
NSLog(@"ATT Status: %ld", (long)status); // Status = 3
break;
}
case ATTrackingManagerAuthorizationStatusDenied:
NSLog(@"ATT Status: Denied (%ld)", (long)status); // Status = 2
break;
case ATTrackingManagerAuthorizationStatusRestricted:
NSLog(@"ATT Status: Restricted (%ld)", (long)status); // Status = 1
break;
case ATTrackingManagerAuthorizationStatusNotDetermined:
NSLog(@"ATT Status: Not Determined (%ld)", (long)status); // Status = 0
break;
default:
NSLog(@"Unknown ATT status.");
break;
}
// Retrieve IDFV (always available)
NSUUID *idfv = [[UIDevice currentDevice] identifierForVendor];
if (idfv != nil) {
NSLog(@"IDFV: %@", [idfv UUIDString]);
} else {
NSLog(@"Unable to retrieve IDFV.");
}
});
}];
}
// Call the method to retrieve identifiers
[self retrieveIdentifiers];
import AdSupport
import AppTrackingTransparency
import UIKit
func retrieveIdentifiers() {
// Request ATT authorization (iOS 14.5+)
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
// ATT authorized, retrieve IDFA
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("IDFA: \(idfa)")
print("ATT Status: \(status.rawValue)") // Status = 3
case .denied:
print("ATT Status: Denied (\(status.rawValue))") // Status = 2
case .restricted:
print("ATT Status: Restricted (\(status.rawValue))") // Status = 1
case .notDetermined:
print("ATT Status: Not Determined (\(status.rawValue))") // Status = 0
@unknown default:
print("Unknown ATT status.")
}
// Retrieve IDFV (always available)
if let idfv = UIDevice.current.identifierForVendor?.uuidString {
print("IDFV: \(idfv)")
} else {
print("Unable to retrieve IDFV.")
}
}
}
}
// Call the function to retrieve identifiers
retrieveIdentifiers()
Identifier Availability:
- IDFA: Requires ATT authorization from iOS 14.5+. Without consent, returns all zeros
- IDFV: Always available—include in all Singular API requests
- ATT Status Values: 0=Undetermined, 1=Restricted, 2=Denied, 3=Authorized
Android Device Identifiers (Google Play)
Android devices with Google Play Services require App Set ID (ASID) on all requests, with Google Advertising ID (GAID/AIFA) when available.
Required Google Play Identifiers
Identifier Requirements:
- ASID: Required on all S2S requests for Google Play devices
- AIFA/GAID: Should be provided when available (not opted out)
Implementation Guide
Google Advertising Identifier (GAID)
The Google Advertising Identifier (GAID), also known as AIFA or Android Advertising ID (AAID), is unique, user-resettable identifier assigned to Android devices. Enables advertisers and developers to track and attribute user actions across apps for campaign targeting and optimization while maintaining privacy.
Dependencies
Add required dependency in your build.gradle:
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
}
Permissions
If targeting Android 12/API level 31+, add permission
in AndroidManifest.xml:
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
Usage
AdIdUtils.getGoogleAdId(getApplicationContext());
Implementation
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
public class AdIdUtils {
public static void getGoogleAdId(Context context) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
String adId = adInfo.getId();
boolean isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled();
Log.d("GoogleAdID", "Advertising ID: " + adId);
Log.d("GoogleAdID", "Limit Ad Tracking: " + isLimitAdTrackingEnabled);
} catch (Exception e) {
Log.e("GoogleAdID", "Error retrieving GAID", e);
}
}
});
}
}
Dependencies
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
}
Permissions
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
Usage
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
val googleAdId = AdIdUtils.getGoogleAdId(applicationContext)
Log.d("MainActivity", "Retrieved Google Ad ID: $googleAdId")
}
}
}
Implementation
import android.content.Context
import android.util.Log
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object AdIdUtils {
suspend fun getGoogleAdId(context: Context): String? {
return withContext(Dispatchers.IO) {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
val adId = adInfo.id
val isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled
Log.d("GoogleAdID", "Advertising ID: $adId")
Log.d("GoogleAdID", "Limit Ad Tracking: $isLimitAdTrackingEnabled")
adId
} catch (e: Exception) {
Log.e("GoogleAdID", "Error retrieving GAID", e)
null
}
}
}
}
App Set ID (ASID)
Android App Set ID provides privacy-conscious cross-app tracking for same developer. Useful for analytics and fraud prevention but cannot be used for personalized advertising.
Dependencies
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
Usage
AppSetIdUtils.getAppSetId(getApplicationContext());
Implementation
import android.content.Context;
import android.util.Log;
import com.google.android.gms.appset.AppSet;
import com.google.android.gms.appset.AppSetIdClient;
import com.google.android.gms.appset.AppSetIdInfo;
import com.google.android.gms.tasks.Task;
public class AppSetIdUtils {
public static void getAppSetId(Context context) {
AppSetIdClient client = AppSet.getClient(context);
Task task = client.getAppSetIdInfo();
task.addOnSuccessListener(info - {
String appSetId = info.getId();
int scope = info.getScope();
Log.d("AppSetID", "App Set ID: " + appSetId);
Log.d("AppSetID", "Scope: " + (scope == AppSetIdInfo.SCOPE_DEVELOPER ? "Developer" : "App"));
}).addOnFailureListener(e - {
Log.e("AppSetID", "Failed to retrieve App Set ID", e);
});
}
}
Dependencies
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
Usage
AppSetIdUtils.getAppSetId(applicationContext)
Implementation
import android.content.Context
import android.util.Log
import com.google.android.gms.appset.AppSet
import com.google.android.gms.appset.AppSetIdClient
import com.google.android.gms.appset.AppSetIdInfo
object AppSetIdUtils {
fun getAppSetId(context: Context) {
val client: AppSetIdClient = AppSet.getClient(context)
val task = client.appSetIdInfo
task.addOnSuccessListener { info -
val appSetId: String = info.id
val scope: Int = info.scope
Log.d("AppSetID", "App Set ID: $appSetId")
Log.d("AppSetID", "Scope: ${if (scope == AppSetIdInfo.SCOPE_DEVELOPER) "Developer" else "App"}")
}.addOnFailureListener { exception -
Log.e("AppSetID", "Failed to retrieve App Set ID", exception)
}
}
}
Android Device Identifiers (Non-Google Play)
Android devices without Google Play Services require alternative identifiers based on device manufacturer and distribution method.
Amazon Device Identifier
AMID: Amazon Advertising ID should be provided for Amazon Fire devices without Google Play Services.
Amazon ID (AMID)
Amazon Advertising Identifier enables user-resettable advertising tracking on Amazon Fire devices without Google Play Services, maintaining user privacy while enabling attribution.
Requirements:
- Works on Amazon Fire devices running Fire OS 5.1+
- Respects user's Limit Ad Tracking preference
- May not be available on non-Fire OS devices
Usage
AdvertisingIdHelper.getAmazonAdvertisingId(getContentResolver());
Implementation
import android.content.ContentResolver;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
public class AdvertisingIdHelper {
public static void getAmazonAdvertisingId(ContentResolver contentResolver) {
String advertisingID = "";
boolean limitAdTracking = false;
try {
limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0;
advertisingID = Settings.Secure.getString(contentResolver, "advertising_id");
Log.d("AdvertisingID", "Amazon Advertising ID: " + advertisingID);
Log.d("LimitAdTracking", "Limit Ad Tracking: " + limitAdTracking);
} catch (SettingNotFoundException e) {
Log.e("AdvertisingID", "Advertising ID not supported on this device", e);
}
}
}
Usage
AdvertisingIdHelper.getAmazonAdvertisingId(contentResolver)
Implementation
import android.content.ContentResolver
import android.provider.Settings
import android.util.Log
object AdvertisingIdHelper {
fun getAmazonAdvertisingId(contentResolver: ContentResolver) {
try {
val limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0
val advertisingID = Settings.Secure.getString(contentResolver, "advertising_id")
Log.d("AdvertisingID", "Amazon Advertising ID: $advertisingID")
Log.d("LimitAdTracking", "Limit Ad Tracking: $limitAdTracking")
} catch (e: Settings.SettingNotFoundException) {
Log.e("AdvertisingID", "Advertising ID not supported on this device", e)
}
}
}
Chinese OEM Identifier
OAID: Open Advertising Identifier should be provided for Chinese-manufactured devices without Google Play Services.
Open Advertising ID (OAID)
Open Advertising Identifier (OAID) is unique, anonymous identifier for advertising on Android devices manufactured in China. Introduced by Mobile Security Alliance (MSA) as alternative to GAID for devices where Google Play Services unavailable.
Supported Devices: Huawei, Xiaomi, OPPO, Vivo, and other Chinese-manufactured Android devices
Access via MSA SDK or Huawei Mobile Services (HMS).
Dependencies
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26'
}
Implementation
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import com.bun.msa.sdk.DeviceId;
import com.bun.msa.sdk.DeviceIdSupplier;
import com.bun.msa.sdk.IIdentifierListener;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "OAIDExample";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getOAID();
}
private void getOAID() {
try {
DeviceId deviceId = new DeviceId(this);
deviceId.getDeviceIds(new IIdentifierListener() {
@Override
public void onSupport(boolean isSupport, DeviceIdSupplier supplier) {
if (isSupport && supplier != null) {
String oaid = supplier.getOAID();
Log.d(TAG, "OAID: " + oaid);
} else {
Log.e(TAG, "OAID not supported on this device");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Error retrieving OAID", e);
}
}
}
Dependencies
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26'
}
Implementation
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.bun.msa.sdk.DeviceId
import com.bun.msa.sdk.DeviceIdSupplier
import com.bun.msa.sdk.IIdentifierListener
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "OAIDExample"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getOAID()
}
private fun getOAID() {
try {
val deviceId = DeviceId(this)
deviceId.getDeviceIds(object : IIdentifierListener {
override fun onSupport(isSupport: Boolean, supplier: DeviceIdSupplier?) {
if (isSupport && supplier != null) {
val oaid = supplier.oAID
Log.d(TAG, "OAID: $oaid")
} else {
Log.e(TAG, "OAID not supported on this device")
}
}
})
} catch (e: Exception) {
Log.e(TAG, "Error retrieving OAID", e)
}
}
}
Android ID Fallback
ANDI Restrictions: Android ID may only be provided if no other identifiers available AND app not distributed via Google Play Store. Prohibited for Google Play apps.
Android ID (ANDI)
Android ID is unique 64-bit identifier generated when device first set up. Starting Android 8.0 (Oreo), scoped per app and per user—different apps receive different Android IDs unless they share same signing key.
Persistence: Remains constant unless device factory reset or app uninstalled/reinstalled after OTA update.
import android.provider.Settings;
import android.content.Context;
String androidId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID
);
import android.provider.Settings
val androidId: String = Settings.Secure.getString(
contentResolver,
Settings.Secure.ANDROID_ID
)
Web & Cross-Platform Identifiers
Web applications and cross-platform implementations require Singular Device ID (SDID) for accurate attribution tracking.
Required Web Identifier
SDID: Singular Device ID required on all S2S requests for Web, PC, Console, and CTV platforms.
Singular Web SDK Device ID
Singular Device ID (SDID) provides consistent cross-session tracking for web applications and non-mobile platforms.
Prerequisites: Singular Web SDK must be implemented and initialized before retrieving SDID.
Usage
// Retrieve SDID after Singular SDK initialization
const sdid = window.singularSdk.getSingularDeviceId();
console.log("Singular Device ID:", sdid);
Implementation Note: Call
getSingularDeviceId()
only after Singular SDK successfully initialized—attempting
retrieval before
initialization returns null.
Mobile Device Parameters
Required device parameters provide essential context for attribution and analytics on mobile platforms.
Required Parameters
Mobile Platforms: Locale, Device Make, Device Model, and Build required on all S2S requests for iOS and Android.
Parameter Retrieval
Collect locale, manufacturer, model, and build information for complete device profiling.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <sys/sysctl.h>
// Retrieve Locale
NSString *retrieveLocale() {
NSString *locale = [[NSLocale currentLocale] localeIdentifier];
NSLog(@"Locale: %@", locale);
return locale;
}
// Retrieve Manufacturer (always Apple for iOS)
NSString *retrieveManufacturer() {
return @"Apple";
}
// Retrieve Device Model
NSString *deviceModel() {
size_t bufferSize = 64;
char model[bufferSize];
int status = sysctlbyname("hw.machine", model, &bufferSize, NULL, 0);
if (status == 0) {
NSString *deviceModel = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
NSLog(@"Device Model: %@", deviceModel);
return deviceModel;
} else {
NSLog(@"Unable to retrieve device model.");
return nil;
}
}
// Retrieve Build Version
NSString *buildVersion() {
size_t bufferSize = 64;
char build[bufferSize];
int status = sysctlbyname("kern.osversion", build, &bufferSize, NULL, 0);
if (status == 0) {
NSString *buildVersion = [NSString stringWithCString:build encoding:NSUTF8StringEncoding];
NSLog(@"Build Version: %@", buildVersion);
return buildVersion;
} else {
NSLog(@"Unable to retrieve build version.");
return nil;
}
}
import Foundation
import UIKit
// Retrieve Locale
func retrieveLocale() - String {
let locale = Locale.current.identifier
print("Locale: \(locale)")
return locale
}
// Retrieve Manufacturer (always Apple for iOS)
func retrieveManufacturer() - String {
return "Apple"
}
// Retrieve Device Model
func deviceModel() - String? {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
print("Device Model: \(identifier)")
return identifier
}
// Retrieve Build Version
func buildVersion() - String? {
var size: Int = 0
sysctlbyname("kern.osversion", nil, &size, nil, 0)
var build = [CChar](repeating: 0, count: size)
sysctlbyname("kern.osversion", &build, &size, nil, 0)
let buildVersion = String(cString: build)
print("Build Version: \(buildVersion)")
return buildVersion
}
import android.os.Build;
import java.util.Locale;
// Locale (lc parameter)
String locale = Locale.getDefault().toString();
// Device Make (ma parameter)
String deviceMake = Build.MANUFACTURER;
// Device Model (mo parameter)
String deviceModel = Build.MODEL;
// Build (bd parameter)
String build = "Build/" + Build.ID;
import android.os.Build
import java.util.Locale
// Locale (lc parameter)
val locale: String = Locale.getDefault().toString()
// Device Make (ma parameter)
val deviceMake: String = Build.MANUFACTURER
// Device Model (mo parameter)
val deviceModel: String = Build.MODEL
// Build (bd parameter)
val build: String = "Build/" + Build.ID
Parameter Mapping:
- Locale (lc): Language and region code (e.g., en_US, zh_CN)
- Make (ma): Device manufacturer (Apple, Samsung, Xiaomi)
- Model (mo): Specific device model (iPhone14,2, SM-G991B)
- Build (bd): OS build version prefixed with "Build/"