Apple Search Ads Event Tracking
Overview
Apple Search Ads provides attribution and conversion tracking through the Apple Search Ads Attribution API and SKAdNetwork integration. These tools enable advertisers to measure campaign performance while respecting iOS privacy guidelines. Understanding both systems is essential for accurate measurement and optimization.
Attribution Methods
Apple Search Ads offers two primary attribution methods:
Apple Search Ads Attribution API
Direct attribution for users who consent to tracking (via ATT):
- Deterministic user-level attribution
- Available when users opt-in to tracking
- Provides full conversion data
- Decreasing coverage due to ATT opt-out rates
SKAdNetwork (SKAN)
Privacy-preserving attribution for all users:
- Aggregated, delayed conversion data
- Works for all users regardless of ATT consent
- Conversion values (0-63) for post-install events
- Limited to single postback (SKAN 3) or multiple (SKAN 4+)
Attribution API Implementation
iOS SDK Integration
Add the AdServices framework:
import AdServices
// Check for attribution token
if #available(iOS 14.3, *) {
do {
let token = try AAAttribution.attributionToken()
// Send token to your server for attribution
sendAttributionToken(token)
} catch {
print("Failed to get attribution token: \(error)")
}
}
Server-Side Attribution
Send the token to Apple's attribution endpoint:
import requests
def get_apple_search_ads_attribution(token):
url = "https://api-adservices.apple.com/api/v1/"
headers = {
"Content-Type": "text/plain"
}
response = requests.post(url, headers=headers, data=token)
if response.status_code == 200:
attribution_data = response.json()
return attribution_data
else:
return None
# Attribution response includes:
# - attribution: true/false
# - orgId: Organization ID
# - campaignId: Campaign ID
# - adGroupId: Ad Group ID
# - keywordId: Keyword ID (if applicable)
# - clickDate: When user clicked the ad
# - conversionType: Download or Redownload
Attribution Response
{
"attribution": true,
"orgId": 123456789,
"campaignId": 987654321,
"adGroupId": 111222333,
"keywordId": 444555666,
"creativeSetId": 777888999,
"clickDate": "2025-01-15T10:30:00Z",
"conversionType": "Download"
}
SKAdNetwork Integration
Configure SKAdNetwork
Add Apple's SKAdNetwork ID to Info.plist:
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>YDX93A7ASS.skadnetwork</string>
</dict>
</array>
Register App for Attribution
import StoreKit
// Call after app install or launch
if #available(iOS 14.0, *) {
SKAdNetwork.registerAppForAdNetworkAttribution()
}
Update Conversion Values
Track post-install events with conversion values (0-63):
import StoreKit
// Update conversion value as user progresses
if #available(iOS 15.4, *) {
// SKAN 4.0 - Coarse values
SKAdNetwork.updatePostbackConversionValue(42, coarseValue: .high) { error in
if let error = error {
print("Failed to update conversion value: \(error)")
}
}
} else if #available(iOS 14.0, *) {
// SKAN 3.0 - Fine values only
SKAdNetwork.updateConversionValue(42)
}
Conversion Value Strategy
Design a conversion value schema to capture meaningful post-install behavior:
| Value Range | Event Type | Example |
|---|---|---|
| 0-10 | Registration/Onboarding | Account created, profile completed |
| 11-30 | Engagement | Sessions, features used |
| 31-50 | Retention | Day 1-7 retention |
| 51-63 | Revenue/Conversion | Purchase, subscription |
Example schema:
enum ConversionValue: Int {
case install = 0
case registration = 10
case tutorialComplete = 20
case day1Retention = 30
case day3Retention = 40
case day7Retention = 50
case firstPurchase = 55
case subscription = 60
case highValueSubscription = 63
}
func updateConversionForEvent(_ event: ConversionValue) {
if #available(iOS 14.0, *) {
SKAdNetwork.updateConversionValue(event.rawValue)
}
}
Standard Events
Track key post-install events for optimization:
App Open/Session
// Track app launch
func applicationDidBecomeActive() {
trackEvent("app_open")
}
Registration
func userDidRegister() {
trackEvent("registration")
updateConversionValue(.registration)
}
Purchase
func userDidPurchase(amount: Double, currency: String, productId: String) {
trackEvent("purchase", parameters: [
"value": amount,
"currency": currency,
"product_id": productId
])
updateConversionValue(.firstPurchase)
}
Subscription
func userDidSubscribe(plan: String, value: Double) {
trackEvent("subscription", parameters: [
"plan": plan,
"value": value
])
updateConversionValue(.subscription)
}
Implementation with MMP
Using Mobile Measurement Partners
Most apps use MMPs (Adjust, AppsFlyer, Branch, Singular, Kochava) for attribution:
// Example with Adjust
import Adjust
// Configure Adjust
let config = ADJConfig(appToken: "YOUR_APP_TOKEN", environment: ADJEnvironmentProduction)
Adjust.appDidLaunch(config)
// Track events
let event = ADJEvent(eventToken: "purchase_token")
event?.setRevenue(9.99, currency: "USD")
Adjust.trackEvent(event)
MMPs handle:
- Attribution API token collection
- Server-side attribution calls
- SKAdNetwork configuration
- Conversion value management
- Reporting consolidation
Direct API Integration
For custom implementations, handle attribution directly:
class AttributionManager {
func checkAttribution() {
// 1. Get attribution token
guard let token = getAttributionToken() else { return }
// 2. Send to your server
sendToServer(token) { attributionData in
// 3. Process attribution
if attributionData.attribution {
self.recordAttributedInstall(attributionData)
}
}
}
private func getAttributionToken() -> String? {
if #available(iOS 14.3, *) {
return try? AAAttribution.attributionToken()
}
return nil
}
}
Reporting & Analytics
Apple Search Ads Dashboard
Access performance data in Search Ads:
- Campaign performance: Impressions, taps, installs, spend
- Keyword performance: Per-keyword metrics
- Demographics: Age, gender breakdowns
- Device: iPhone vs iPad performance
- Location: Geographic performance
API Reporting
Pull data programmatically:
import requests
def get_campaign_report(org_id, campaign_id, start_date, end_date):
url = f"https://api.searchads.apple.com/api/v4/reports/campaigns"
headers = {
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"X-AP-Context": f"orgId={org_id}"
}
payload = {
"startTime": start_date,
"endTime": end_date,
"selector": {
"orderBy": [{"field": "impressions", "sortOrder": "DESCENDING"}],
"conditions": [{"field": "campaignId", "operator": "EQUALS", "values": [campaign_id]}],
"pagination": {"offset": 0, "limit": 100}
},
"groupBy": ["countryCode"],
"returnRowTotals": True,
"returnGrandTotals": True
}
response = requests.post(url, headers=headers, json=payload)
return response.json()
Custom Reports
Create reports combining Search Ads data with:
- In-app analytics (retention, engagement)
- Revenue data (LTV, ARPU)
- SKAdNetwork postbacks
- Attribution API data
Best Practices
Attribution
- Implement both API and SKAdNetwork for maximum coverage
- Design conversion value schema before implementation
- Call attribution check early in app lifecycle
- Handle errors gracefully for missing attribution
- Use MMP if possible for simplified integration
Conversion Values
- Plan schema carefully - only 64 values available
- Prioritize high-value events in upper ranges
- Consider time-based values for retention tracking
- Test schema before launch
- Document value meanings for reporting clarity
Optimization
- Focus on downstream metrics not just installs
- Use Cost per Goal bidding for SKAN optimization
- Analyze by keyword for bid adjustments
- Monitor conversion value distribution
- Allow for SKAN delays in performance assessment
Privacy Compliance
- Respect ATT preferences - don't require opt-in
- Provide clear ATT prompt messaging
- Use SKAdNetwork as primary with API as supplement
- Follow Apple guidelines for data handling
- Keep privacy policy updated
Debugging
Check Attribution Token
func debugAttributionToken() {
if #available(iOS 14.3, *) {
do {
let token = try AAAttribution.attributionToken()
print("Attribution token: \(token)")
} catch {
print("Attribution error: \(error)")
}
}
}
Verify SKAdNetwork
func debugSKAdNetwork() {
if #available(iOS 14.0, *) {
// Log current conversion value (not directly available)
print("SKAdNetwork configured, check postbacks in dashboard")
}
}
Test Campaigns
Use test campaigns in Apple Search Ads:
- Create test ad group
- Set low daily budget
- Target unique keywords
- Verify attribution flow
- Check conversion values in reports