Apple Search Ads integration plan
This article describes how to connect Apple Search Ads data in the AE backend. The AE backend currently supports the Apple Search Ads Reporting API.
If you are using a version earlier than AE 4.2, see Apple Search Ads data integration solution for data integration
Note that data generated by third-party data integration counts toward the cluster's data consumption
Summary
Interface overview
| Interface | Type | Granularity | Attribution | Cost | Revenue | Impressions | Clicks | Conversions |
|---|---|---|---|---|---|---|---|---|
| Reporting API | API | Aggregated data | ✅ | ✅ | ✅ | ✅ |
The Apple Search Ads Reporting API provides data reports at multiple levels, from campaigns (Campaign-Level) to ads (Ad-Level). The AE system currently supports pulling data at the following levels:
- Campaign-level reports (Campaign-Level Reports)
- Ad Group-level reports (Ad Group-Level Reports)
- Keyword-level reports (Keyword-Level Reports)
Note that due to feature changes in Apple Search Ads, Creative Set-level reports (Creative Set-Level Reports) are no longer supported, and historical configurations may stop working
Integration process
The process for connecting Apple Search Ads data is as follows:
- Complete the authorization for the Apple Search Ads API
- Log in to the AE backend, go to the Third-party Integration module, add an Apple Search Ads integration plan, and complete the related configuration
- Check whether the AE system receives the data successfully, and build reports
1. Complete the authorization for the Apple Search Ads API
Before connecting ASA data, you need to complete the ASA authorization. The process consists of the following steps:
- Create a user with API access
- Generate a private key and a public key, and upload the public key to the ASA dashboard
- Create a client secret (Client Secret)
- Request an access token (Access Token)
You can complete the authorization by following the Apple Search Ads official documentation directly, or by following the process in this section. All of the following content comes from that official documentation.
1.1 Create a user with API access
First, log in with an admin account and create a user with API permissions as follows:
- Go to Apple Search Ads UI and log in with the admin account
- Go to Account Settings > User Management
- Click Invite Users to invite a user in your ASA organization
- In the User Details section, enter the user's name and Apple ID
- In the User Access and Role section, select a user role with API access
- Click Send Invite to send the invitation email. The invited user receives an email with a secure code. The user clicks the Apple URL in the email and enters the secure code to activate the account
1.2 Generate a private key and a public key, and upload the public key to the ASA dashboard
Next, you need to generate a private key and a public key with code. This section requires some technical background. If you are not a developer, contact a developer or ThinkingAI staff to complete this step
If you use Windows, download and install OpenSSL
- Enter the following command on the command line to generate a private key file named private-key.pem
openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem
- Then, in the same directory, run the following command to generate a public key file named public-key.pem
openssl ec -in private-key.pem -pubout -out public-key.pem
- Go to Apple Search Ads UI and log in as the user with API permissions that you created in the previous step. Select Account Settings > API and copy the public key to the Public Key section. After you click Save, you can see
clientId,teamId, andkeyIdabove the Public Key section. The following is a data sample:
clientId SEARCHADS.aeb3ef5f-0c5a-4f2a-99c8-fca83f25a9
teamId SEARCHADS.hgw3ef3p-0w7a-8a2n-77c8-scv83f25a7
keyId a273d0d3-4d9e-458c-a173-0db8619ca7d7
1.3 Create a client secret
Next, you also need to generate a client secret with code. This section requires some technical background. If you are not a developer, contact a developer or ThinkingAI staff to complete this step.
The Apple Search Ads client secret is a JSON web token (JWT). Apple provides a Python 3 code sample. After installing the dependency libraries, run the following code to create the client secret:
import os
import datetime as dt
from authlib.jose import jwt
from Crypto.PublicKey import ECC
# We recommend that you place the private key and public key files in the same directory as the py file
private_key_file = "private-key.pem"
public_key_file = "public-key.pem"
# Replace these with the clientId, teamId, and keyId obtained in the previous step
client_id = "SEARCHADS.9703f56c-10ce-4876-8f59-e78e5e23a152"
team_id = "SEARCHADS.9703f56c-10ce-4876-8f59-e78e5e23a152"
key_id = "d136aa66-0c3b-4bd4-9892-c20e8db024ab"
audience = "https://appleid.apple.com"
alg = "ES256"
# If the private key file does not exist, create it
if os.path.isfile(private_key_file):
with open(private_key_file, "rt") as file:
private_key = ECC.import_key(file.read())
else:
private_key = ECC.generate(curve='P-256')
with open(private_key_file, 'wt') as file:
file.write(private_key.export_key(format='PEM'))
# If the public key file does not exist, create it
public_key = private_key.public_key()
if not os.path.isfile(public_key_file):
with open(public_key_file, 'wt') as file:
file.write(public_key.export_key(format='PEM'))
# Get the current timestamp
issued_at_timestamp = int(dt.datetime.utcnow().timestamp())
# Define the expiration time. It cannot exceed 180 days; otherwise, the Client Secret becomes invalid
expiration_timestamp = issued_at_timestamp + 86400*180
# Define the JWT headers.
headers = dict()
headers['alg'] = alg
headers['kid'] = key_id
# Define the JWT payload.
payload = dict()
payload['sub'] = client_id
payload['aud'] = audience
payload['iat'] = issued_at_timestamp
payload['exp'] = expiration_timestamp
payload['iss'] = team_id
# Read the private key content
with open(private_key_file, 'rt') as file:
private_key = ECC.import_key(file.read())
# Encode the JWT and sign it with the private key
client_secret = jwt.encode(
header=headers,
payload=payload,
key=private_key.export_key(format='PEM')
).decode('UTF-8')
# Write the Client secret to a file
with open('client_secret.txt', 'w') as output:
output.write(client_secret)
In the above code, note the following:
- Place the private key and public key files generated in the previous step in the same directory as the .py file, or modify the paths of the private key and public key files in the code
- Replace the corresponding values in the code with the clientId, teamId, and keyId obtained in the previous step
Then run the code. The client secret is recorded in the client_secret.txt file in that directory. Record this information. The following is a sample client secret:
eyJraWQiOiJiYWNhZWJkYS1lMjE5LTQxZWUtYTkwNy1lMmMyNWIyNGQxYjIiLCJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJEcmVhbWNvbXBhbnkiLCJhdWQiOiJBdXRoZW50aWNhdG9yIiwiZXhwIjoxNTcxNjcwNjIxLCJuYmYiOjE1NzE2NjcwMjEsInN1YiI6Im11c3RlciIsImNsaWVudF9pZCI6ImFiY2QxMjM0IiwiYWRtaW4iOiJ0cnVlIn0.s4C3p9kVNFeRAB5tChatC3ldQX07v9mG7thL7FeEO6cClfNuiaLSgq8f8ymbfO3OQYW_KuwaA1KYRuoy1JmKk 4DBbYLcz6aoABe0pzI5Z_6wgMzAyqz8pQtwDAcd4Idoi8JdRbtzZce9o-0nZiFA4hVAXqYwpEYC4UU8ZmJO_z8tY4juHPTV3nDugdtqyNnmAiBoLryOfGNngQZccdY1_QvkXS1y0bg1a0k8cVVtnq- _93fYJIt9Z64CTvlH3uOeh7uaEv3nIxpXhvhkTySpUmY8e04TO09oTyZijiloByv3KFQ92OOJ8L 5N5_CeEc5p9LWjT1pcX8ATamOycZz2Q
1.4 Get the Org ID
Finally, you need to get the Org ID, which is the Campaign Group ID in the ASA dashboard. You can find it in the ASA dashboard. It is the ID circled in red in the image below:
1.5 What you need
Finally, confirm that you have obtained and recorded the following during the authorization process above:
- client_id
- org_id
- client_secret
2. Plan configuration
After you complete the ASA authorization, log in to the AE system and configure the new plan in the Third-party Integration module. The image below shows the ASA configuration page. Follow this section to create the plan:
2.1 Authorization information configuration
Click the Configure authorization information button under Authorization Information, and enter the information you obtained during authorization in the pop-up
Org ID is the Campaign Group ID in the Apple Search Ads dashboard (see 1.4 for how to get it)
2.2 Sync Schedule
In the Sync Schedule module, you can set the policy for the AE system to pull ASA data on a schedule. You can choose to pull data for a period of time at a specific time every day or every hour. Because pulled data also counts toward the data volume, avoid pulling data for overly long periods on a schedule. The Reporting API can pull data from a time range of up to the past 1000 days, with up to 31 days per pull. Below Sync Schedule, there is also a TimeZone option, which defaults to ORTZ.
2.3 Receive Settings
You can control whether ASA data is written as events. Because ASA data is written only to the event table, do not turn off this setting.
2.4 Configuration
Finally, in the Configuration module, you can control the detailed settings of data pulling, including the time aggregation granularity of the data, the metric fields and dimensions to pull, and the event name after ingestion.
The content of the configuration is a JSON, which you can customize as follows:
| Module | Name | Description |
|---|---|---|
| sink_event | event_name | Event name after ingestion; customizable |
| event_mapping | Event name after ingestion for each level. The key is a level name in report_types, and the value is the event name after ingestion. For usage, see 2.4.4. The default configuration for a new plan uses event_mapping | |
source | time_granularity | Time aggregation granularity of the data, that is, whether the pulled data is aggregated by day or by hour Valid values: day, hour |
report_types | Level of data to pull; list type. We recommend that you enter only one element, that is, pull data of only one level at a time Valid values: campaign, ad_group, keyword | |
| metrics | Metrics in the data; list type. Different levels support different metrics, so fill this in carefully | |
| group_by | Grouping dimensions in the data; list type. Different levels support different group_by values, so fill this in carefully | |
| transfer | double_columns | Numeric field definitions; list type. Fields listed here are ingested as numeric values. The default configuration for a new plan already lists metric fields such as impressions, taps, and installs |
Because the data configuration differs greatly across levels, we recommend that you use the configuration template for each level directly or fine-tune the template.
2.4.1 Campaign-level template
The template for Campaign-level reports is as follows:
{
"sink_event": {
"event_name": "asa_campaign_level_data"
},
"source": {
"time_granularity": "hour",
"group_by": [
"countryOrRegion"
],
"report_types": [
"campaign"
]
}
}
- The time granularity used in the template is hour. The system uses the date field in the data, that is, the date + hour of the data, as the time of the data
- The event name used in the template is -- asa_campaign_level_data
- The following are the ingested fields of Campaign-level reports
------------------------Dimension fields------------------------
campaignid
campaignname
deleted
campaignstatus
app_adamid
app_appname
servingstatus
servingstatereasons
countriesorregions
modificationtime
totalbudget_amount
totalbudget_currency
dailybudget_amount
dailybudget_currency
displaystatus
supplysources
adchanneltype
orgid
countryorregionservingstatereasons
countryorregion
billingevent
------------------------Metric fields------------------------
impressions
taps
installs
newdownloads
redownloads
latoninstalls
latoffinstalls
ttr
avgcpa_amount
avgcpa_currency
avgcpt_amount
avgcpt_currency
avgcpm_amount
avgcpm_currency
localspend_amount
localspend_currency
conversionrate
2.4.2 Ad Group-level template
- The template for Ad Group-level reports is as follows:
{
"sink_event": {
"event_name": "asa_adgroup_level_data"
},
"source": {
"time_granularity": "hour",
"group_by": [
"countryOrRegion"
],
"report_types": [
"ad_group"
]
}
}
- The time granularity used in the template is hour. The system uses the date field in the data, that is, the date + hour of the data, as the time of the data
- The event name used in the template is -- asa_adgroup_level_data
- The following are the ingested fields of Ad Group-level reports
------------------------Dimension fields------------------------
campaignid
adgroupid
adgroupname
adgroupdisplaystatus
adgroupstatus
adgroupservingstatus
adgroupservingstatereasons
deleted
cpagoal
orgid
modificationtime
pricingmodel
defaultbidamount_amount
defaultbidamount_currency
countryorregion
------------------------Metric fields------------------------
impressions
taps
installs
newdownloads
redownloads
latoninstalls
latoffinstalls
ttr
avgcpa_amount
avgcpa_currency
avgcpt_amount
avgcpt_currency
avgcpm_amount
avgcpm_currency
localspend_amount
localspend_currency
conversionrate
2.4.3 Keyword-level template
- The template for Keyword-level reports is as follows:
{
"sink_event": {
"event_name": "asa_keyword_level_data"
},
"source": {
"time_granularity": "hour",
"group_by": [
"countryOrRegion"
],
"report_types": [
"keyword"
]
}
}
- The time granularity used in the template is hour. The system uses the date field in the data, that is, the date + hour of the data, as the time of the data
- The event name used in the template is -- asa_keyword_level_data
- The following are the ingested fields of Keyword-level reports
------------------------Dimension fields------------------------
keywordid
keyword
keywordstatus
matchtype
bidamount_amount
bidamount_currency
deleted
keyworddisplaystatus
adgroupid
adgroupname
adgroupdeleted
modificationtime
countryorregion
------------------------Metric fields------------------------
impressions
taps
installs
newdownloads
redownloads
latoninstalls
latoffinstalls
ttr
avgcpa_amount
avgcpa_currency
avgcpt_amount
avgcpt_currency
avgcpm_amount
avgcpm_currency
localspend_amount
localspend_currency
conversionrate
2.4.4 Multi-level template
To pull data at multiple levels at once, we recommend the following template. The main changes are:
- Write the names of the levels to pull in report_types
- Use event_mapping to manage the event names of multiple levels. The key of each element in event_mapping corresponds to a level name in report_types, and the value is the event name after ingestion
- Data at each level is pulled with the same configuration, that is, the same time_granularity and group_by
{
"sink_event": {
"event_mapping":{
"campaign": "asa_campaign_level_data",
"ad_group": "asa_adgroup_level_data",
"keyword": "asa_keyword_level_data"
}
},
"source": {
"time_granularity": "hour",
"group_by": [
"countryOrRegion"
],
"report_types": [
"campaign",
"ad_group",
"keyword"
]
}
}
2.5 Standardized fields
The following are the standardized fields of Apple Search Ads:
| Original field | Standardized field | Description |
|---|---|---|
| orgid | te_ads_object.ad_account_id | Ad account ID |
| campaignname | te_ads_object.campaign_name | Campaign name |
| campaignid | te_ads_object.campaign_id | Campaign ID |
| adgroupname | te_ads_object.ad_group_name | Ad group name, or the Unit name for monetization ads |
| adgroupid | te_ads_object.ad_group_id | Ad group ID, or the Unit ID for monetization ads |
| adname | te_ads_object.ad_name | Ad name |
| adid | te_ads_object.ad_id | Ad ID |
| app_adamid | te_ads_object.app_id | App ID |
| app_appname | te_ads_object.app_name | App name |
| Fixed value iOS | te_ads_object.platform | Platform, such as Android or iOS |
| countryorregion | te_ads_object.country | Country or region code |
| localspend_currency | te_ads_object.currency | Currency of the cost or revenue |
| impressions | te_ads_object.impressions | Impressions |
| taps | te_ads_object.clicks | Clicks |
| installs | te_ads_object.installs | Conversions (installs) |
| localspend_amount | te_ads_object.cost | User acquisition cost |
3. Next steps
3.1 Check data ingestion
You can check on the Management page whether the callback events have been stored:
- asa_campaign_level_data: Campaign-level data
- asa_adgroup_level_data: Ad Group-level data
- asa_keyword_level_data: Keyword-level data
3.2 Pull data once
If you want to connect data from an earlier period, such as historical data or data that failed to be pulled, open the plan page again after saving the plan and click the Single pull task button in the upper right corner to pull data for a specific time range once. Note that if the period of the pulled data overlaps with earlier pulls, for example, you pull data for 2023-08-01 and data for that day was already pulled before, the most recently pulled data overwrites the earlier data:

