Skip to content

Instantly share code, notes, and snippets.

@mirzemehdi
Created January 8, 2026 19:52
Show Gist options
  • Select an option

  • Save mirzemehdi/c9e11a320277a841399421c8220cce61 to your computer and use it in GitHub Desktop.

Select an option

Save mirzemehdi/c9e11a320277a841399421c8220cce61 to your computer and use it in GitHub Desktop.
Automatically generates App Store and Google Play metadata (title, subtitle, keywords, description) for multiple locales using OpenAI. Accepts an app idea or PRD file and optionally target keywords, following real ASO best practices. Outputs files for iOS and Android, with parallel generation and locale support.
#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# ASO Metadata Generator (Named Arguments)
# ============================================================
# ----------------------------
# CONFIG
# ----------------------------
OPENAI_API_KEY="" # set your OpenAI key
MODEL="gpt-4"
OUTPUT_DIRECTORY="./store_metadata"
DEFAULT_LOCALES=("en-US")
DEFAULT_STORE="both"
# ----------------------------
# VARIABLES
# ----------------------------
STORE="$DEFAULT_STORE"
KEYWORDS=""
IDEA_TEXT=""
IDEA_FILE=""
LOCALES_INPUT=""
# ----------------------------
# HELP
# ----------------------------
usage() {
cat <<EOF
Usage:
./generate_aso_metadata.sh --keywords "<keywords>" (--idea "<text>" | --idea-file <file>) [--locales "<l1,l2>"]
Required:
--idea "<text>" OR --idea-file <file>
Options:
--keywords Comma-separated ASO target keywords that you want to be ranked for
--idea Short app idea or description
--store ios|android|both (default: both)
--idea-file Path to PRD / idea document (markdown or text)
--locales Comma-separated locales (default: ${DEFAULT_LOCALES[*]})
-h, --help Show this help
Examples:
./generate_aso_metadata.sh --idea "AI manga translator"
./generate_aso_metadata.sh --idea-file prd.md --store ios
./generate_aso_metadata.sh --idea "OCR scanner" --keywords "document scanner,ocr app" --locales "en-US,es-ES"
EOF
exit 1
}
# ----------------------------
# ARGUMENT PARSING
# ----------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
--keywords)
KEYWORDS="$2"
shift 2
;;
--idea)
IDEA_TEXT="$2"
shift 2
;;
--idea-file)
IDEA_FILE="$2"
shift 2
;;
--locales)
LOCALES_INPUT="$2"
shift 2
;;
--store)
STORE="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "❌ Unknown argument: $1"
usage
;;
esac
done
# ----------------------------
# VALIDATION
# ----------------------------
if [[ -z "$IDEA_TEXT" && -z "$IDEA_FILE" ]]; then
echo "❌ One of --idea or --idea-file is required"
usage
fi
if [[ -n "$IDEA_TEXT" && -n "$IDEA_FILE" ]]; then
echo "❌ Use either --idea or --idea-file, not both"
usage
fi
if [[ "$STORE" != "ios" && "$STORE" != "android" && "$STORE" != "both" ]]; then
echo "❌ Invalid --store value"
usage
fi
# ----------------------------
# LOAD IDEA CONTENT
# ----------------------------
if [[ -n "$IDEA_FILE" ]]; then
if [[ ! -f "$IDEA_FILE" ]]; then
echo "❌ Idea file not found: $IDEA_FILE"
exit 1
fi
echo "πŸ“„ Loading app idea from $IDEA_FILE" >&2
APP_IDEA="$(cat "$IDEA_FILE")"
else
APP_IDEA="$IDEA_TEXT"
fi
# ----------------------------
# LOCALES
# ----------------------------
if [[ -z "$LOCALES_INPUT" ]]; then
LOCALES_INPUT=$(IFS=','; echo "${DEFAULT_LOCALES[*]}")
fi
IFS=',' read -ra LOCALES <<< "$LOCALES_INPUT"
# ----------------------------
# PROMPT BUILDER
# ----------------------------
build_ios_prompt() {
local locale="$1"
cat <<EOF
You are a professional iOS App Store ASO expert.
Your goal is to maximize keyword ranking according to Apple's App Store rules.
IMPORTANT ASO FACTS (follow strictly):
- App Name has the HIGHEST ranking weight
- Words earlier (left-most) rank stronger
- Subtitle has the SECOND highest ranking weight
- Keyword field has LOWER weight
- Description has NO ranking impact (conversion only)
APP CONTEXT (this is the source of truth):
$APP_IDEA
TARGET KEYWORDS (OPTIONAL):
${KEYWORDS:-"(not provided – derive from app context)"}
LOCALE:
$locale
TASK:
Generate optimized iOS App Store metadata following these rules:
1. APP NAME (Title)
- Put the SINGLE most important keyword FIRST
- Front-load it (left-most position)
- Can include a short brand or descriptor after
- No fluff, no marketing phrases
- Maximize ranking first, branding second
2. SUBTITLE
- Use 1–2 SECONDARY keywords
- Front-load important words
- Do NOT repeat words from the title
- Keep it concise and descriptive
3. KEYWORDS FIELD
- Comma-separated
- Max 100 characters total
- No spaces
- NO words used in title or subtitle
- Include synonyms, plural/singular, long-tail variations
4. DESCRIPTION
- Written ONLY for conversion
- Explain features and benefits clearly
- No keyword stuffing
OUTPUT FORMAT:
- Return ONLY valid JSON
- No explanations
- No markdown
- Keys exactly: name, subtitle, keywords, description
EOF
}
build_android_prompt() {
local locale="$1"
cat <<EOF
You are a professional Google Play ASO expert.
IMPORTANT GOOGLE PLAY RULES:
- Title has the HIGHEST ranking weight
- Short description has VERY strong ranking impact
- Long description IS indexed
- No keyword field
- Natural keyword usage and density matter
APP CONTEXT:
$APP_IDEA
TARGET KEYWORDS (optional):
${KEYWORDS:-"(not provided – derive from app context)"}
INSTRUCTIONS:
- If keywords are not provided, derive them from app context
- Choose a PRIMARY keyword and several SECONDARY keywords
- Use keywords naturally, not spammy
LOCALE:
$locale
FIELDS:
1. TITLE
- Include PRIMARY keyword
- Place it as early as possible
- Maximize ranking but remain readable
2. SHORT DESCRIPTION
- 80 characters max
- Include PRIMARY + SECONDARY keywords
- Strong value proposition
- Natural language
3. FULL DESCRIPTION
- 3–5 mentions of PRIMARY keyword
- Natural mentions of secondary keywords
- Explain features, benefits, and use cases
- Use short paragraphs or bullet points
- Avoid keyword stuffing
OUTPUT:
Return ONLY valid JSON:
title, short_description, full_description
EOF
}
# ----------------------------
# OPENAI CALL
# ----------------------------
call_openai() {
local prompt="$1"
local payload
payload=$(jq -n \
--arg model "$MODEL" \
--arg content "$prompt" \
'{
model: $model,
messages: [{ role: "user", content: $content }]
}'
)
response=$(curl -s https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d "$payload"
)
if echo "$response" | jq -e '.error' >/dev/null; then
echo "❌ OpenAI error:" >&2
echo "$response" >&2
exit 1
fi
echo "$response" | jq -r '.choices[0].message.content'
}
# ----------------------------
# GENERATE (PARALLEL)
# ----------------------------
PIDS=()
for locale in "${LOCALES[@]}"; do
(
echo "πŸš€ $locale β†’ $STORE" >&2
if [[ "$STORE" == "ios" || "$STORE" == "both" ]]; then
ios_prompt="$(build_ios_prompt "$locale")"
ios_json="$(call_openai "$ios_prompt")"
out="$OUTPUT_DIRECTORY/appstore/$locale"
mkdir -p "$out"
echo "$ios_json" | jq -r '.name' > "$out/name.txt"
echo "$ios_json" | jq -r '.subtitle' > "$out/subtitle.txt"
echo "$ios_json" | jq -r '.keywords' > "$out/keywords.txt"
echo "$ios_json" | jq -r '.description' > "$out/description.txt"
fi
if [[ "$STORE" == "android" || "$STORE" == "both" ]]; then
android_prompt="$(build_android_prompt "$locale")"
android_json="$(call_openai "$android_prompt")"
out="$OUTPUT_DIRECTORY/playstore/$locale"
mkdir -p "$out"
echo "$android_json" | jq -r '.title' > "$out/title.txt"
echo "$android_json" | jq -r '.short_description' > "$out/short_description.txt"
echo "$android_json" | jq -r '.full_description' > "$out/full_description.txt"
fi
echo "βœ… $locale done" >&2
) &
PIDS+=($!)
done
for pid in "${PIDS[@]}"; do
wait "$pid"
done
echo "πŸŽ‰ ASO metadata generated successfully in $OUTPUT_DIRECTORY !"
@mirzemehdi
Copy link
Author

