Composable Apple Search Ads CLI for the Apple Ads API, built for operators, developers, and AI agents that need safe campaign inventory, keyword reporting, and dry-run bid management.
Built by Attainment Labs
Apple Ads work often starts with a simple question:
Which campaigns, ad groups, and keywords are live right now?
The hard part is making the answer safe enough for automation. This CLI wraps asa-api-client with stable JSON, explicit pagination, credential-safe diagnostics, and dry-run-first write commands so a human or agent can inspect the account before making a live change.
Common use cases:
- Apple Search Ads account inventory
- App Store Ads keyword reporting
- ASA API diagnostics
- Campaign and ad group discovery
- Keyword bid previews
- Safe keyword bid updates after approval
- Agent-friendly Apple Ads workflows
The installed command is:
apple-adsThis project is not affiliated with, endorsed by, or sponsored by Apple Inc.
- Stable
--jsonoutput for success and error cases. - Invalid arguments return JSON when
--jsonis present. - Inventory commands expose pagination metadata.
- Inventory commands fail closed on truncation unless
--allow-partialis explicitly used. - Bid updates are dry-run by default.
- Bid apply requires
--apply. - Bid apply verifies live readback after the update and exits nonzero on mismatch.
- The CLI never prints full credentials.
- Local profile IDs are masked by default in
doctorandprofiles list. - Use
--revealonly when you need exact local values.
From source:
git clone https://github.com/attainmentlabs/apple-ads-cli.git
cd apple-ads-cli
python3 -m pip install -e .Requires Python 3.13 or newer.
Local development:
make install-localMore install options are in docs/install.md, including pipx and uv.
The CLI uses the environment variables expected by asa-api-client:
export ASA_CLIENT_ID="..."
export ASA_TEAM_ID="..."
export ASA_KEY_ID="..."
export ASA_PRIVATE_KEY_PATH="/path/to/AuthKey.p8"
export ASA_ORG_ID="..."You can also point a profile at a local env file. The profile stores only paths and IDs, not secret values:
apple-ads --json profiles set \
--name my-app \
--env-file ~/.config/apple-ads/my-app.env \
--org 1234567890 \
--default \
--writePreview profile writes by omitting --write.
apple-ads --json doctor
apple-ads --version
apple-ads --json doctor --live
apple-ads --json campaigns list
apple-ads --json ad-groups list --campaign CAMPAIGN_ID
apple-ads --json keywords list --campaign CAMPAIGN_ID --ad-group AD_GROUP_ID
apple-ads --json keywords live --serving-only
apple-ads --json reports keywords --campaign CAMPAIGN_ID --latest-complete --nonzero-only
apple-ads --json request get campaigns --param limit=10| Command | Purpose | Live write |
|---|---|---|
doctor |
Check config, auth variables, client import, and optional live reachability. | No |
profiles list |
List local non-secret profiles with IDs masked by default. | No |
profiles set |
Preview or write a local non-secret profile. | Local file only with --write |
campaigns list |
List campaigns with pagination metadata. | No |
ad-groups list |
List ad groups in one campaign. | No |
keywords list |
List keywords in one campaign and ad group. | No |
keywords live |
List active keywords and distinguish currently serving keywords. | No |
keywords set-bid |
Preview or apply one keyword bid update. | Yes, only with --apply |
reports keywords |
Read keyword performance metrics. | No |
request get |
Run a raw read-only GET request relative to the Apple Ads API version. | No |
More examples live in examples.
The raw request get command is a repair hatch for read-only Apple Ads API endpoints that do not have a high-level command yet. It does not expose POST, PUT, PATCH, or DELETE.
Successful commands return a top-level ok: true field:
{
"ok": true,
"rows": []
}Errors return a top-level ok: false field:
{
"ok": false,
"error": {
"code": "invalid_args",
"message": "argument --campaign: must be a positive integer"
}
}Inventory commands include pagination details:
{
"ok": true,
"partial": false,
"pagination": {
"total_results": 1001,
"returned": 1001,
"page_count": 2,
"truncated": false
},
"rows": []
}If the page walk is incomplete, the command returns nonzero partial_read unless --allow-partial is used.
Preview first:
apple-ads --json keywords set-bid \
--campaign CAMPAIGN_ID \
--ad-group AD_GROUP_ID \
--keyword KEYWORD_ID \
--bid 1.25Apply only after reviewing the exact preview:
apple-ads --json keywords set-bid \
--campaign CAMPAIGN_ID \
--ad-group AD_GROUP_ID \
--keyword KEYWORD_ID \
--bid 1.25 \
--applyRecommended repository description:
Apple Search Ads CLI for ASA API reporting, keyword inventory, and dry-run bid management.
Recommended GitHub topics:
apple-search-ads apple-ads asa-api app-store-ads app-store-marketing cli python marketing-automation keyword-research bid-management
make checkManual checks:
python3 -m unittest discover -s tests
PYTHONPYCACHEPREFIX=/private/tmp/apple-ads-cli-pycache python3 -m compileall apple_ads_cli
HOME=/private/tmp/apple-ads-cli-check-home python3 -m apple_ads_cli.cli --json doctorBefore public release, also run a private-data scan for product names, credential paths, account IDs, campaign IDs, ad group IDs, keyword IDs, local user paths, and forbidden Unicode dash characters.
python3 scripts/public_safety_scan.pyAlpha. Built around asa-api-client and intended for careful Apple Ads operator and agent workflows.