Apple Search Ads Event Tracking | Blue Frog Docs

Apple Search Ads Event Tracking

Complete guide to Apple Search Ads attribution and conversion tracking

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

  1. Implement both API and SKAdNetwork for maximum coverage
  2. Design conversion value schema before implementation
  3. Call attribution check early in app lifecycle
  4. Handle errors gracefully for missing attribution
  5. Use MMP if possible for simplified integration

Conversion Values

  1. Plan schema carefully - only 64 values available
  2. Prioritize high-value events in upper ranges
  3. Consider time-based values for retention tracking
  4. Test schema before launch
  5. Document value meanings for reporting clarity

Optimization

  1. Focus on downstream metrics not just installs
  2. Use Cost per Goal bidding for SKAN optimization
  3. Analyze by keyword for bid adjustments
  4. Monitor conversion value distribution
  5. Allow for SKAN delays in performance assessment

Privacy Compliance

  1. Respect ATT preferences - don't require opt-in
  2. Provide clear ATT prompt messaging
  3. Use SKAdNetwork as primary with API as supplement
  4. Follow Apple guidelines for data handling
  5. 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:

  1. Create test ad group
  2. Set low daily budget
  3. Target unique keywords
  4. Verify attribution flow
  5. Check conversion values in reports
// SYS.FOOTER