Ad Revenue Tracking

Understanding Ad Revenue Attribution

Ad Revenue Attribution helps you connect your ad revenue to the specific campaigns that brought users to your app. This gives you a clear picture of how well your ads are performing by showing the campaign cost, in-app revenue, and ad revenue all in one place. This feature also lets you send ad revenue data back to your ad networks to improve your ad performance.

Key Points:

  • What It Does: Ad Revenue Attribution ties mobile app ad revenue to the marketing campaigns that generated users for your app. This way, you can see how much you earned from each campaign and how it affects your overall ad ROI.
  • Data Source: This data usually comes from your mediation platform and can be at the user level or impression level. Singular supports different ways to get this attribution data.
  • Read More: For more details, check out the FAQ and Troubleshooting article on Singular Ad Revenue Attribution.

Important Notes:

  1. Currency Codes: Use three-letter ISO 4217 currency codes (e.g., "USD" for US Dollars, "EUR" for Euros, "INR" for Indian Rupees). Many mediation platforms use "USD", so make sure your code matches this if you're using it. If you use a different currency, update the validation code accordingly.
  2. Data Accuracy: Always check that your revenue and currency data are correct before sending it to Singular. Incorrect data can't be fixed later, so it’s crucial to ensure it’s accurate.

Implementing Ad Revenue Attribution

  1. Update the SDK: Make sure you have the latest version of the Singular SDK.
  2. Add Code Snippets: Depending on your mediation platform, add the right code snippets to your Singular SDK setup.

Following these steps will help you set up Ad Revenue Attribution correctly and get the most out of your ad data.

AdMob
Partner Notes
  • This feature needs to be enabled in your AdMob account.

    See AdMob Support.

  • When you load an ad format (such as "App Open," "Banner," "Interstitial," "Native," or "Rewarded"), set up a paidEventHandler as a callback function that is triggered whenever an ad generates revenue. The Google Mobile Ads SDK tracks impression events and calls this handler with the Ad generated revenue.

    To do this, modify the "load" function of the ad format to include the paidEventHandler. Inside this callback, you manage the ad revenue data, validate it, and send it to Singular using the Singular.adRevenue function.

    For example, when a "Rewarded ad" loads successfully, the paidEventHandler will receive the ad's revenue information (adValue). In this function, handle the revenue data and send it to Singular.

    For more details, check the AdMob documentation.

    IMPORTANT: The AdMob SDK reports revenue differently depending on the platform. For instance, ad revenue of $0.005 will be returned as 5000 on Unity and Android platforms but as 0.005 on iOS. For iOS, send 0.005 directly to the Singular SDK. On other platforms, convert adValue from micros to dollars before sending it to Singular.
Select the code base for your SDK implementation:
KotlinJava
How it works:
  • Implement the Google AdMob Mobile Ads SDK (Android): See the Getting Started Guide.
  • AdMob Integration: Load the ad from AdMob and set a setOnPaidEventListener to handle ad revenue events.
  • Revenue Validation: Add a check to ensure that revenue is greater than 0. This prevents sending zero or negative revenue values.
  • Currency Validation: Ensure that the currency is not nil or empty before sending data to Singular.
  • Logging Invalid Data: If the data fails validation, print a log message for debugging, and do not send the data to Singular.
import com.singular.sdk.Singular
import com.singular.sdk.SingularAdData

private const val AD_UNIT_ID = "AD_UNIT_ID"

class AdManager(private val context: Context) {

    private var rewardedAd: RewardedAd? = null

    fun loadRewardedAd() {
        val adRequest = AdRequest.Builder().build()
        RewardedAd.load(context, AD_UNIT_ID, adRequest, object : RewardedAdLoadCallback() {
            override fun onAdLoaded(ad: RewardedAd) {
                rewardedAd = ad
                rewardedAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
                    override fun onAdShowedFullScreenContent() {
                        Log.d("AdManager", "Rewarded ad displayed.")
                    }

                    override fun onAdFailedToShowFullScreenContent(adError: AdError) {
                        Log.d("AdManager", "Rewarded ad failed to show with error: ${adError.message}")
                    }

                    override fun onAdDismissedFullScreenContent() {
                        Log.d("AdManager", "Rewarded ad dismissed.")
                    }
                }
                rewardedAd?.setOnPaidEventListener { adValue: AdValue ->
                    // Ensure valid revenue data
                    val revenue = adValue.valueMicros / 1_000_000.0
                    val currency = adValue.currencyCode

                    if (revenue > 0 && !currency.isNullOrEmpty()) {
                        val data = SingularAdData(
                            "Admob",
                            currency,
                            revenue
                        )
                        
                        // Send Ad Revenue data to Singular
                        Singular.adRevenue(data)
                        
                        // Log the data for debugging
                        Log.d("AdManager", "Ad Revenue reported to Singular: $data")
                    } else {
                        Log.d("AdManager", "Invalid ad revenue data: revenue = $revenue, currency = $currency")
                    }
                }
            }

            override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                Log.d("AdManager", "Rewarded ad failed to load with error: ${loadAdError.message}")
            }
        })
    }
}
AppLovinMax
Partner Notes
  • Share impression-level ad revenue data using the Applovin Impression-Level User Revenue API.
Select the code base for your SDK implementation:
KotlinJava
How it works:
  • Uses the AppLovin Impression-Level User Revenue API (Android): See the Getting Started Guide.
  • AppLovin Integration: Load a rewarded ad from AppLovin MAX and use the onMessageReceived function to handle ad revenue events.
  • Revenue Validation: Add a check to ensure that revenue is greater than 0. This prevents sending zero or negative revenue values.
  • Currency Validation: In the sample below, the currency is hard coded to "USD". Ensure that the currency is accurate and not nil or empty before sending data to Singular.
  • Logging Invalid Data: If the data fails validation, print a log message for debugging, and do not send the data to Singular.
import com.singular.sdk.*