mirzemehdi commented Jan 8, 2026

ASO Metadata Generator (iOS + Google Play)

Automatically generates App Store and Google Play metadata using OpenAI GPT, following ASO best practices.

Features

  • Supports iOS (name, subtitle, keywords, description)
  • Supports Android (title, short description, full description)
  • Multiple locales supported
  • App idea or PRD file as input
  • Optional target keywords (auto-derived if missing)
  • Fastlane-ready output
  • Parallel generation per locale

Usage

# Generate metadata for both stores using an app idea
./generate_aso_metadata.sh --idea "AI manga translator app" --store both --locales "en-US,ja-JP"

# Generate iOS only from PRD file
./generate_aso_metadata.sh --idea-file prd.md --store ios

# Generate Android only with keywords that you want to target
./generate_aso_metadata.sh --idea "OCR scanner app" --keywords "document scanner,ocr" --store android

Arguments

Options:
  --keywords     Comma-separated ASO target keywords that you want to be ranked
  --idea         Short app idea or description
  --store ios|android|both   (default: "both")
  --idea-file    Path to PRD / idea document (markdown or text)
  --locales      Comma-separated locales (default: "en-US")

Output Strucutre

File names are same as what Fastlane uses

store_metadata/
β”œβ”€β”€ ios/
β”‚ └── /
β”‚ β”œβ”€β”€ name.txt
β”‚ β”œβ”€β”€ subtitle.txt
β”‚ β”œβ”€β”€ keywords.txt
β”‚ └── description.txt
└── android/
└── /
β”œβ”€β”€ title.txt
β”œβ”€β”€ short_description.txt
└── full_description.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment