aso-keyword-toolkit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aso_keyword_toolkit-0.1.0/LICENSE +21 -0
- aso_keyword_toolkit-0.1.0/PKG-INFO +495 -0
- aso_keyword_toolkit-0.1.0/README.md +464 -0
- aso_keyword_toolkit-0.1.0/aso_keyword_toolkit.egg-info/PKG-INFO +495 -0
- aso_keyword_toolkit-0.1.0/aso_keyword_toolkit.egg-info/SOURCES.txt +24 -0
- aso_keyword_toolkit-0.1.0/aso_keyword_toolkit.egg-info/dependency_links.txt +1 -0
- aso_keyword_toolkit-0.1.0/aso_keyword_toolkit.egg-info/entry_points.txt +2 -0
- aso_keyword_toolkit-0.1.0/aso_keyword_toolkit.egg-info/requires.txt +4 -0
- aso_keyword_toolkit-0.1.0/aso_keyword_toolkit.egg-info/top_level.txt +1 -0
- aso_keyword_toolkit-0.1.0/asokit/__init__.py +8 -0
- aso_keyword_toolkit-0.1.0/asokit/__main__.py +3 -0
- aso_keyword_toolkit-0.1.0/asokit/asc.py +257 -0
- aso_keyword_toolkit-0.1.0/asokit/cli.py +422 -0
- aso_keyword_toolkit-0.1.0/asokit/metadata.py +108 -0
- aso_keyword_toolkit-0.1.0/asokit/report.py +88 -0
- aso_keyword_toolkit-0.1.0/asokit/research.py +146 -0
- aso_keyword_toolkit-0.1.0/asokit/sources.py +133 -0
- aso_keyword_toolkit-0.1.0/asokit/storefronts.py +101 -0
- aso_keyword_toolkit-0.1.0/asokit/suggest.py +123 -0
- aso_keyword_toolkit-0.1.0/pyproject.toml +55 -0
- aso_keyword_toolkit-0.1.0/setup.cfg +4 -0
- aso_keyword_toolkit-0.1.0/tests/test_metadata.py +85 -0
- aso_keyword_toolkit-0.1.0/tests/test_research.py +144 -0
- aso_keyword_toolkit-0.1.0/tests/test_sources.py +28 -0
- aso_keyword_toolkit-0.1.0/tests/test_storefronts.py +44 -0
- aso_keyword_toolkit-0.1.0/tests/test_suggest.py +101 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gustavo Germano
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aso-keyword-toolkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Free App Store keyword research and ASO metadata toolkit — per-storefront autocomplete data, competitor analysis, and App Store Connect sync. No subscription.
|
|
5
|
+
Author: Gustavo Germano
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ggapp1/aso-keyword-toolkit
|
|
8
|
+
Project-URL: Issues, https://github.com/ggapp1/aso-keyword-toolkit/issues
|
|
9
|
+
Keywords: aso,app-store-optimization,keyword-research,app-store,app-store-connect,ios,aso-tools,mobile-marketing,itunes-search-api,app-store-keywords
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
22
|
+
Classifier: Topic :: Software Development :: Localization
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Provides-Extra: connect
|
|
28
|
+
Requires-Dist: pyjwt>=2.0; extra == "connect"
|
|
29
|
+
Requires-Dist: cryptography>=3.4; extra == "connect"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# ASO Keyword Toolkit — free App Store keyword research from the command line
|
|
33
|
+
|
|
34
|
+
[](https://github.com/ggapp1/aso-keyword-toolkit/actions/workflows/tests.yml)
|
|
35
|
+
[](LICENSE)
|
|
36
|
+
[](https://www.python.org/downloads/)
|
|
37
|
+
[](pyproject.toml)
|
|
38
|
+
|
|
39
|
+
**App Store Optimization keyword research, competitor analysis, and App Store
|
|
40
|
+
Connect metadata automation — for 60 countries, from your terminal, for free.**
|
|
41
|
+
|
|
42
|
+
Find the keywords real users type in any App Store storefront. See who ranks for
|
|
43
|
+
them and how beatable they are. Discover where your app is already winning and
|
|
44
|
+
where it's invisible. Then write, validate, and publish localized metadata
|
|
45
|
+
without touching the App Store Connect web UI.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install git+https://github.com/ggapp1/aso-keyword-toolkit
|
|
49
|
+
asokit init --app-id 1234567890 --markets de,fr,jp
|
|
50
|
+
asokit research --all
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
That's it. No account, no API key, no subscription, no trial. The research path
|
|
54
|
+
uses two public Apple endpoints and has **zero dependencies**.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Why this exists
|
|
59
|
+
|
|
60
|
+
ASO tools start around $19/month and climb into the hundreds. A large part of
|
|
61
|
+
what they sell is packaging of two Apple endpoints that are open to anyone: the
|
|
62
|
+
App Store's autocomplete and the iTunes Search API. This gives you direct
|
|
63
|
+
access to both, adds the analysis layer on top, and connects the result
|
|
64
|
+
straight to the App Store Connect API so the whole loop runs from one command.
|
|
65
|
+
|
|
66
|
+
It is genuinely useful on its own, and it is also an honest floor: it will
|
|
67
|
+
never invent a search-volume number that Apple does not publish. More on that
|
|
68
|
+
in [What the numbers mean](#what-the-numbers-mean).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
Not on PyPI yet — install straight from GitHub:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install git+https://github.com/ggapp1/aso-keyword-toolkit
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**pipx or uv** — if you want it as an isolated global tool:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pipx install git+https://github.com/ggapp1/aso-keyword-toolkit
|
|
84
|
+
uv tool install git+https://github.com/ggapp1/aso-keyword-toolkit
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**For development**, or if you want to read the code first:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/ggapp1/aso-keyword-toolkit
|
|
91
|
+
cd aso-keyword-toolkit && pip install -e .
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**App Store Connect sync** needs two extra libraries for JWT signing. Keyword
|
|
95
|
+
research does not:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install "aso-keyword-toolkit[connect] @ git+https://github.com/ggapp1/aso-keyword-toolkit"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Claude Code plugin
|
|
102
|
+
|
|
103
|
+
The repo doubles as a [Claude Code](https://claude.com/claude-code) plugin
|
|
104
|
+
marketplace. Installing it gives your agent a six-phase ASO playbook with
|
|
105
|
+
review gates, wired to this CLI:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
/plugin marketplace add ggapp1/aso-keyword-toolkit
|
|
109
|
+
/plugin install aso-keyword-toolkit@aso-toolkit
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The skill calls the `asokit` CLI, so install that too (above). Then ask
|
|
113
|
+
Claude to research a market, or invoke the skill directly with
|
|
114
|
+
`/aso-keyword-toolkit:aso-localize`. It runs the research, reads the report,
|
|
115
|
+
drafts metadata that respects Apple's character and duplication rules, and
|
|
116
|
+
stops for your review before anything is published.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Getting started in one command
|
|
121
|
+
|
|
122
|
+
If your app is already on the App Store, let the toolkit write your config:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
asokit init --app-id 1234567890 --markets de,fr,jp
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
It reads your live listing, looks at the apps ranking alongside you, and
|
|
129
|
+
derives starter seed keywords from what those apps actually call themselves:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
reading your listing in the US store...
|
|
133
|
+
Budget Expense Tracker — Finance
|
|
134
|
+
looking at apps that rank alongside it (this takes a moment)...
|
|
135
|
+
read 68 competitor listings
|
|
136
|
+
|
|
137
|
+
wrote asokit.json with 16 seeds and 3 market(s)
|
|
138
|
+
|
|
139
|
+
Suggested seeds:
|
|
140
|
+
expense tracker
|
|
141
|
+
budget planner
|
|
142
|
+
money manager
|
|
143
|
+
spending
|
|
144
|
+
receipts
|
|
145
|
+
savings
|
|
146
|
+
...
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Your app ID is the number in your App Store URL
|
|
150
|
+
(`apps.apple.com/app/id1234567890`). Starting from scratch instead?
|
|
151
|
+
`asokit init` writes a template you can fill in by hand.
|
|
152
|
+
|
|
153
|
+
Check everything is ready before a long run:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
asokit doctor
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
config asokit.json — 3 market(s)
|
|
161
|
+
apple api reachable — 10 suggestions for a test query
|
|
162
|
+
connect not configured (only needed to push metadata)
|
|
163
|
+
|
|
164
|
+
ready
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## App Store keyword research
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
asokit research --market de
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Every seed goes through the App Store's autocomplete **for that specific
|
|
176
|
+
storefront**, harvesting what Apple suggests to users in that country. Every
|
|
177
|
+
candidate is then scored against the top 50 ranked apps there.
|
|
178
|
+
|
|
179
|
+
Real output from the German App Store, tracking a budgeting app:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
| # | keyword | pop | ac-rank | seeds | comp | median top-5 ratings | exact-title | our rank | opp |
|
|
183
|
+
|---|----------------------------|-----|---------|-------|------|----------------------|-------------|----------|-----|
|
|
184
|
+
| 1 | kostenkontrolle | 10 | 1 | 1 | 1 | 23 | 1 | — | 18 |
|
|
185
|
+
| 2 | savings goals | 10 | 1 | 1 | 2 | 240 | 1 | — | 16 |
|
|
186
|
+
| 3 | ausgaben tracker | 11 | 1 | 2 | 3 | 2,697 | 1 | 1 | 15 |
|
|
187
|
+
| 4 | geld sparen | 11 | 1 | 2 | 3 | 1,899 | 1 | 13 | 15 |
|
|
188
|
+
| 5 | haushaltsbuch | 9 | 2 | 1 | 3 | 8,592 | 8 | 1 | 13 |
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
| column | what it tells you |
|
|
192
|
+
|--------|-------------------|
|
|
193
|
+
| `pop` | autocomplete-derived ranking proxy — higher means Apple surfaces it sooner |
|
|
194
|
+
| `ac-rank` | best position across every seed that surfaced it |
|
|
195
|
+
| `seeds` | how many different seeds led to it (breadth of relevance) |
|
|
196
|
+
| `comp` | competition tier 1–5, from median rating count of the top 5 apps |
|
|
197
|
+
| `exact-title` | how many of the top 10 put this exact term in their title |
|
|
198
|
+
| `our rank` | where your app currently sits, if it's in the top 50 |
|
|
199
|
+
| `opp` | `pop + (5 - comp) * 2` — a sort key, not a verdict |
|
|
200
|
+
|
|
201
|
+
A term at `comp` 1 whose top-5 apps have two dozen ratings is a real opening. A
|
|
202
|
+
term at `comp` 5 belongs to someone with a marketing budget.
|
|
203
|
+
|
|
204
|
+
Each run also prints the headline takeaways so you don't have to read the whole
|
|
205
|
+
table:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
aso/de/report.md
|
|
209
|
+
least contested: kostenkontrolle, savings goals
|
|
210
|
+
you already rank: ausgaben tracker (#1), haushaltsbuch kostenlos (#1), budget planner (#2)
|
|
211
|
+
winnable gaps: kostenkontrolle, savings goals
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Responses are cached on disk, so re-running the same market weeks later to
|
|
215
|
+
measure whether your changes worked costs almost nothing.
|
|
216
|
+
|
|
217
|
+
### Competitor analysis
|
|
218
|
+
|
|
219
|
+
Every report ends with the apps that kept appearing across your queries. A
|
|
220
|
+
rival's title *is* their keyword strategy, stated publicly:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
| app | ratings | top-5 appearances | best rank |
|
|
224
|
+
|--------------------------------|---------|-------------------|-----------|
|
|
225
|
+
| Ausgaben Budget Planner Fleur | 1,899 | 20 | 1 |
|
|
226
|
+
| Monefy: Ausgaben manager | 4,710 | 18 | 2 |
|
|
227
|
+
| Haushaltsbuch MoneyStats | 22,435 | 18 | 2 |
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Autocomplete mixes competitor **app names** in with genuine queries, which
|
|
231
|
+
quietly corrupts keyword research. Those are detected and listed separately
|
|
232
|
+
rather than scored as opportunities — including in Japanese and Chinese
|
|
233
|
+
listings, which join name and tagline with `・` and `:` instead of `:`.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 60 storefronts, every language
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
asokit storefronts # list all of them
|
|
241
|
+
asokit storefronts --check jp # verify one against the live endpoint
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
Japan (jp) header=143462-4,29
|
|
246
|
+
suggestions for '家計簿': 家計簿, 家計簿アプリ, 家計簿 無料 人気, 家計簿 レシート, 家計簿 共有
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Country selection uses the `X-Apple-Store-Front` header. **The `cc=` query
|
|
250
|
+
parameter you'll find in older blog posts does not work** — Apple accepts it
|
|
251
|
+
and silently returns US results, which is an excellent way to ship a German
|
|
252
|
+
keyword set built entirely from American data. This toolkit gets it right and
|
|
253
|
+
gives you `--check` to prove it.
|
|
254
|
+
|
|
255
|
+
Non-Latin scripts, accents, and compound-word languages are first-class.
|
|
256
|
+
Diacritics matter more than people expect: in the Spanish App Store,
|
|
257
|
+
`credito` and `crédito` return different suggestion sets.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## ASO metadata validation
|
|
262
|
+
|
|
263
|
+
The most expensive mistake in App Store metadata is repetition. Apple indexes
|
|
264
|
+
your **app name, subtitle, and keyword field as a single pool** and combines
|
|
265
|
+
words across them. Repeating a term you already used buys nothing and burns
|
|
266
|
+
part of a 100-character budget you can't extend.
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
asokit metadata check de.json
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
--- name (26/30) ---
|
|
274
|
+
Budgeteer: Expense Tracker
|
|
275
|
+
|
|
276
|
+
--- subtitle (24/30) ---
|
|
277
|
+
Haushaltsbuch & Finanzen
|
|
278
|
+
|
|
279
|
+
--- keywords (71/100) ---
|
|
280
|
+
budgetplaner,ausgaben,sparen,kostenkontrolle,quittungen,einnahmen,konto
|
|
281
|
+
|
|
282
|
+
All fields within limits. No repetition across name, subtitle and keywords.
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
It catches what actually goes wrong:
|
|
286
|
+
|
|
287
|
+
- fields over the 30 / 30 / 100 character limits
|
|
288
|
+
- a keyword that already appears in your name or subtitle
|
|
289
|
+
- a word duplicated between name and subtitle
|
|
290
|
+
- the same keyword listed twice
|
|
291
|
+
- `a, b, c` spacing in the keyword field, which silently costs you characters
|
|
292
|
+
|
|
293
|
+
This command needs no credentials, so it doubles as clean copy-paste output if
|
|
294
|
+
you'd rather paste into the web UI.
|
|
295
|
+
|
|
296
|
+
### Metadata rules worth knowing
|
|
297
|
+
|
|
298
|
+
- Limits: **name 30, subtitle 30, keywords 100**, promotional text 170.
|
|
299
|
+
- Never repeat a term across those three fields.
|
|
300
|
+
- No spaces after commas in the keyword field.
|
|
301
|
+
- Use singular forms — Apple handles plurals.
|
|
302
|
+
- Apple combines words across fields, so prefer distinct single words in the
|
|
303
|
+
keyword field over repeating a phrase from your title.
|
|
304
|
+
- Compound-word languages (German, Dutch, Finnish) can't have compounds
|
|
305
|
+
assembled from parts across fields, so each compound must appear whole.
|
|
306
|
+
Budget for it: `Haushaltsbuch` costs 13 of your 30 subtitle characters.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## App Store Connect automation
|
|
311
|
+
|
|
312
|
+
Localized metadata is split across two different API resources and is only
|
|
313
|
+
writable while a version sits in an editable state — the kind of detail that
|
|
314
|
+
makes hand-rolling this annoying. The toolkit handles both, creates locales
|
|
315
|
+
that don't exist yet, and dry-runs by default.
|
|
316
|
+
|
|
317
|
+
Create a key at **App Store Connect → Users and Access → Integrations → App
|
|
318
|
+
Store Connect API** with the App Manager role:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
export ASC_KEY_ID=XXXXXXXXXX
|
|
322
|
+
export ASC_ISSUER_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
323
|
+
export ASC_PRIVATE_KEY_PATH=~/.appstoreconnect/AuthKey_XXXXXXXXXX.p8
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
asokit metadata status # is a version editable right now?
|
|
328
|
+
asokit metadata push de.json # dry run — shows exactly what would change
|
|
329
|
+
asokit metadata push de.json --apply # write it
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Validation runs before anything is sent, so a bad file fails locally rather
|
|
333
|
+
than halfway through a batch. Credentials are read from the environment only;
|
|
334
|
+
`.p8` keys are gitignored by default.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## A worked example: budgeting apps in the German App Store
|
|
339
|
+
|
|
340
|
+
A real run — 16 seeds, 40 candidates scored — tracking one of the established
|
|
341
|
+
German budgeting apps. Every number below came out of the tool.
|
|
342
|
+
|
|
343
|
+
**It already owns the German expense vocabulary.** Rank **#1** for
|
|
344
|
+
`haushaltsbuch`, `ausgaben tracker`, `ausgaben tracker kostenlos`, and
|
|
345
|
+
`haushaltsbuch kostenlos`; **#2** for `expense tracker`, `budget planner`, and
|
|
346
|
+
`budgetplaner`. That's a defended position, and the first job of any metadata
|
|
347
|
+
change is not to break it.
|
|
348
|
+
|
|
349
|
+
**But it's invisible where the competition is weakest.** `kostenkontrolle`
|
|
350
|
+
scored competition tier 1 — the median top-5 app there has **23 ratings** — and
|
|
351
|
+
this app doesn't rank for it at all. Compare `haushaltsbuch`, where the median
|
|
352
|
+
top-5 app has **8,592 ratings** and eight of the top ten put the word directly
|
|
353
|
+
in their title. Same category, wildly different economics.
|
|
354
|
+
|
|
355
|
+
The read: the contested compound is already won and should be protected. The
|
|
356
|
+
cheap opening is the term nobody is fighting for.
|
|
357
|
+
|
|
358
|
+
**The competitor table shows the strategy behind the rankings:**
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
| Ausgaben Budget Planner Fleur | 1,899 | 20 | 1 |
|
|
362
|
+
| Monefy: Ausgaben manager | 4,710 | 18 | 2 |
|
|
363
|
+
| Haushaltsbuch MoneyStats | 22,435 | 18 | 2 |
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Three of the strongest apps put `Ausgaben` in their title. That isn't something
|
|
367
|
+
you have to guess at — it's their keyword bet, published.
|
|
368
|
+
|
|
369
|
+
**One thing the numbers alone would get wrong.** In the US store, `mint` looks
|
|
370
|
+
like an obvious budgeting keyword. The top result is **Mint Mobile, a wireless
|
|
371
|
+
carrier**. Ranking there buys traffic from people shopping for a phone plan.
|
|
372
|
+
**A popularity score rates the query, not who answers it** — which is exactly
|
|
373
|
+
why every report shows you who currently ranks.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## What the numbers mean
|
|
378
|
+
|
|
379
|
+
`pop` is a **ranking proxy derived from Apple's own autocomplete ordering**,
|
|
380
|
+
not a search-volume estimate. Apple does not publish search volume through any
|
|
381
|
+
free endpoint. Tools that show you "volume: 4,400/mo" from these sources are
|
|
382
|
+
modelling, not measuring.
|
|
383
|
+
|
|
384
|
+
This matters practically. Autocomplete ordering is real signal — it is Apple
|
|
385
|
+
telling you what people in that country type — and it is excellent for *ranking
|
|
386
|
+
candidates against each other*. It cannot tell you a term's absolute traffic.
|
|
387
|
+
Use it to narrow hundreds of candidates down to a handful, then validate those
|
|
388
|
+
few against Apple Search Ads if you need absolute numbers.
|
|
389
|
+
|
|
390
|
+
Relevance stays a human judgment. The toolkit filters competitor app names out
|
|
391
|
+
of your results, but no heuristic can tell you that a term in your category
|
|
392
|
+
serves a different audience. That's what the competitor table is for — read it.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Command reference
|
|
397
|
+
|
|
398
|
+
| command | what it does |
|
|
399
|
+
|---------|--------------|
|
|
400
|
+
| `asokit init --app-id ID` | write a config with seeds derived from your live listing |
|
|
401
|
+
| `asokit doctor` | check config, Apple connectivity, and credentials |
|
|
402
|
+
| `asokit storefronts` | list all 60 storefronts |
|
|
403
|
+
| `asokit storefronts --check de` | verify one storefront against the live endpoint |
|
|
404
|
+
| `asokit research --market de` | expand and score keywords for one market |
|
|
405
|
+
| `asokit research --all` | run every market in your config |
|
|
406
|
+
| `asokit metadata check FILE` | validate limits and duplication |
|
|
407
|
+
| `asokit metadata status` | show which version and locales are editable |
|
|
408
|
+
| `asokit metadata push FILE` | dry-run the sync |
|
|
409
|
+
| `asokit metadata push FILE --apply` | write metadata to App Store Connect |
|
|
410
|
+
|
|
411
|
+
Each research run writes `report.md` plus the raw `scores.json` and
|
|
412
|
+
`expansion.json`, so you can build your own analysis on the data.
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## FAQ
|
|
417
|
+
|
|
418
|
+
**Is this really free?**
|
|
419
|
+
Yes. Keyword research uses public Apple endpoints that need no account.
|
|
420
|
+
Publishing metadata uses your own App Store Connect API key, which Apple issues
|
|
421
|
+
free to developers. There is no hosted service and nothing to sign up for.
|
|
422
|
+
|
|
423
|
+
**Do I need an Apple Search Ads account?**
|
|
424
|
+
No. Apple Search Ads is one way to get official popularity scores, but this
|
|
425
|
+
toolkit doesn't require it and doesn't use it.
|
|
426
|
+
|
|
427
|
+
**How is this different from paid ASO tools?**
|
|
428
|
+
Paid tools add Apple Search Ads popularity scores, historical rank tracking,
|
|
429
|
+
and dashboards. This gives you the per-storefront keyword discovery, competitor
|
|
430
|
+
analysis, and metadata publishing that most of the workflow actually consists
|
|
431
|
+
of — scriptable, inspectable, and free. Many people need the second thing far
|
|
432
|
+
more often than the first.
|
|
433
|
+
|
|
434
|
+
**Does it work for Google Play?**
|
|
435
|
+
Not currently. Everything here is App Store specific: Apple's endpoints, the
|
|
436
|
+
30/30/100 character model, and the App Store Connect API.
|
|
437
|
+
|
|
438
|
+
**Will Apple block this?**
|
|
439
|
+
These endpoints are public and used by the App Store itself. The toolkit sets a
|
|
440
|
+
descriptive user agent, spaces requests to respect the iTunes Search API's
|
|
441
|
+
roughly 20-requests-per-minute limit, and caches everything so repeat runs make
|
|
442
|
+
almost no requests. Be reasonable and you'll be fine.
|
|
443
|
+
|
|
444
|
+
**Can I use it in CI?**
|
|
445
|
+
Yes. Everything is non-interactive with meaningful exit codes —
|
|
446
|
+
`asokit metadata check` fails the build on a validation error, which makes a
|
|
447
|
+
good pre-submission gate.
|
|
448
|
+
|
|
449
|
+
**What Python version do I need?**
|
|
450
|
+
3.9 or newer, tested on 3.9, 3.11 and 3.13. Research has no dependencies at
|
|
451
|
+
all; only App Store Connect sync adds `pyjwt` and `cryptography`.
|
|
452
|
+
|
|
453
|
+
**Why isn't it on PyPI?**
|
|
454
|
+
It will be. Until then `pip install git+https://github.com/ggapp1/aso-keyword-toolkit`
|
|
455
|
+
installs the same package and gives you the `asokit` command.
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## How it works
|
|
460
|
+
|
|
461
|
+
| source | what it provides | cost |
|
|
462
|
+
|--------|------------------|------|
|
|
463
|
+
| App Store autocomplete (`MZSearchHints`) | Apple's per-storefront suggestion ranking | free, no auth |
|
|
464
|
+
| iTunes Search API | ranked apps per query per country, with rating counts | free, official |
|
|
465
|
+
| App Store Connect API | reading and writing localized metadata | free, your own key |
|
|
466
|
+
|
|
467
|
+
Nothing is proxied through a third party. Every request goes from your machine
|
|
468
|
+
to Apple.
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Development
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
git clone https://github.com/ggapp1/aso-keyword-toolkit
|
|
476
|
+
cd aso-keyword-toolkit
|
|
477
|
+
pip install -e .
|
|
478
|
+
python3 -m unittest discover -s tests -t .
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
The codebase is small and deliberately boring: `storefronts` (the country
|
|
482
|
+
table), `sources` (HTTP and caching), `research` (expansion and scoring),
|
|
483
|
+
`metadata` (pure validation, no I/O), `asc` (App Store Connect), `report`
|
|
484
|
+
(rendering), `suggest` (seed derivation).
|
|
485
|
+
|
|
486
|
+
Contributions welcome — especially additional storefront verifications,
|
|
487
|
+
stopwords for languages not yet covered, and app-name punctuation conventions
|
|
488
|
+
from stores you know better than I do.
|
|
489
|
+
|
|
490
|
+
## License
|
|
491
|
+
|
|
492
|
+
MIT. See [LICENSE](LICENSE).
|
|
493
|
+
|
|
494
|
+
Not affiliated with, endorsed by, or connected to Apple Inc. "App Store" and
|
|
495
|
+
"App Store Connect" are trademarks of Apple Inc.
|