Ad Revenue Attribution
Track ad revenue from mediation platforms and attribute it to the marketing campaigns that acquired your users, providing complete ROI visibility across campaign costs, in-app purchases, and advertising monetization.
Overview
What Is Ad Revenue Attribution
Ad Revenue Attribution connects mobile app advertising revenue to the user acquisition campaigns that drove app installs, enabling you to measure true campaign profitability including ad monetization.
Key Benefits:
- Unified ROI View: See campaign costs, in-app revenue, and ad revenue in a single dashboard
- Campaign Optimization: Send ad revenue data back to ad networks to improve bidding and targeting
- LTV Measurement: Calculate complete user lifetime value including advertising monetization
Data Sources: Ad revenue data typically comes from your mediation platform (e.g., AdMob, AppLovin MAX, IronSource) at either the user level or impression level. Singular supports multiple integration methods to receive this data.
Learn More: See the Ad Revenue Attribution FAQ for comprehensive details on setup, reporting, and troubleshooting.
Implementation Requirements
Critical Guidelines
Data Accuracy Is Critical:
- Currency Codes: Use three-letter ISO 4217 currency codes (e.g., USD, EUR, INR). Many mediation platforms report in USD—verify your platform's currency before implementation
-
Validate Before Sending: Always validate revenue
and currency data before calling
Singular.adRevenue(). Incorrect data cannot be corrected after submission - Platform Differences: iOS receives revenue directly in standard currency units (e.g., $0.005), unlike Android which uses micros. Always check your platform's documentation for the correct format
Setup Steps
Follow these steps to implement ad revenue attribution:
- Update SDK: Ensure you're using the latest Singular SDK version
- Choose Integration: Select the mediation platform integration below that matches your setup
- Implement Callbacks: Add platform-specific paid event listeners to capture revenue data
- Validate Data: Test revenue reporting and verify data appears in Singular dashboard
Platform Integrations
AdMob Integration
Track ad revenue from Google AdMob using the paid event listener for impression-level revenue reporting.
Requirements:
- Enable paid event tracking in your AdMob account (see AdMob Support)
- Implement Google Mobile Ads SDK for iOS (see Getting Started Guide)
Platform Revenue Reporting: AdMob reports revenue differently by platform. iOS returns revenue in standard currency units (e.g., $0.005 = 0.005). Send the value directly to Singular without conversion.
Implementation
Set a paid event handler when loading ads to capture revenue data and send it to Singular.
import Singular
import GoogleMobileAds
private let adUnitID = "AD_UNIT_ID"
var rewardedAd: GADRewardedAd?
func loadRewardedAd() {
let request = GADRequest()
GADRewardedAd.load(withAdUnitID: adUnitID, request: request) { [weak self] ad, error in
guard let self = self else { return }
if let error = error {
print("Rewarded ad failed to load: \(error.localizedDescription)")
return
}
self.rewardedAd = ad
// Set paid event handler for revenue tracking
self.rewardedAd?.paidEventHandler = { adValue in
// Extract revenue and currency from AdValue
let revenue = adValue.value.doubleValue
let currency = adValue.currencyCode
// Validate revenue and currency before sending
guard revenue > 0, let currency = currency, !currency.isEmpty else {
print("Invalid ad revenue data: revenue = \(revenue), currency = \(String(describing: currency))")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "AdMob",
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) \(currency)")
}
}
}
#import <Singular/Singular.h>
#import <GoogleMobileAds/GoogleMobileAds.h>
static NSString *const adUnitID = @"AD_UNIT_ID";
@interface AdManager ()
@property(nonatomic, strong) GADRewardedAd *rewardedAd;
@end
@implementation AdManager
- (void)loadRewardedAd {
GADRequest *request = [[GADRequest alloc] init];
[GADRewardedAd loadWithAdUnitID:adUnitID
request:request
completionHandler:^(GADRewardedAd *ad, NSError *error) {
if (error) {
NSLog(@"Rewarded ad failed to load: %@", error.localizedDescription);
return;
}
self.rewardedAd = ad;
// Set paid event handler for revenue tracking
self.rewardedAd.paidEventHandler = ^(GADAdValue *adValue) {
// Extract revenue and currency from AdValue
double revenue = adValue.value.doubleValue;
NSString *currency = adValue.currencyCode;
// Validate revenue and currency before sending
if (revenue > 0 && currency.length > 0) {
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatfrom:@"AdMob"
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f %@", revenue, currency);
} else {
NSLog(@"Invalid ad revenue data: revenue = %f, currency = %@",
revenue, currency);
}
};
}];
}
@end
AppLovin MAX Integration
Share impression-level ad revenue using the AppLovin Impression-Level User Revenue API.
Requirements:
- Implement AppLovin MAX SDK (see Impression-Level Revenue API Guide)
-
Subscribe to the
max_revenue_eventstopic via AppLovin Communicator
Implementation
Handle revenue messages through the AppLovin Communicator callback.
import Singular
import AppLovinSDK
func didReceive(_ message: ALCMessage) {
// Check for revenue events topic
if message.topic == "max_revenue_events" {
// Extract and validate revenue value
guard let revenueValue = message.data["revenue"] as? Double,
revenueValue > 0 else {
print("Failed to parse valid revenue value or revenue is not greater than 0")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "AppLovin",
currency: "USD", // AppLovin typically reports in USD
revenue: revenueValue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenueValue) USD")
}
}
#import <Singular/Singular.h>
#import <AppLovinSDK/AppLovinSDK.h>
- (void)didReceive:(ALCMessage *)message {
// Check for revenue events topic
if ([@"max_revenue_events" isEqualToString:message.topic]) {
NSDictionary *data = message.data;
// Extract and validate revenue value
NSNumber *revenueNumber = data[@"revenue"];
double revenueValue = [revenueNumber doubleValue];
if (revenueValue > 0) {
// Create ad revenue data object
SingularAdData *adData = [[SingularAdData alloc]
initWithAdPlatfrom:@"AppLovin"
currency:@"USD"
revenue:revenueValue];
// Send to Singular
[Singular adRevenue:adData];
NSLog(@"Ad revenue sent: %f USD", revenueValue);
} else {
NSLog(@"Failed to parse valid revenue value or revenue is not greater than 0");
}
}
}
Unity LevelPlay (IronSource) Integration
Track impression-level revenue from ironSource and mediated networks using the IronSource SDK.
Requirements:
- Implement ironSource SDK (see Getting Started Guide)
- Enable ARM SDK Postbacks flag in your IronSource dashboard
- Set impression data listener to receive revenue callbacks
Learn More: See IronSource Ad Revenue Documentation for complete setup details.
Implementation
Implement the impression data success callback to capture and send revenue.
import Singular
import IronSource
func impressionDataDidSucceed(_ impressionData: ISImpressionData?) {
// Validate impression data
guard let impressionData = impressionData else {
print("No impression data available")
return
}
// Extract and validate revenue
let revenue = impressionData.revenue
guard revenue > 0 else {
print("Invalid revenue value: \(revenue)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "IronSource",
currency: "USD", // IronSource typically reports in USD
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) USD")
}
#import <Singular/Singular.h>
#import <IronSource/IronSource.h>
- (void)impressionDataDidSucceed:(ISImpressionData *)impressionData {
// Validate impression data
if (!impressionData) {
NSLog(@"No impression data available");
return;
}
// Extract and validate revenue
double revenue = impressionData.revenue;
if (revenue <= 0) {
NSLog(@"Invalid revenue value: %f", revenue);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatfrom:@"IronSource"
currency:@"USD"
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f USD", revenue);
}
TradPlus Integration
Capture ad revenue from TradPlus mediation using the impression delegate.
Requirements:
-
Set impression delegate via
TradPlus.sharedInstance().impressionDelegate -
Handle
tradPlusAdImpressioncallback to receive revenue data - Convert eCPM from milli-units to standard currency (divide by 1000)
Implementation
Register an impression delegate to track all ad impressions and revenue.
import Singular
import TradPlusSDK
// Set up the delegate
TradPlus.sharedInstance().impressionDelegate = self
// Delegate method for handling ad impressions
func tradPlusAdImpression(_ adInfo: [String: Any]) {
let currency = "USD"
// Extract and validate eCPM value
guard let ecpmValue = adInfo["ecpm"] as? NSNumber else {
print("No eCPM data available in adInfo")
return
}
// Convert eCPM from milli-units to dollars
let revenue = ecpmValue.doubleValue / 1000.0
// Validate revenue
guard revenue > 0 else {
print("Ad Revenue value out of expected range: \(revenue)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "TradPlus",
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) \(currency)")
}
#import <Singular/Singular.h>
#import <TradPlusSDK/TradPlusSDK.h>
// Set up the delegate
TradPlus.sharedInstance.impressionDelegate = self;
// Delegate method for handling ad impressions
- (void)tradPlusAdImpression:(NSDictionary<NSString *, id> *)adInfo {
NSString *currency = @"USD";
// Extract and validate eCPM value
NSNumber *ecpmValue = adInfo[@"ecpm"];
if (!ecpmValue) {
NSLog(@"No eCPM data available in adInfo");
return;
}
// Convert eCPM from milli-units to dollars
double revenue = [ecpmValue doubleValue] / 1000.0;
// Validate revenue
if (revenue <= 0) {
NSLog(@"Ad Revenue value out of expected range: %f", revenue);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatfrom:@"TradPlus"
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f %@", revenue, currency);
}
Generic Integration (Other Platforms)
Integrate any mediation platform using the generic
SingularAdData interface.
Requirements:
- Access to impression-level revenue data from your mediation platform
- Revenue amount in standard currency units
- ISO 4217 currency code (e.g., USD, EUR, INR)
Data Accuracy: Validate revenue and currency data before sending to Singular. Incorrect data cannot be corrected after submission.
Implementation
Create a SingularAdData object with platform name, currency,
and revenue, then call Singular.adRevenue().
import Singular
func reportAdRevenue(adPlatform: String, currency: String, revenue: Double) {
// Validate revenue
guard revenue > 0 else {
print("Invalid revenue value: \(revenue)")
return
}
// Validate currency
guard !currency.isEmpty else {
print("Invalid currency: \(currency)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: adPlatform,
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Revenue sent: \(revenue) \(currency) from \(adPlatform)")
}
// Example usage
reportAdRevenue(adPlatform: "MyMediationPlatform", currency: "USD", revenue: 0.05)
#import <Singular/Singular.h>
- (void)reportAdRevenueWithPlatform:(NSString *)adPlatform
currency:(NSString *)currency
revenue:(double)revenue {
// Validate revenue
if (revenue <= 0) {
NSLog(@"Invalid revenue value: %f", revenue);
return;
}
// Validate currency
if (!currency || currency.length == 0) {
NSLog(@"Invalid currency: %@", currency);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatfrom:adPlatform
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Revenue sent: %f %@ from %@", revenue, currency, adPlatform);
}
// Example usage
[self reportAdRevenueWithPlatform:@"MyMediationPlatform"
currency:@"USD"
revenue:0.05];
Testing and Validation
Verify Revenue Reporting
Test your ad revenue implementation to ensure data flows correctly to Singular.
- Check Logs: Verify revenue callback logs appear with correct values and currency
- Test Ads: Load and display test ads to trigger revenue events
- Dashboard Verification: Confirm revenue appears in Singular dashboard within 24 hours
- Data Accuracy: Validate revenue amounts match your mediation platform reports
Troubleshooting: If revenue doesn't appear in Singular, check that:
- Ad revenue attribution is enabled in your Singular account
- Revenue values are greater than 0
- Currency codes are valid ISO 4217 codes
- Platform name matches Singular's expected format