For years I barely touched the App Store "promotional text" field. It is the 170-character line that sits above your description, and it is one of the few areas you can swap without going through app review. It is made for seasonal notes, sales, and feature announcements, yet I had several apps still running their original launch copy from nearly ten years ago.
I have been building apps solo since 2014, mostly wallpapers and calming utilities, and they have reached around 50 million downloads in total. Even so, I had never run this 170-character slot with any care. I spent a month running a small Gemini API loop to draft candidates weekly, so here are my honest notes, kept free of the usual polish.
Why I started with promotional text, not the description
The description body requires waiting for the next update's review whenever you change it. Promotional text, by contrast, can be swapped instantly inside App Store Connect. So the slot suited for a "refresh it weekly" rhythm was clearly this one.
At first I tried to have the AI rewrite the whole description. But once review round-trips enter the picture, the weekly rhythm falls apart. Starting from a slot with near-zero swap cost was simply more convenient for testing how to split work between AI and myself. My grandfathers were temple carpenters, and thinking of them, the habit of starting with the part you can safely shave and then watching what happens feels natural to me.
The minimal weekly pipeline
The setup is almost embarrassingly simple. For each app I pass one line of "category, key features, this week's context (season, any sale)," and have Gemini produce three candidates within the character limit. Then I always pick one with my own eyes.
import os
from google import genai
client = genai.Client(api_key=os.environ["YOUR_GEMINI_API_KEY"])
def draft_promo_texts(app_name: str, category: str, features: str, context: str) -> str:
prompt = f"""You are an App Store copywriter.
Write three promotional text candidates for the app below.
# Constraints
- Each candidate stays under 120 characters (safely short of the 170 limit)
- No hype, minimal emoji, no unfounded superlatives like "No.1"
- Weave in exactly one piece of this week's context, naturally
- Vary the tone across the three (plain / seasonal / feature-led)
# App info
Name: {app_name}
Category: {category}
Key features: {features}
This week's context: {context}
"""
res = client.models.generate_content(
model="gemini-2.5-flash",
contents=prompt,
)
return res.text
if __name__ == "__main__":
print(draft_promo_texts(
app_name="Shizuku Wallpapers",
category="Wallpapers & Customization",
features="high-resolution nature wallpapers, dark-mode sync, weekly features",
context="Start of the rainy season; a rain wallpaper feature ships this week",
))Two things matter here. First, I deliberately cap the length well under the real limit. When I let Gemini aim for the very edge, it sometimes cut off mid-sentence. Erring short means fewer fixes after I pick one.
Second, I explicitly say "exactly one piece of context." Pass several, and the candidates get greedy and turn into a hard-to-read everything-bagel of a sentence.
What I handed to AI, and what I always touched
After a month, the division of labor became quite clear.
The initial burst of candidates is entirely AI's job. Hand-writing copy for 40 apps eats a whole weekend by itself. Having Gemini produce three drafts each gets me a starting point in minutes. That genuinely helped.
On the other hand, the final pick and the tuning of proper nouns and seasonal phrasing always passed through my hands. "Start of the rainy season," for example, does not land the same way in every release region. In the English version, a literal translation reads oddly. Gemini will swap the term for "rainy season," but whether it resonates culturally is a separate question, and I felt that was risky to leave unchecked.
# After picking one candidate, reduce the human final check to a small gate
def sanity_check(text: str) -> list[str]:
issues = []
if len(text) > 120:
issues.append("over 120 chars; risk of truncation")
for ng in ["No.1", "world's best", "guaranteed", "lose weight fast"]:
if ng.lower() in text.lower():
issues.append(f"possible unfounded superlative/hype: {ng}")
if text.count("!") >= 3:
issues.append("too many exclamation marks")
return issuesThis sanity_check is not a flashy feature. But running 40 apps weekly, the AI occasionally slips in a baseless superlative like "the No.1 feel." That is also a review risk, so I let the machine reject what it can, and read the rest by hand. That boundary was the difference between sustainable and not.
What moved in a month, and what I honestly could not tell
Let me be honest about the numbers. Isolating the effect of changing only the promotional text from everything else is hard. In the same week I might also be touching keywords, or a seasonal factor might be riding along.
Still, in the week I announced the rain wallpaper feature, the product-page-view-to-download conversion for those apps moved more pleasantly than usual. Whether that was the 170 characters or the appeal of the feature itself, I cannot fully separate. "Feels like it worked, but I cannot assert it" is my honest one-month conclusion.
What I can state clearly is that operating cost dropped sharply. Where I used to leave descriptions untouched because editing felt like a chore, simply having a draft appear weekly nearly erased the psychological barrier. Turning it into something I can keep doing may have been the biggest gain.
Three small traps I hit in a month
There were no dramatic failures, but a few quiet traps that mattered.
The first is that Gemini occasionally miscounts the width of letters and symbols. Even when I ask for "under 120 characters," the felt length drifts once full-width and half-width characters mix. That is exactly why I kept a step that re-measures with len() after I pick, instead of trusting the generation constraint alone. A prompt instruction is a request, not a guarantee — an obvious truth I kept rediscovering across 40 apps.
The second is that passing too much seasonal context makes the candidates converge. In the week I handed over "rainy season, new feature, weekend sale," all three drafts came out with nearly the same skeleton. Ask for varied tone all you want; when the information is dense, the differences vanish. The more I narrowed context to one item, the wider the three drafts spread, and the fun of choosing came back.
The third is the pull to just pick the AI's first option. Even though I generate three, when the first is safe enough, my thinking tends to stop there. Once I made a point of reading all three aloud and comparing, the second or third often hugged the season more closely. Choosing really is the human's job, and I keep reminding myself of that.
What I want to try next
Next I plan to collect a month of picked promotional texts alongside actual conversion figures, and have Gemini write a retrospective: which tone tended to get picked, whether there is any loose correlation between the picked draft and conversion, summarized so a human can read it. I make the judgments myself, but I want to borrow the AI's initial burst for the review step too, extending the same split I used here.
If you are also running store operations for many apps alone, I would suggest starting small with the review-free promotional text. A slot with near-zero swap cost turned out to be just the right size as a safe place to practice dividing work with AI. Thank you for reading.