A powerful CLI for Apple Search Ads
Scriptable · Multi-account · CI-friendly · JSON-first
This project is in public beta. Core functionality (auth, campaigns, reports, keyword management) has been tested against live Apple Search Ads accounts. However, not all API endpoints and edge cases have been fully validated. Use in production at your own discretion and please open an issue if you encounter any problems.
Stop clicking through dashboards. Start shipping campaigns from your terminal.
| 🚀 Full campaign control | Create, update, pause, and report — all from one binary |
| 👥 Multi-account ready | Named profiles for agencies and multi-org operators |
| 🛡️ Safe mutations | --dry-run, interactive confirms, --yes for CI pipelines |
| 📊 JSON-first output | Pipe into jq, feed scripts, build dashboards |
| 🔌 Escape hatch | Hit any endpoint with appleads api — no wrapper needed |
Option 1 — Homebrew (recommended)
brew tap FerdiKT/tap
brew install appleadsOption 2 — Go install
go install github.com/ferdikt/appleads-cli@latestOption 3 — Build from source
git clone https://github.com/FerdiKT/appleads-cli.git
cd appleads-cli
make tidy
make build VERSION=1.1.0
./bin/appleads versionOption 4 — Multi-arch release binaries
make dist VERSION=1.1.0
ls dist/See docs/RELEASE.md for full release workflow.
📋 First time with Apple Ads API? Follow the API Setup Guide for the current OAuth flow: pick the right Apple Ads user, generate a key pair, upload the public key, collect
clientId/teamId/keyId, then create a token.
Get up and running in 5 minutes.
What this actually means:
- Use an Apple Ads login that can access the target account or campaign groups. In practice this is usually a separate Apple ID invited as the API user, not the primary admin login.
- Generate a local private/public key pair.
- Upload the public key in Apple Ads to generate
clientId,teamId, andkeyId. - Save those values locally and mint a token.
When Apple shows the credentials, you can paste the full 3-line block directly into the wizard:
clientId SEARCHADS....
teamId SEARCHADS....
keyId xxxxxxxx-....
# Create your first profile
appleads auth initappleads auth init now guides that sequence interactively. If you prefer to do it step by step:
appleads auth keygen # generate EC P-256 keypair
appleads auth public-key # print PEM to upload in Apple Ads UIThen save your credentials:
appleads auth set \
--client-id "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--team-id "SEARCHADS.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--key-id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"appleads auth token # exchange credentials for access token
appleads auth orgs --select # pick your org interactively
appleads auth status --json # inspect active auth/profile stateorg_id is optional for token creation, but required by most campaign/report commands.
appleads doctor # runs 7-layer health check ✅appleads campaigns list --limit 5
appleads orgs list --json
appleads reports campaigns --start-date 2026-04-10 --end-date 2026-04-16 --json
appleads campaigns health --jsonMost create, update, find, and report commands accept JSON payloads:
appleads campaigns create --body-file ./payloads/campaign-create.json
appleads reports campaigns --body-file ./payloads/report-campaigns.json💡 Tip: Every body-based command includes copy/paste-ready examples in
--help.
| Group | Commands | Highlights |
|---|---|---|
auth |
init · set · token · orgs · keygen · public-key · show/status | OAuth setup, profile management |
orgs |
list · use | Top-level org discovery and switching |
doctor |
— | 7-layer health check (config → API) |
campaigns |
list · get · create · update · delete · find · enable · pause · health | Full CRUD + quick actions |
adgroups |
list · get · create · update · delete · find · enable · pause | Full CRUD + quick actions |
ads |
list · get · create · update · delete · find · enable · pause | Full CRUD + quick actions |
keywords |
targeting · campaign-negative · adgroup-negative · recommendations | Bid management & discovery |
targeting |
show · set · clear · replace · country · device | Dimension presets |
reports |
campaigns · adgroups · keywords · searchterms · ads · impressionshare · template | Presets & template generator |
apps |
details · eligibilities · product-pages | App metadata |
search |
app · geo | App & geo lookup |
creatives |
list · get · find · create · update · delete | Creative management |
budget-orders |
list · get · create · update · find | Financial controls |
account |
me · acls | Account introspection |
api |
— | Raw API caller for any endpoint |
agent |
install-skill · link-skill · show-skill-path | Install bundled Codex skill assets |
Full reference:
appleads --help·appleads <group> --help·appleads <group> <command> --help
Repo-local guidance also lives in AGENTS.md and skills/appleads-cli/SKILL.md.
To install the bundled Codex skill directly from the CLI:
appleads agent install-skillFor local development, if you want a symlink instead of a copied install:
appleads agent link-skill --source ./skills/appleads-cliManage unlimited profiles for different accounts, clients, or environments.
# Create profiles
appleads auth profiles create personal
appleads auth profiles create agency
# Initialize each
appleads -p personal auth init
appleads -p agency auth init
# Switch context
appleads auth profiles use personal
appleads auth profiles switch agency # aliasAdvanced profile operations
appleads auth profiles list
appleads auth profiles rename agency agency-main
appleads auth profiles clone agency-main agency-backup
appleads auth profiles export agency-main --out ./agency-main.profile.json
appleads auth profiles import ./agency-main.profile.json --name agency-importedWhen you omit
-p,appleadsuses the active profile automatically.
Mutations are safe by default. Every destructive or state-changing command supports:
| Flag | Behavior |
|---|---|
--dry-run |
Preview the payload without calling the API |
| (default) | Interactive confirmation prompt |
--yes |
Bypass prompt — designed for CI/CD pipelines |
--confirm-org <id> |
Abort if the resolved org does not match the expected org |
# Preview what would happen
appleads targeting country add \
--campaign-id 123456 --adgroup-id 987654 \
--codes US,CA --dry-run
# Lock the command to one org before sending
appleads campaigns enable 123456 --confirm-org 8526150 --yes
# Execute with auto-confirm (CI mode)
appleads targeting country add \
--campaign-id 123456 --adgroup-id 987654 \
--codes US,CA --yesUse first-class date flags for common report pulls:
appleads reports campaigns --start-date 2026-04-10 --end-date 2026-04-16
appleads reports searchterms --campaign-id 123456 --start-date 2026-04-10 --end-date 2026-04-16 --time-zone ORTZappleads reports ... now fills sane request defaults when possible:
selector.paginationdefaults tooffset=0, limit=1000selector.orderByis added automatically per report type- when neither
granularitynor row totals are supplied,returnRowTotals=trueis added
Generate report payloads in seconds with built-in presets:
appleads reports template campaigns --preset last-7d
appleads reports template keywords --campaign-id 123456 --preset last-30dExecute directly:
appleads reports template campaigns --preset yesterday --runAvailable presets: today · yesterday · last-7d · last-30d
A comprehensive 7-layer diagnostic that validates your entire setup:
✓ Config file resolved
✓ Profile loaded
✓ Auth fields present
✓ Private key readable
✓ Client secret generated
✓ Token valid / refreshed
✓ Org resolved
✓ API reachable (/me, ACLs, /campaigns)
appleads doctor # full check
appleads doctor --no-network # skip API calls
appleads doctor --strict # warnings → errorsExit code is non-zero on failures. With
--strict, warnings also trigger non-zero exit.
Need an endpoint that doesn't have a typed command yet? Use the escape hatch:
appleads api --method GET --path /campaigns --query limit=20 --query offset=0
appleads api --method POST --path /campaigns/find --body-file ./payloads/campaign-find.json| Setting | Default |
|---|---|
| Config path | ~/Library/Application Support/appleads/config.json |
| API version | v5 |
| API base URL | https://api.searchads.apple.com |
| OAuth token URL | https://appleid.apple.com/auth/oauth2/token |
Global flags: --output table|json · --json · --profile <name> · --config <path> · --confirm-org <id>
# JSON output for automation pipelines
appleads campaigns list --limit 20 --json | jq '.data[].name'| Error | Fix |
|---|---|
org_id is not set |
Run appleads auth orgs --select or pass --org-id |
| 401 / 403 from API | Run appleads doctor then appleads auth token |
| 404 on a command | Check docs/GAP_MATRIX.md — some endpoints are capability-dependent |
-
Fork the repo and create a feature branch
-
Add or adjust commands
-
Run tests:
go test ./... -
Update
README.mdanddocs/GAP_MATRIX.mdwhen behavior changes
- Never commit real credentials or
.p8private keys - Use profile export/import through secured channels
- Maintain separate profiles per client/account
Built with ❤️ for Apple Search Ads power users