ASO keywords → metadata rewrite
Researches App Store / Google Play keywords for one app with live AppGoat data (current ranks, competitor keyword gaps, popularity, difficulty, relevance) and rewrites the title, subtitle, keyword field and description within the store character limits.
Finds the search terms an app can realistically win and rewrites its metadata to target them, staying inside every store limit. The output is ready to paste into App Store Connect or Play Console.
Install
The appgoat/skills repository is private until its public release, so the plugin and git commands below are coming soon. Today, run the playbooks as prompts of the AppGoat MCP server (Claude, Cursor, ChatGPT) or download the SKILL.md from this page.
Install the AppGoat plugin (all playbooks plus the AppGoat MCP server), then run /mcp to sign in.
/plugin marketplace add appgoat/skills
/plugin install appgoat-playbooks@appgoatRun it by asking for it in plain words, or by name:
/aso-keywords-metadata-rewrite <app> [country]Add the connector: Settings → Connectors → Add custom connector → https://api.appgoat.ai/mcp.
Pick aso-keywords-metadata-rewrite from the “+” menu → AppGoat (it is served as a prompt), or upload the skill folder as a zip in Settings → Capabilities → Skills.
Add the MCP server to .cursor/mcp.json:
{ "mcpServers": { "appgoat": { "url": "https://api.appgoat.ai/mcp" } } }Type / in the chat and pick aso-keywords-metadata-rewrite, or save the SKILL.md as .cursor/rules/aso-keywords-metadata-rewrite.mdc.
In developer mode (Settings → Apps & Connectors → Advanced), add a connector with https://api.appgoat.ai/mcp and sign in. Then ask:
Use the AppGoat list_playbooks tool and run the aso-keywords-metadata-rewrite playbook.Copy the skill folder into your agent’s skills directory and add the MCP server:
git clone https://github.com/appgoat/skills /tmp/appgoat-skills
cp -r /tmp/appgoat-skills/skills/aso-keywords-metadata-rewrite ~/.claude/skills/
claude mcp add --transport http appgoat https://api.appgoat.ai/mcpWhen to use
- The user wants new metadata or keyword picks for an existing app (or a pre-launch app with a draft listing).
- After
app-audit-liteflagged title, subtitle or keyword problems. - Not for review analysis or idea hunting.
Inputs
app(required): store URL, Apple id, Play package, bundle id or name. For an unreleased app, ask for the draft title, subtitle and description and the closest competitor instead.country(optional, defaultus): the storefront to optimize. Metadata is per locale; run once per country.
Ask the user for the brand name to keep (default: the part of the current title before ":" or "-") and anything that must not change.
Steps
Costs in AppGoat credits (see the footer of each tool result). Without the MCP, use the REST equivalents in the REST calls below.
get_app{app, country} - 1 (+1 if refreshed). Note title, subtitle (Play: short description), description, category, languages.get_app_keywords{app, country, limit: 200} - 1. Current ranked terms with rank, popularity, difficulty.get_competitors{app, country, limit: 8} - 1.get_app_keywords{app: <competitor app_id>, store, country, max_rank: 20, limit: 100} for the top 5 competitors - 5. Terms competitors rank top 20 for are your candidate pool.- Build a candidate list of ≤ 50 terms: competitor terms the app does not rank top 20 for, plus
current terms ranked 11–50, plus 3–5 seeds you derive from the app's core job.
keyword_research{terms: 5 seeds, store, country, include: ["related"]} - 5 per seed, ~25. keyword_research{terms: ≤ 25 candidates, store, country, app} - 1 per keyword, twice for 50 candidates - ~50. Returns popularity, difficulty, the app's rank andrelevance(0–1: 1 = in title, 0.7 = subtitle, 0.4 = description) per term. Re-callpendingterms after a minute.- Write the metadata (no calls), then validate it with
python3 scripts/check_metadata.py --store <apple|play> --title "…" --subtitle "…" --keywords "…"(Play:--short "…" --full-file description.txt). Fix every error it reports before answering. - Optional:
track_keywords{terms: the 10–20 chosen terms, store, countries: [country]} - 0 credits, uses keyword slots - so the next run can measure the effect withget_keyword_ranks. Ask before tracking.
Budget: about 40–80 credits per country.
Decision rules
- Candidate filter: popularity ≥ 20; drop competitor brand names and terms that misdescribe the app.
- Pick score:
popularity × fit × (1 − difficulty/100) × gap, wherefit= 1 if the term describes what the app does, 0.5 if adjacent, 0 otherwise (your judgment, notrelevance), andgap= 1.5 if the app ranks 11–50 (striking distance), 1.2 if it ranks 51+ or not at all and difficulty ≤ 50, 1.0 if already top 10 (keep, do not chase), 0.6 if difficulty > 75. - Placement (most weight first): title > subtitle > keyword field (Apple) / short description (Play) > description. Put the top 1–2 terms in the title next to the brand, the next 2–3 in the subtitle.
- Keep what wins: never remove a term the app ranks top 10 for with popularity ≥ 30 from the field that holds it, unless the user agrees to the trade.
- Apple limits: title ≤ 30 characters, subtitle ≤ 30, keyword field ≤ 100 bytes, comma separated without spaces after commas, no word repeated from the title or subtitle (Apple combines them), no plurals of words already present, no competitor brands, no "app" or "free".
- Play limits: title ≤ 30, short description ≤ 80, full description ≤ 4000. Play indexes the description: use each top term 2–4 times in natural sentences, never a keyword list.
- Style: readable title first, keywords second. No ALL CAPS, no emoji in the title, no claims ("#1", "best") the user cannot back up.
- Give 2 variants for title + subtitle (safe and aggressive) and one keyword field.
Output format
# ASO rewrite: <app> (<store>, <country>) - <date>
## Target keywords
| Term | Popularity | Difficulty | Rank now | Relevance now | Score | Placed in |
|---|---|---|---|---|---|---|
| habit tracker | 48 | 62 | 23 | 0.4 | 27.4 | title |
## Metadata
| Field | Current | Proposed (A, safe) | Proposed (B, aggressive) |
|---|---|---|---|
| Title (≤ 30) | Streaks (7) | Streaks: Habit Tracker (22) | … |
| Subtitle (≤ 30) | … | … | … |
| Keyword field (≤ 100 bytes) | n/a | routine,daily,goals,… (97) | |
## Description
<rewritten description, or the first 3 lines for Apple, which is what users see>
## Why
- <term> moves from the description (0.4) to the title (1.0); you rank 23 with popularity 48.
- Dropped <term>: popularity 8.
## Measure it
Track these terms (`track_keywords`) and compare ranks 2 and 4 weeks after the release.
_check_metadata.py: OK · Credits used: <sum>._
Automate it
Re-run after every competitor metadata change, or monthly.
GitHub Action (.github/workflows/appgoat-aso.yml): opens a PR with metadata/<country>.md so the
change is reviewed before you paste it into the store console.
name: AppGoat ASO refresh
on:
schedule:
- cron: "0 7 15 * *"
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
aso:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install AppGoat playbooks
run: |
git clone --depth 1 https://github.com/appgoat/skills /tmp/appgoat-skills
mkdir -p .claude/skills
cp -r /tmp/appgoat-skills/skills/* .claude/skills/
- uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ github.token }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Use the aso-keywords-metadata-rewrite skill for <app link>, country us. Do not call track_keywords.
Write the result to metadata/us.md, commit it on a new branch and open a pull request with gh pr create.
claude_args: |
--mcp-config '{"mcpServers":{"appgoat":{"command":"npx","args":["-y","@appgoat/mcp"],"env":{"APPGOAT_API_KEY":"${{ secrets.APPGOAT_API_KEY }}"}}}}'
--allowedTools mcp__appgoat__get_app,mcp__appgoat__get_app_keywords,mcp__appgoat__get_competitors,mcp__appgoat__keyword_research,mcp__appgoat__get_job,Write,Bash(python3 .claude/skills/aso-keywords-metadata-rewrite/scripts/check_metadata.py:*),Bash(git:*),Bash(gh pr create:*)
Claude Code routine:
/schedule monthly on the 15th at 09:00 - Run the aso-keywords-metadata-rewrite playbook for <app link> in us. Compare the ranks of our tracked keywords (list_tracked_keywords, get_keyword_ranks) with last month and propose a new title/subtitle/keyword field only if a target term lost ≥ 5 positions or a competitor took it.
Without MCP: the REST calls
Without the AppGoat MCP server, call the API directly. Base URL https://api.appgoat.ai/v1,
header x-api-key: $APPGOAT_API_KEY (create a key at https://dash.appgoat.ai). Responses are
{ "data": …, "meta": { "credits_used", "credits_remaining", … } }; errors carry error.hint.
| MCP tool | REST call |
|---|---|
get_app | GET /apps/{app}?country=us |
get_app_keywords | GET /apps/{app}/keywords?country=us&limit=200 (competitors: &max_rank=20) |
get_competitors | GET /apps/{app}/competitors?country=us&limit=8 |
keyword_research (expand) | GET /keywords?qs=seed1,seed2&store=apple&country=us&include=related (≤ 5 seeds) |
keyword_research (score) | GET /keywords?qs=t1,t2,…&store=apple&country=us&app={app} (≤ 25 terms) |
track_keywords | POST /tracked-keywords {"terms":[…],"store":"apple","countries":["us"]} (0 credits) |
list_tracked_keywords | GET /tracked-keywords?store=apple&country=us |
get_keyword_ranks | GET /tracked-keywords/{id}/ranks?app={app_id} |
curl -s -G -H "x-api-key: $APPGOAT_API_KEY" https://api.appgoat.ai/v1/keywords \
--data-urlencode "qs=habit tracker,daily routine" -d store=apple -d country=us -d app=284882215
More playbooks
/app-audit-lite~25–40 credits a runApp audit (lite)
Runs a quick App Store / Google Play audit of one app with live AppGoat data – listing quality, ratings trend, recent complaints, keyword rankings, competitors and their recent changes – and ends with a prioritized fix list.
/find-app-ideas~150–250 credits a runFind app ideas
Finds underserved App Store / Google Play niches with live AppGoat data – search demand (keyword popularity) against weak supply (low ratings, abandoned incumbents, few strong apps) – and returns a ranked shortlist of app ideas with evidence and user pain points.
/competitor-digest~15–55 credits a runCompetitor digest
Writes a weekly competitor digest for one App Store / Google Play app from an AppGoat competitor watch – what each competitor changed (title, subtitle, description, screenshots, icon), price and in-app purchase changes, new versions with release-note summaries, rating and chart moves, keyword gains and losses against the user's app, keyword gaps and review themes – and ends with 3 recommended actions linked to other playbooks. Sets up or reuses the competitor_set watch.
/reviews-to-github-issues~2–10 credits a runReviews → GitHub issues
Turns recent negative App Store / Google Play reviews into deduplicated GitHub issues – one issue per problem topic, with linked and quoted reviews, new reviews added as comments to the existing issue instead of duplicates. Uses AppGoat review data and the gh CLI (or the GitHub MCP server).
/reply-to-every-review~1–3 + 1 per posted reply credits a runReply to every review
Drafts a reply to every unanswered App Store and Google Play review of the user's own app, in the app's voice, through their connected App Store Connect / Play Console account – each reply is a dry run with a before/after diff and AppGoat guardrail scores (policy, tone, claims), and only the replies the user approves are posted.
/apple-ads-optimizer~5–8 per campaign + 1 per applied change credits a runApple Ads optimizer
Optimizes the user's Apple Ads (Search Ads) campaigns through their connected Apple Ads account – reads the last 14 days of keyword and search term reports, raises bids on keywords that convert under the target CPA (capped +30% per run), lowers or pauses keywords that spend without installs, adds converting search terms as exact keywords and wasteful ones as negatives – every change a dry run with a diff the user approves.
/localization-push~35–60 per locale + 1 per applied change credits a runLocalization push
Localizes the user's own App Store / Google Play listing into new languages through their connected App Store Connect or Play Console account – picks target locales from markets with demand, researches the keywords people actually search in each country with AppGoat, writes a localized name, subtitle, keyword field and promotional text (Play title, short and full description) within the store limits, and pushes each locale as a dry run with a before/after diff the user approves.