override fun onMessageReceived(message: AppLovinCommunicatorMessage) { // In the case that you are subscribed to multiple topics, check for the desired one if ("max_revenue_events" == message.topic) { val adData: Bundle? = message.messageData // Safely access and validate revenue value val revenueValue = adData?.getDouble("revenue", 0.0) ?: 0.0 if (revenueValue > 0) { val data = SingularAdData( adPlatform = "AppLovin", currency = "USD", revenue = revenueValue ) Singular.adRevenue(data) } else { Log.e("AppLovinRevenue", "Failed to parse valid revenue value from message data or revenue is not greater than 0") } } }
Unity LevelPlay (IronSource)
Partner Notes
  • The Impression Level Revenue (ILR) SDK API provides impression level data for ironSource Ads and other mediated networks, using the ironSource SDK. Read more on [developers.is.com]
  • Ensure that the ARM SDK Postbacks Flag in IronSource is turned on
Select the code base for your SDK implementation:
KotlinJava
How it works:
  • Uses the ironSource SDK to get Impression-Level User Revenue (Android): See the Getting Started Guide.
  • Ironsource Integration: Load a rewarded ad from Ironsource and use the onImpressionDataSuccess function to handle Ad Revenue events.
  • Revenue Validation: Add a check to ensure that revenue is greater than 0. This prevents sending zero or negative revenue values.
  • Currency Validation: In the sample below, the currency is hard coded to "USD". Ensure that the currency is accurate and not nil or empty before sending data to Singular.
  • Logging Invalid Data: If the data fails validation, print a log message for debugging, and do not send the data to Singular.
import com.singular.sdk.Singular

// Method called when impression data is successfully received
fun onImpressionDataSuccess(impressionData: ISImpressionData?) {
    // Ensure impressionData is not null
    if (impressionData == null) {
        Log.d("IronSource", "No impression data available.")
        return
    }

    // Ensure revenue value is valid
    val revenue = impressionData.revenue.toDouble()
    if (revenue <= 0) {
        Log.w("IronSource", "Invalid revenue value: $revenue")
        return
    }

    // Create SingularAdData object with required fields
    val data = SingularAdData(
        adPlatform = "IronSource",
        currency = "USD",
        revenue = revenue
    )

    // Send the Ad Revenue data to Singular
    Singular.adRevenue(data)

    // Log the data for debugging
    Log.i("IronSource", "Ad Revenue reported to Singular: AdPlatform: ${data.adPlatform}, Currency: ${data.currency}, Revenue: ${data.revenue}")
}
TradPlus
Partner Notes
  • Set the impressionDelegate
  • Add Singular to the TradPlusAdImpression Callback
Select the code base for your SDK implementation:
KotlinJava
How it works:
  • TradPlus Integration: Load a rewarded ad from TradPlus and use the onImpressionSuccess function to handle Ad Revenue events.
  • Revenue Validation: Add a check to ensure that revenue is greater than 0. This prevents sending zero or negative revenue values. The adInfo dictionary contains an "ecpm" key with a valid NSNumber value. It converts this value to a Double and scales it (typically ecpm is given in milli-units, so dividing by 1000.0 converts it to dollars).
  • Currency Validation: In the sample below, the currency is hard coded to "USD". Ensure that the currency is accurate and not nil or empty before sending data to Singular.
  • Logging Invalid Data: If the data fails validation, print a log message for debugging, and do not send the data to Singular.
import com.singular.sdk.Singular
import com.singular.sdk.SingularAdData

// Set the global impression listener for TradPlus
TradPlusSdk.setGlobalImpressionListener(
    object : GlobalImpressionManager.GlobalImpressionListener {
        override fun onImpressionSuccess(tpAdInfo: TPAdInfo?) {
            // Ensure tpAdInfo is not null
            if (tpAdInfo == null) {
                println("AdInfo is null")
                return
            }

            // Calculate revenue (assuming ecpm is a valid field)
            val revenue = tpAdInfo.ecpm.toDouble() / 1000

            // Validate the revenue value
            if (revenue <= 0) {
                println("Ad Revenue value out of expected range: $revenue")
                return
            }

            // Create SingularAdData object with required fields
            val data = SingularAdData(
                adPlatform = "TradPlus",
                currency = "USD", // Assuming USD, adjust if necessary
                revenue = revenue
            )

            // Send the Ad Revenue data to Singular
            Singular.adRevenue(data)

            // Log for debugging
            println("Ad Revenue reported to Singular: AdPlatform: ${data.adPlatform}, Currency: ${data.currency}, Revenue: ${data.revenue}")
        }
    }
)
Other (Generic)
Partner Notes
  • Generic Integration: Initialize a SingularAdData object and pass the required data. Data should include the adPlatform as a String, currency as a String, and revenue as a Double.
  • Revenue Reporting: Check that your revenue and currency data are correct before sending it to Singular. Incorrect data can NOT be fixed later, so it’s crucial to ensure it’s accurate.
  • Tip: Log information for debugging purposes.
Select the code base for your SDK implementation:
KotlinJava
import com.singular.sdk.Singular
import com.singular.sdk.SingularAdData

// Function to send Ad Revenue data to Singular
fun reportAdRevenue(adPlatform: String, currency: String, revenue: Double) {
    // Validate the revenue value
    if (revenue <= 0) {
        println("Invalid revenue value: $revenue")
        return
    }
    
    // Create a SingularAdData object with the provided fields
    val data = SingularAdData(
        adPlatform = adPlatform,
        currency = currency,
        revenue = revenue
    )
    
    // Send the Ad Revenue data to Singular
    Singular.adRevenue(data)
    
    // Log the data for debugging
    println("Ad Revenue reported to Singular: AdPlatform: ${data.adPlatform}, Currency: ${data.currency}, Revenue: ${data.revenue}")
}