ASO is the task indie developers know they should do and almost never make time for. Keyword research, multi-language metadata, periodic refresh when the app changes — each step is well-defined, repetitive, and deeply unglamorous compared to shipping features. For Storyie, we already had an OpenClaw AI agent running in the codebase handling development tasks. This post is about extending that same setup to cover ASO: using asc CLI as the interface to App Store Connect and the agent as the reasoning layer on top.
TL;DR
- Organic search drives the majority of App Store installs for a zero-ad-budget indie app. ASO is not optional.
ascis a Go CLI for App Store Connect with JSON-first output — well-suited to agent use.- The agent reads the app's locale files directly, so keyword and metadata suggestions reflect what the app actually does.
- A
--dry-rungate before every submission keeps a human in the loop without slowing the draft cycle down. - We automated research, drafting, and scheduling. Final sign-off stays with a person.
Automation layer | Tool | What it does |
|---|---|---|
Metadata fetch |
| Pulls current title / subtitle / keywords / description for all locales |
Keyword research | OpenClaw + web search | Generates candidates from locale files and competitor signals |
Draft generation | OpenClaw | Writes locale-specific copy respecting Apple's character limits |
File structure | OpenClaw | Produces the metadata directory tree asc expects |
Submission preview |
| Shows the diff before anything changes |
Submission |
| Applies after human review |
Monthly review | Cron job | Re-runs the whole cycle, compares against codebase changes |
Why ASO matters for indie apps
The App Store has millions of apps. Research consistently puts the share of installs coming from search at well above 60%. For an app with no ad spend, organic search is the entire acquisition funnel. The frustration is that meaningful ASO involves tasks that feel like admin work:
- Finding keywords with decent volume that large apps haven't already locked up.
- Writing distinct copy for every supported locale — not just translated, but optimized for how users in that market actually search.
- Revisiting all of the above whenever a feature ships.
The third point is the one that almost never happens in practice. You optimize at launch, and then the metadata drifts while the product moves on.
What asc CLI is
asc is an unofficial CLI for the App Store Connect API. Install via Homebrew or the install script:
# Homebrew
brew install asc
# Install script
curl -fsSL https://asccli.sh/install | bashIt covers most of what the App Store Connect web UI can do: TestFlight, builds, submissions, signing, analytics, screenshots, subscriptions. For ASO the relevant surface is metadata and localizations — fetching the current state, and updating it.
The design characteristic that makes it useful for agent workflows: in non-interactive contexts (pipes, CI) it defaults to JSON output. No screen-scraping required.
# Explicit JSON output
asc apps list --output jsonSet up authentication once with your App Store Connect API key:
# App Store Connect > Users and Access > Integrations > API Keys
# Key type: Individual, Role: App Manager or higher
asc auth login \
--name "Storyie" \
--key-id "XXXXXXXXXX" \
--issuer-id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--private-key ./AuthKey_XXXXXXXXXX.p8After that, asc apps list just works, and the agent can call any asc command without re-authenticating.
Architecture: agent + CLI
The setup is deliberately simple:
┌────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ OpenClaw │────▶│ asc CLI │────▶│ App Store │
│ AI Agent │ │ (asc commands) │ │ Connect │
│ │◀────│ │◀────│ (metadata) │
└────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌────────────────┐
│ Codebase │
│ (locales/*) │
└────────────────┘The part that makes this better than a generic ASO tool: the agent can read the codebase. It sees the actual feature set, the terminology used in-product, and the in-app translations — all before writing a single word of store copy.
Keyword research without a paid tool
The standard approach to ASO keyword research is subscribing to App Annie or Sensor Tower at somewhere between $100 and $500 a month. That is not a realistic line item for an indie project.
The alternative: generate candidates from the product itself.
Grounding candidates in the codebase
OpenClaw can read the app's locale files directly. Storyie's English locale lives in apps/expo/locales/en.ts:
// apps/expo/locales/en.ts
export default {
translation: {
// biometric auth, theme settings, export, AI features...
// Each feature name is a keyword candidate
},
};Asking the agent to produce 50 keyword candidates from the feature list takes seconds and yields copy that is actually grounded in what the app does — not generic "journal app" phrasing that could describe any of hundreds of competitors.
Estimating volume without an ASO subscription
asc only covers your own app's data — it doesn't expose category rankings or competitor metadata. To layer in search volume signals, the agent uses web search to pull publicly available competitor descriptions and App Store category information. This is imprecise compared to Sensor Tower, but combined with the codebase-grounded candidates it produces a useful ranked list, especially for long-tail terms where competition is lower and precision matters less.
Managing metadata across 10 locales
Storyie supports 10 languages:
// app.config.ts
infoPlist: {
CFBundleLocalizations: [
"en", "ja", "zh", "es", "ar", "hi", "pt", "ru", "de", "fr",
],
}That means 10 sets of title, subtitle, keywords, and description — all with Apple's character constraints (30 / 30 / 100 / 4000 respectively).
Pull the current state first
Before generating anything, the agent fetches what is already live:
# All localizations for the app
asc localizations list --app "YOUR_APP_ID"
# Full app info as JSON
asc app-info get --app "YOUR_APP_ID" --output json --prettyThis gives the agent the baseline to diff against — what has changed in the codebase since the last update, and which locales are out of date.
Localization, not just translation
The most common mistake in multi-locale ASO is running the English copy through a translation API and calling it done. The keyword field in particular needs to reflect how users in that market actually search, which is often different from a literal translation.
In Japanese, both "日記アプリ" (diary app) and "ジャーナル" (journal) see meaningful search volume. In English, "journal" outperforms "diary" by a wide margin. The agent prompt for localization makes this explicit:
Task: Optimize the following App Store metadata for {locale}.
Constraints:
- Use keywords that users in this market actually search for, not literal translations
- Title: max 30 characters
- Subtitle: max 30 characters
- Keywords field: max 100 characters, comma-separated
- Description: lead with the user's problem, then the solution — not a feature list
Current app features:
{contents of locales/en.ts}Consistency with in-app language
Because the agent reads the locale files before writing store copy, it can flag mismatches automatically. If en.ts uses "Theme Settings" and a metadata draft says "Customization," the agent catches and corrects that before it reaches the dry-run step. This used to require a manual cross-reference; now it's implicit.
Updating metadata with asc CLI
Once the agent has generated copy it is satisfied with, it structures the output as the directory tree that asc release run --metadata-dir expects:
metadata/
├── en-US/
│ ├── name.txt
│ ├── subtitle.txt
│ ├── keywords.txt
│ └── description.txt
├── ja/
│ ├── name.txt
│ └── ...
├── zh-Hans/
│ └── ...
└── ... (10 locales)Dry-run before every submission
This is the gate that keeps the automation safe:
# Preview exactly what would change
asc release run \
--app "YOUR_APP_ID" \
--version "1.2.3" \
--build "BUILD_ID" \
--metadata-dir "./metadata/version/1.2.3" \
--dry-run
# Submit after human review
asc release run \
--app "YOUR_APP_ID" \
--version "1.2.3" \
--build "BUILD_ID" \
--metadata-dir "./metadata/version/1.2.3" \
--confirmThe dry-run output shows every field that would change, per locale. A human reviews that diff before the confirm command runs. If anything looks wrong — a keyword that slipped in from the wrong locale, a description that is technically accurate but tonally off — it gets caught here without ever touching App Store Connect.
Checking submission status
asc status --app "YOUR_APP_ID"Screenshots: still manual
asc can list existing screenshots and video previews:
asc screenshots list --app "YOUR_APP_ID"
asc video-previews list --app "YOUR_APP_ID"We use this to audit coverage — which locales are missing screenshots for a given device class. But generating the screenshots themselves is still manual. Automated screenshot tools haven't reached a quality bar where we would trust them to represent the app in the store without human review of every frame.
Monthly review via cron
An OpenClaw cron job runs once a month and performs the full cycle:
Monthly ASO cycle:
1. asc localizations list → fetch current metadata for all locales
2. Compare against codebase locales/* → detect new features or changed terminology
3. If changes found → generate updated metadata draft
4. Send draft for human review before any submissionThe trigger for a review is not the calendar — it is code changes. If no new features shipped and no terminology changed, the agent may find nothing to update. If a significant feature landed, the agent notices the gap between the codebase and the live store copy and surfaces a specific draft rather than a generic reminder.
What we decided not to automate
We did not aim for full automation. The deliberate omissions:
- Screenshots — The most persuasive element in a store listing. Automated generation is not there yet.
- Final submission — The
--dry-rungate is cheap and prevents an entire class of hard-to-reverse mistakes. The cost of keeping a human in the loop at this step is negligible. - A/B testing — Apple's Product Page Optimization already provides this capability natively. No reason to build a parallel system.
The division that emerged: agent handles research, drafting, structuring, and scheduling; a human handles final judgment on what ships.
What we learned
asc and AI agents are a natural fit. The CLI is non-interactive, JSON-first, and covers the full App Store Connect surface. There is no screen-scraping, no browser automation, no session management. The agent calls a command, gets structured data back, reasons about it, and calls the next command. That loop is easy to build and easy to audit.
Localization ROI is higher than it looks. Ten locales sounds like ten times the work. With an agent doing the drafting, the marginal cost per additional locale is close to zero. More locales means more keywords indexed across more markets — the total addressable keyword surface grows roughly linearly with the number of supported locales.
Long-tail keywords are where indie apps win. Competing on "diary app" or "journal" against apps with large review counts and established ranking is a losing proposition. The agent is good at generating large lists of specific candidates — "AI journal," "private diary with biometrics," "daily reflection app" — and those long-tail terms are where organic discovery actually happens for a smaller app.
ASO is a maintenance task, not a one-time setup. Shipping a new feature without updating the store description means users who would have found the app via that feature's keywords never see it. Wiring the review cycle to codebase changes rather than a fixed calendar makes it much more likely that metadata stays current.
Related Posts
- Building a Cross-Platform Mobile App with Expo — Storyie's Expo architecture and the full mobile stack
- Cross-platform Lexical with
use dom: monorepo gains and the bridges you still own — how the Lexical editor ships across web and mobile
Try Storyie
Storyie is available at storyie.com on the web and on the App Store for iOS. The ASO work described here is live — if you found the app through search, this pipeline had something to do with it.