GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Articles/API / SDK
API / SDK/2026-04-04Advanced

Automating App Store Reviews with Gemini API and App Store Connect API: Implementation Notes from Running 50M-Download Apps

Implementation notes for combining Gemini API and App Store Connect API to handle review sentiment analysis, reply drafting, competitor monitoring, and weekly ASO reports in Python. Includes lessons learned from running indie apps with over 50 million cumulative downloads.

Gemini API192App Store Connect APIASO2Review AutomationPython38Competitor AnalysisApp Development2

Premium Article

When Review Replies Were Eating My Mornings

For a long stretch of my indie developer life, opening App Store Connect, reading unanswered reviews one by one, running unfamiliar languages through browser translation, gauging the tone, and drafting a reply consumed one to two hours every day. Since releasing my first app to the App Store in 2013 and growing the catalog to over 50 million cumulative downloads, the inbox routinely hit several dozen reviews a day — and over a thousand in some months.

Even so, I never wanted to drop the practice. One-star reviews always got a reply. Five-star reviews with a concrete request got one too. That discipline quietly held up the long-term rating across my apps. But the hours were real, and I kept telling myself I would automate this someday.

When Gemini API matured to a point where I trusted it in production, I rewired the whole flow on top of App Store Connect API. The result: review handling dropped from one to two hours a day to under fifteen minutes, and reply quality stayed steady because I switched to a "Gemini drafts, I approve" pattern rather than full hands-off automation.

This article is the implementation note for that pipeline, with the rough edges I hit along the way. The system covers:

  • Multilingual sentiment analysis and classification (Japanese, English, Spanish, and Chinese reviews coexist in my inbox)
  • Context-aware reply drafting (responses that match the specific review, not boilerplate)
  • Competitor rating and review-trend monitoring
  • Weekly ASO intelligence reports auto-delivered to Slack

The target audience is indie developers running iOS or macOS apps, comfortable with App Store Connect, and intermediate-to-advanced Python users.

Prerequisites: Working knowledge of App Store Connect, Python 3.10+, and an existing Gemini API key.


App Store Connect API Authentication

Generating Your API Key

App Store Connect API uses JWT (JSON Web Token) authentication. Log in to App Store Connect, navigate to Users and Access → Keys, and generate an App Store Connect API key.

Required permissions:

  • Customer Support: Required to respond to reviews
  • Reports: Required to download sales and reports
  • App Store: Required to read app information

After generating the key, download the .p8 file and store it securely — it cannot be re-downloaded.

Environment Setup

# .env file — NEVER commit this to Git
APP_STORE_KEY_ID=YOUR_KEY_ID_HERE
APP_STORE_ISSUER_ID=YOUR_ISSUER_ID_HERE
APP_STORE_PRIVATE_KEY_PATH=/path/to/AuthKey_XXXXXXXX.p8
GEMINI_API_KEY=YOUR_GEMINI_API_KEY
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL

JWT Generation and API Client Implementation

import jwt
import time
import httpx
from pathlib import Path
from dotenv import load_dotenv
import os
 
load_dotenv()
 
class AppStoreConnectClient:
    """App Store Connect API client with automatic JWT refresh"""
 
    BASE_URL = "https://api.appstoreconnect.apple.com/v1"
    TOKEN_LIFETIME = 1200  # 20 minutes (maximum allowed)
 
    def __init__(self):
        self.key_id = os.getenv("APP_STORE_KEY_ID")
        self.issuer_id = os.getenv("APP_STORE_ISSUER_ID")
        self.private_key = Path(os.getenv("APP_STORE_PRIVATE_KEY_PATH")).read_text()
        self._token: str | None = None
        self._token_expiry: float = 0
 
    def _generate_token(self) -> str:
        """Generate a fresh JWT token"""
        now = int(time.time())
        payload = {
            "iss": self.issuer_id,
            "iat": now,
            "exp": now + self.TOKEN_LIFETIME,
            "aud": "appstoreconnect-v1",
        }
        return jwt.encode(payload, self.private_key, algorithm="ES256", headers={"kid": self.key_id})
 
    @property
    def token(self) -> str:
        """Cached token with automatic refresh before expiry"""
        if not self._token or time.time() > self._token_expiry - 60:
            self._token = self._generate_token()
            self._token_expiry = time.time() + self.TOKEN_LIFETIME
        return self._token
 
    def get(self, path: str, params: dict | None = None) -> dict:
        """GET request with pagination support"""
        headers = {"Authorization": f"Bearer {self.token}"}
        response = httpx.get(f"{self.BASE_URL}{path}", headers=headers, params=params, timeout=30)
        response.raise_for_status()
        return response.json()
 
    def patch(self, path: str, body: dict) -> dict:
        """PATCH request for updating resources like review replies"""
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json",
        }
        response = httpx.patch(f"{self.BASE_URL}{path}", headers=headers, json=body, timeout=30)
        response.raise_for_status()
        return response.json()

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Working code for sentiment analysis and reply generation with Gemini 2.5 Flash, including the temperature and prompt parameters used in production
Lessons learned from running apps at 50M+ download scale: App Store Connect API gotchas around JWT caching, rate limits, and territory-based review filtering
A two-stage approval gate for reply drafts and a weekly-report design that keeps the system from drifting into boilerplate replies
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API / SDK2026-07-16
I Asked Gemini to Grade My App Store Screenshots. Everything Scored 78–85.
Ask Gemini Vision to grade App Store screenshots out of 100 and good candidates and deliberately broken ones both land at 78–85. Here is how I measured the judge's discrimination power, dropped absolute scoring, and rebuilt it as debiased pairwise comparison.
API / SDK2026-07-09
Google Sheets API × Gemini API: A Python Data Pipeline — No Apps Script Required
Learn how to build a fully Python-based pipeline that reads data from Google Sheets, processes it with Gemini API, and writes results back — without touching Apps Script. Covers service account auth, structured output, and rate limit handling.
API / SDK2026-06-29
Guarding Gemini API Responses in CI: Snapshot and Semantic Regression Testing
How to defend non-deterministic Gemini API responses with pytest snapshot tests plus embedding-based semantic regression detection — including CI wiring, separating flakiness from real regressions, and snapshot-update governance, all in working code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →