Practical Apple Ads workflows for safe setup, read-only checks, reporting, and guarded mutations
Use these playbooks when you are operating Apple Ads from asc in a terminal,
CI job, or agent run. The goal is to start read-only, confirm the ad-account context,
and only mutate resources after the target IDs and payload files are reviewed.
The primary examples use the direct Platform API v1 resource commands under
asc ads with --ad-account. The deprecated Campaign Management API v5 tree
is isolated in the migration and legacy sections below.
Apple Ads credentials are separate from App Store Connect API credentials.
asc auth login does not configure asc ads.
Create a named Apple Ads profile and store the default ad account when you know it:
asc ads auth login \
--name "Marketing" \
--client-id "SEARCHADS_CLIENT_ID" \
--team-id "SEARCHADS_TEAM_ID" \
--key-id "KEY_ID" \
--private-key "$HOME/.asc/apple-ads-private-key.pem" \
--ad-account "987654" \
--networkFor CI, prefer secrets and environment variables over checked-in config:
export ASC_ADS_CLIENT_ID="SEARCHADS_CLIENT_ID"
export ASC_ADS_TEAM_ID="SEARCHADS_TEAM_ID"
export ASC_ADS_KEY_ID="KEY_ID"
export ASC_ADS_PRIVATE_KEY_PATH="$HOME/.asc/apple-ads-private-key.pem"
export ASC_ADS_AD_ACCOUNT_ID="987654"
export ASC_ADS_BYPASS_KEYCHAIN=1
asc ads apps search --ad-account "$ASC_ADS_AD_ACCOUNT_ID" --query "Example" --output jsonUse a read-only API command for CI validation so the run resolves the exported
ASC_ADS_* credentials and org context instead of only listing stored profiles.
Use ASC_ADS_PRIVATE_KEY_B64 when your CI secret store handles single-line
values more reliably than PEM blocks. Keep ASC_ADS_ACCESS_TOKEN for cases
where another trusted system already minted a short-lived token.
For new automation, start with the Apple Ads Platform API v1 command tree:
asc ads me view --output json
asc ads acls list --output json
asc ads apps search --ad-account "987654" --query "Example" --output jsonThe legacy Campaign Management API v5 commands under asc ads v5 still run in
4.4.0 and print a deprecation warning on stderr. Apple retires v5 on January
26, 2027. A v5 command keeps its existing --org context; a v1 command uses
the separate --ad-account context. Do not pass an organization ID where a v1
ad account ID is required, or assume that the two IDs identify the same thing.
V1 query, report, and mutation bodies use the Platform API schemas. Put the
appropriate v1 object in --file; the CLI does not convert a v5 selector,
reporting request, numeric ID, or response envelope. V1 report pagination stays
in the request JSON, so do not add the legacy --paginate flag to a v1 report
command:
asc ads reports apps campaigns \
--ad-account "987654" \
--file report.json \
--output jsonPlatform v1 exposes one negative-keywords resource for both campaign and ad
group scope. Move the scope into the v1 query or mutation body rather than
looking for separate campaign-negative-keywords and ad-group-negative-keywords
commands. The v5 asc ads v5 reports preset helper remains a warning-producing
compatibility path; replace it with an explicit v1 report leaf and reviewed
request file.
Seven v5 leaves have no one-command v1 replacement in 4.4.0: product-page
countries, product-page devices, targeting-keyword bulk delete, campaign
negative-keyword bulk delete, ad-group negative-keyword bulk delete, and
impression-share report list and view. Related v1 commands are not drop-in
replacements for those contracts. In particular, geo search is not the v5
country/device mapping, insights impression-share is not the custom report
list/view API, and v1 has no bulk-delete operation.
For raw calls, keep asc ads v5 api request for v5 paths and its --org context.
It remains runnable with a warning and is not rewritten. Use
asc ads api request --path v1/... --ad-account ... for a raw v1 call.
Start outside an ad-account context. These commands do not require
--ad-account:
asc ads acls list --output jsonUse the ACL response to confirm the ad account ID, then pin it explicitly for the rest of the session:
export ASC_ADS_AD_ACCOUNT_ID="987654"
asc ads apps search --ad-account "$ASC_ADS_AD_ACCOUNT_ID" --query "Example" --limit 1 --output jsonWhen operating multiple Apple Ads accounts, avoid relying on the ambient default. Pass both profile and ad account:
asc ads apps search \
--ads-profile "Marketing" \
--ad-account "987654" \
--query "Example" \
--output jsonUse this sequence before an automation run or after rotating credentials:
ASC_BYPASS_KEYCHAIN=1 asc ads acls list --output json
ASC_BYPASS_KEYCHAIN=1 asc ads apps search --ad-account "987654" --query "Example" --limit 1 --output jsonExpected result: every command exits successfully, and the app search returns
either a result envelope or an empty result. Treat auth failures, missing account
errors, or unexpected account names as stop conditions.
When using stored Ads profiles, add profile validation before the API smoke test:
asc ads auth status --validate --output jsonQuery campaigns first, then inspect ad groups under one confirmed campaign ID:
asc ads campaigns find \
--ad-account "987654" \
--file campaign-query.json \
--output json
asc ads ad-groups find \
--ad-account "987654" \
--file ad-group-query.json \
--output jsonFor a full v1 query, keep pagination in the request body:
asc ads campaigns find --ad-account "987654" --file campaign-query.json --output json
asc ads ad-groups find --ad-account "987654" --file ad-group-query.json --output jsonDo not use report payloads as inventory replacements. Reports answer performance questions; list and find endpoints answer "what exists right now?" questions.
Use Platform API v1 for new report automation. Its body shape differs from v5:
{
"pagination": {"offset": 0, "pageSize": 20},
"filters": [
{"field": "campaignId", "operator": "EQUALS", "value": ["444555666"]}
],
"groupBy": ["countryOrRegion"],
"timeRange": {
"start": "2025-01-01",
"end": "2025-01-31",
"timeZone": "ORTZ",
"granularity": "DAILY"
}
}asc ads reports apps campaigns \
--ad-account "987654" \
--file platform-report.json \
--output jsonThe v1 report response keeps Apple's raw result and pagination fields.
Change offset and pageSize in the file to fetch another page. Run
asc ads reports apps campaigns --help for body and option guidance; report
entities do not all accept the same groupBy values.
The request below is the deprecated v5 shape. Keep it only while existing automation migrates:
{
"startTime": "2026-05-01",
"endTime": "2026-05-31",
"returnRowTotals": true,
"returnGrandTotals": true,
"selector": {
"pagination": {
"offset": 0,
"limit": 100
},
"orderBy": [
{
"field": "impressions",
"sortOrder": "DESCENDING"
}
]
}
}Run the campaign report with an explicit org and output format:
asc ads v5 reports campaigns \
--org "123456" \
--file reporting-request.json \
--output jsonFor ad-group, keyword, search-term, or ad-level reports, first verify the parent
campaign ID with asc ads v5 campaigns list. Report pagination lives inside the
Apple Ads request body, so update the file's selector pagination rather than
adding --paginate.
Use asc ads api request for Platform API v1 debugging, newly added Apple
fields, or support captures. Prefer first-class commands for routine work.
Read-only raw request:
asc ads api request \
--method POST \
--path v1/campaigns/query \
--ad-account "987654" \
--file campaign-query.json \
--output jsonRaw v1 requests only accept Platform API v1 paths or
https://api.ads.apple.com/v1/... URLs. DELETE requests and risky mutation
payloads require --confirm; multipart asset upload must use
asc ads assets upload.
For legacy troubleshooting only, keep asc ads v5 api request with its v5 path
and --org context:
asc ads v5 api request \
--method POST \
--path v5/campaigns/find \
--org "123456" \
--file selector.json \
--output jsonRaw requests only accept Apple Ads v5 paths or Apple Ads API URLs. DELETE
requests require --confirm; do not pass it until the command line includes
the exact target path you intend to delete.
Before running create, update, delete, or bulk commands:
- Run the matching
--helpcommand and confirm required flags. - Resolve account access with
asc ads acls list --output json. - Run a read-only resource check:
asc ads apps search --ad-account "987654" --query "Example" --limit 1 --output json. - Pass
--ads-profileand--ad-accountexplicitly when more than one account is possible. - Store request bodies in reviewed JSON files; avoid shell-escaped inline JSON.
- Create paused or clearly named test resources when validating workflow shape.
- Print or log the target IDs before using
--confirm. - Prefer deleting temporary parent campaigns only after confirming Apple allows the cleanup.
Example guarded delete:
asc ads campaigns view --ad-account "987654" --campaign "campaign-id" --output json
asc ads campaigns delete --ad-account "987654" --campaign "campaign-id" --confirmUse the auth doctor first:
asc ads auth doctor --output json
asc ads auth status --verbose --output jsonCommon fixes:
- Missing ad account: pass
--ad-account, exportASC_ADS_AD_ACCOUNT_ID, or store--ad-accountduring login. - Wrong profile: pass
--ads-profile "Marketing"or exportASC_ADS_PROFILE. - Keychain prompts in CI: export
ASC_ADS_BYPASS_KEYCHAIN=1. - Mixed sources: set
ASC_ADS_STRICT_AUTH=1to fail when both profile and env token/key sources are present. - Token-only runs:
ASC_ADS_ACCESS_TOKENstill needs--ad-account,ASC_ADS_AD_ACCOUNT_ID, or a stored profile account for scoped v1 commands.
When debugging API responses, use an explicit output format and redact tokens, client IDs, team IDs, key IDs, org IDs, and account names before sharing logs.