Apple App Store ASO
When to use this skill
Use whenever the user is preparing or revising App Store Connect metadata for an Apple platform app. Typical triggers:
- "Help me with App Store keywords / subtitle / description"
- "Review my App Store listing"
- "My release notes are too long"
- "Localize the metadata for <locale>"
- "Validate this metadata against Apple's limits"
- "Analyze what my competitors are doing" / "look at competitor X"
Do not use for Google Play Store — character limits, indexing rules, and best practices differ.
Core workflow
-
Gather context. Ask only what you cannot infer:
- App purpose, primary user job, top 3 differentiators
- Target locale(s) — default
en-USif unspecified - Existing metadata if revising (paste-in or file path)
- Any constraints (brand tone, trademarked terms to avoid, must-include keywords)
-
Load the playbook. Read
references/aso-playbook.mdbefore drafting copy. It is the source of truth for current Apple ranking signals (June 2025 algorithm update included). -
Draft metadata. Produce a candidate set for every field the user asked for. Follow the output format below — always show character counts inline as
(X/LIMIT). -
Validate. Run
scripts/validate_metadata.pyagainst the draft. It is non-interactive when given flags; prefer flag mode inside an automated workflow:python3 scripts/validate_metadata.py \ --app-name "..." --subtitle "..." --keywords "..." \ --promotional-text "..." --description "..." --whats-new "..."If any field fails, revise that field only and re-validate. Do not silently truncate — surface the overflow to the user.
-
Localize on request. When generating for a non-
en-USlocale, follow the localization rules inreferences/aso-playbook.md(keyword field is per-locale, descriptions should be culturally adapted not literally translated, App Store Connect treats each locale independently). -
Pull competitor context when relevant. Whenever the user asks about competitors, or when drafting metadata for a competitive category and you don't have competitor context yet, fetch real listings before opining. Use
scripts/fetch_listing.py— see the Competitive analysis section for the workflow. Do not invent competitor data; do not refuse for lack of context; just go fetch it.
Hard character limits (Apple App Store Connect)
| Field | Limit | Notes |
|---|---|---|
| App Name | 30 | Indexed for search. Highest ranking weight. |
| Subtitle | 30 | Indexed for search. Per-locale. |
| Keywords | 100 | Indexed for search. Comma-separated, no spaces. |
| Promotional Text | 170 | Not indexed. Editable without resubmit. |
| Description | 4000 | Not indexed since iOS 11. Optimize for conversion. |
| What's New | 4000 | Release notes. Per-version. Per-locale. |
| In-App Purchase | 30 | Display name. |
Authoritative limits are mirrored in references/apple-limits.md.
Output format
Always present generated metadata in this exact shape so it is paste-ready into App Store Connect:
### App Name (X/30)
<text>
### Subtitle (X/30)
<text>
### Keywords (X/100)
<comma,separated,no,spaces>
### Promotional Text (X/170)
<text>
### Description (X/4000)
<text>
### What's New (X/4000)
<text>
After the metadata block, include a Validation section with the output of validate_metadata.py and a Rationale section explaining keyword choices and the positioning angle (1-3 short bullets).
Competitive analysis
When the user says "analyze what my competitors are doing" (or anything in that shape), the expectation is: find them, fetch them, report. Don't ask the user to supply the competitor list unless you genuinely cannot infer one. Don't fabricate numbers or fields.
Workflow
-
Identify candidates. In order of preference:
- If the user named specific apps or pasted App Store URLs → use those.
- Else if you already have context on the user's app (name, category, primary keyword from the conversation) → run a search:
Pairpython3 scripts/fetch_listing.py --search "<primary keyword>" --country <cc> --limit 10 --field trackId python3 scripts/fetch_listing.py --search "<primary keyword>" --country <cc> --limit 10 --field trackNametrackIdwithtrackNameto pick 3–5 sensible competitors. Skip the user's own app if it shows up. - Else (no context at all) → ask the user one question: "what's your app's primary keyword or category?" Then proceed as above.
-
Fetch their listings in one batch.
python3 scripts/fetch_listing.py --id <id1>,<id2>,<id3>,<id4>,<id5> --country <cc>Use the same
--countryas the user's target market. Defaultusif unspecified. -
Read the JSON and report. Use the Competitive analysis report format below.
-
Be explicit about what you don't see. Subtitle, Keywords field, and Promotional Text are not in the public API. Do not infer them. Do not invent them. If the user wants to know a competitor's subtitle, the only way is to open the App Store URL (
trackViewUrlin the JSON) and read it. Say so.
Call modes (reference)
# By App Store URL (any locale — the script extracts the numeric ID)
python3 scripts/fetch_listing.py --url 'https://apps.apple.com/us/app/threads/id6446901002'
# By numeric ID(s), comma-separated — single batch lookup
python3 scripts/fetch_listing.py --id 6446901002,544007664 --country us
# By bundle identifier
python3 scripts/fetch_listing.py --bundle-id com.burbn.barcelona
# Free-text search
python3 scripts/fetch_listing.py --search "pomodoro timer" --country us --limit 10
# Extract a single field across all results
python3 scripts/fetch_listing.py --search "pomodoro" --country it --limit 5 --field trackName
# Dump every field Apple returns (for debugging or deep inspection)
python3 scripts/fetch_listing.py --id 6446901002 --raw
Add --country xx for non-US App Store (e.g., it, de, ja, fr).
What the API returns
For each app: trackName, sellerName, primaryGenreName + genres, averageUserRating + userRatingCount, description (full published text), releaseNotes (latest "What's New"), version, releaseDate, currentVersionReleaseDate, languageCodesISO2A (supported locales), contentAdvisoryRating, price + formattedPrice + currency, screenshotUrls, ipadScreenshotUrls, bundleId, trackId, trackViewUrl, minimumOsVersion.
What the API does NOT return
- Subtitle — Apple does not expose it in the public API.
- Keywords field — never exposed publicly.
- Promotional Text — never exposed publicly.
- Install counts, keyword volume, ranking position, share of voice — these require paid third-party services (AppFollow, Sensor Tower, AppTweak, Mobile Action) or Apple's private App Store Connect API (own app only). Out of scope for this skill — do not estimate, do not invent, just name the limit and move on.
Competitive analysis report format
For each competitor, present:
### <trackName> by <sellerName>
- Category: <primaryGenreName> (+ secondary: <genres minus primary>)
- Rating: <averageUserRating> (<userRatingCount> ratings)
- Price: <formattedPrice>
- Last update: <currentVersionReleaseDate> — version <version>
- Locales: <count> (<comma-separated list, or "EN + N more" if long>)
- Positioning angle: <one-sentence summary inferred from the first 3 lines of description>
- Description hook (first 200 chars):
<quo