Your App Store growth stack in one binary.
The complete ASO + Apple Ads CLI for indie developers — Apple Ads Platform API v1
management fused with organic rank tracking, keyword intelligence, and
keyword-level revenue attribution. JSON-first, agent-native, --dry-run everywhere.
the Apple Ads + ASO command line — from research to ROAS
$ appadscli aso research "meditation" --expand
{
"term": "meditation",
"searchPopularity": 5, ← Apple's own demand score
"difficulty": 9.6, ← computed from the live top-10
"top10": [ { "rank": 1, "name": "Insight Timer", ... } ],
"expandedCandidates": [ "sleep", "mindfulness", "breathing", ... ]
}
$ appadscli harvest run --discovery 74130 --target 74131 --min-installs 2 --dry-run
[ { "action": "promote", "searchTerm": "habit streak app",
"installs": 6, "cpa": 1.82, "bid": 1.21,
"reason": "6 installs at 1.82 CPA — promote to exact in target, negate in discovery" } ]Every existing tool has half the picture. ASO tools have no ads management. ASA tools have no organic data. Both charge SaaS prices for data Apple gives you for free through your own API credentials.
| ASO tools | ASA tools | appadscli | |
|---|---|---|---|
| Apple's search popularity (1–5) | 💰 resold | ✗ | ✅ free, from your API |
| Organic rank tracking | ✅ | ✗ | ✅ local SQLite history |
| Campaign / keyword / bid management | ✗ | ✅ | ✅ full v1 surface |
| Search-term harvesting loop | ✗ | some | ✅ harvest run |
| Impression share insights | ✗ | some | ✅ insights impression-share |
| Keyword-level ROAS (RevenueCat) | ✗ | 💰 SaaS | ✅ roas report --by keyword |
| Guided campaign setup | ✗ | ✗ | ✅ campaigns scaffold |
| Guardrail automation with approval | ✗ | black box | ✅ watch → plan apply |
| MCP server + agent skills | ✗ | ✗ | ✅ mcp serve, install-skills |
| Price | $29–299/mo | % of spend | free, open source |
Built v1-native for the Apple Ads Platform API (the v5 Campaign Management API sunsets January 26, 2027).
npm (macOS / Linux / Windows)
npm install -g appadscliThe package downloads the prebuilt binary for your platform and verifies its SHA-256 against the release checksums.
Homebrew
brew install appadscli/tap/appadscliThe
appadscli/tap/prefix names the tap the formula lives in. A barebrew install appadsclirequires acceptance into homebrew-core, which gates on notability (30 forks / 30 watchers / 75 stars) — tracked in #1.
Install script (macOS / Linux)
curl -fsSL https://raw.githubusercontent.com/appadscli/appadscli/main/install.sh | shGo
go install github.com/appadscli/appadscli@latestFrom source
git clone https://github.com/appadscli/appadscli && cd appadscli && make install1. Create API credentials — Apple Ads → Account Settings → API:
openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem
openssl ec -in private-key.pem -pubout -out public-key.pem # upload this one2. Log in (credentials go to the macOS keychain, or a 0600 file elsewhere):
appadscli auth login \
--client-id SEARCHADS.xxxx --team-id SEARCHADS.xxxx \
--key-id xxxx --private-key ./private-key.pem
appadscli auth doctor # full diagnostic: key, token, org access
appadscli accounts list
appadscli accounts use <adAccountId>3. Go:
appadscli dashboard --since 7d
appadscli aso research "your category" --expand
appadscli campaigns scaffold --app <adamId> --daily-budget 10 --dry-runNo credentials yet? The ASO half works without any login:
appadscli aso difficulty, aso track, aso metadata audit,
aso competitors gap all run on public data.
appadscli
├── auth login · status · doctor · logout
├── accounts list · use
├── me
├── apps search · get · eligibility · rejections · languages
├── geo search
│
├── aso ← organic growth (no ads credentials needed for most)
│ ├── research popularity + difficulty + top-10 + candidate fan-out
│ ├── popularity bulk Apple demand scores (1–5) across countries
│ ├── suggest Apple's keyword/phrase/category suggestions
│ ├── difficulty scored 1–10 from the live top-10
│ ├── track add · run · report · alerts (cron/CI-friendly)
│ ├── metadata audit · generate (keyword field from converting terms)
│ ├── competitors gap · watch (snapshot + diff competitor metadata)
│ └── reviews list
│
├── campaigns list · get · create · update · pause · resume · delete
│ └── scaffold ← one-shot brand/category/competitor/discovery structure
├── adgroups list · get · create · update
├── keywords list · add · update · pause · bulk upsert
├── negatives list · add
├── harvest run · report ← THE core loop: promote winners, negate waste
├── bids adjust (CPA/ROAS targets) · rules apply (declarative)
├── budget pacing · orders (shared budgets)
├── reco list · apply · dismiss (Apple's own recommendations)
│
├── reports campaigns · adgroups · keywords · ads · searchterms
├── insights impression-share · popularity
├── dashboard one-screen spend/CPA/installs summary
│
├── creatives list · get · create · delete
├── ads list · create · pause
├── cpp list · test start · test report (CPP A/B testing)
├── maps brands · locations · groups · categories · reports
│
├── watch guardrails tick: alert → propose → auto
├── plan show · apply --confirm (PR-style approval)
├── history Apple's change history · verify (external-change detection)
│
├── rc connect · ingest (RevenueCat)
├── roas report --by campaign|adgroup|keyword
├── ltv report --horizon 30d|90d|1y
│
├── mcp serve built-in MCP server for AI agents
├── install-skills harvest/launch/audit playbooks for agents
├── docs list · show <topic>
└── completion bash · zsh · fish · powershell
appadscli campaigns scaffold --app 1459969523 \
--structure brand,category,competitor,discovery \
--daily-budget 10 --country us --dry-runFour campaigns, each with a configured ad group — discovery gets Search Match
ON to mine terms. Drop --dry-run, add --confirm to create.
appadscli harvest run \
--discovery <discoveryCampaignId> --target <categoryCampaignId> \
--min-installs 2 --max-cpa 3.00 --auto-negate --dry-runPromotes converting search terms to exact match (bid = observed CPT × 1.1),
negates them in discovery, negates wasteful terms. Local memory ensures a term
is never promoted twice. Run weekly, or wire it into watch.
appadscli aso track add --app <adamId> --keywords ./kw.txt --countries us,gb
appadscli aso track run # cron this
appadscli aso track alerts --drop 5 # CI: exit 1 on rank drops
appadscli aso metadata generate --app <adamId> # keyword field from converting paid termsappadscli rc connect --api-key <v2-key> --project <id>
appadscli rc ingest ./export.csv
appadscli roas report --by keyword --since 30d
appadscli bids adjust --adgroup <id> --target-roas 150% --dry-runTwo lines of RC SDK setup, no ATT prompt required — see
docs/guides/revenuecat-roas.md.
appadscli watch --config ./guardrails.json # cron every few hours
appadscli plan show ./appadscli-plan-*.json # review what it wants to do
appadscli plan apply ./appadscli-plan-*.json --confirmguardrails.json (example) sets CPA
ceilings, spend caps, never-pause lists, and the autonomy level:
alert → propose → auto. Every mutation is logged locally;
appadscli history verify cross-checks Apple's change history for changes made
outside the CLI.
claude mcp add appadscli -- appadscli mcp serve # 32 tools, 1:1 with commands
appadscli install-skills # harvest/launch/audit playbooksMutating MCP tools require confirm: true — otherwise they run --dry-run
and return the plan. An agent must show you changes before it can spend a cent.
- every mutating command supports
--dry-run - anything that spends, pauses, or deletes requires
--confirm(or an interactive "y") - all mutations are logged to
~/.appadscli/appadscli.dband verifiable against Apple's change history (appadscli history verify) - credentials live in the macOS keychain (or 0600 files), never in configs
- no telemetry — the API traffic goes to Apple and nowhere else
TTY → table. Pipe/CI → JSON. --output json|table|csv|markdown always wins;
APPADSCLI_OUTPUT sets the default.
appadscli reports keywords --since 7d | jq '.[] | select(.totalInstalls == "0")'
appadscli reports campaigns --since 30d --output csv > spend.csv
appadscli aso track report --output markdown >> weekly-report.md- Rank tracking uses the public iTunes Search API (unofficial for this purpose) — appadscli paces requests at 1 rps and caches for an hour.
- Reviews: Apple's public RSS feed has been returning empty results for many storefronts since 2025; the command degrades gracefully. ASC API support for own-app reviews is planned.
- ROAS is a floor — limit-ad-tracking users land in the organic bucket, and Basic Search Ads accounts produce "Unspecified" attribution rows.
- No daemon — 24/7 autonomy is cron/CI running
appadscli watch. - Each user runs their own credentials. Don't aggregate or resell Apple's data.
make build # build ./appadscli
make test # go test ./...
make lint # go vet
make install # install to GOPATH/binRepo layout: cmd/ (command tree) · internal/api (v1 client) ·
internal/aso, internal/engine (intelligence + workflows) ·
internal/store (SQLite state) · .agents/skills (agent playbooks) ·
docs/guides (embedded docs).
Contributions welcome — see CONTRIBUTING.md.
MIT © Tawhid Joarder