--- name: store-analytics description: Take store analytics snapshots for a mobile app or game (App Store impressions, product page views, downloads and conversion through the Analytics Reports API; Google Play store listing visitors, acquisitions and installs from the Play reports bucket) and compare them with listing, screenshot and experiment changes from git; download App Store sales / finance reports and Play earnings / sales reports with a summary per product and currency. Use when the user asks whether a listing change, new screenshots or a store experiment worked, wants install / conversion numbers, sets up store reporting, or wants sales, proceeds or earnings numbers. --- # Store analytics CLI: `storebound analytics setup | snapshot | compare`. MCP: `analytics_snapshot`, `analytics_compare` (offline), `analytics_setup`. Read-only on both stores except `setup` (creates the App Store report request). Snapshots go to `.storebound/analytics/.json` (gitignored; they are your business numbers). ## What is possible (and what is not) - **App Store**: the Analytics Reports API. An Admin key creates one ONGOING report request (`storebound analytics setup`); the first reports come 1-2 days later, then daily, and Apple keeps instances for 35 days, so take a snapshot at least monthly (weekly is better). A request left unread for a long time stops (`stoppedDueToInactivity`); `setup` makes a new one. storebound reads "App Store Discovery and Engagement" (Event Impression / Page view, Counts and Unique Counts) and "App Store Downloads" (First-time Download, Redownload; updates are left out), checks each segment's md5 checksum and keeps, per day, the newest processing of that day. Conversion = (first-time downloads + redownloads) / unique impressions. App Usage (sessions, active devices, crashes) comes from the "App Sessions" and "App Crashes" reports of the same request. Source: https://developer.apple.com/documentation/appstoreconnectapi/downloading-analytics-reports - **Google Play**: there is **no API** for store listing statistics (the Play Developer Reporting API only has vitals). Play Console writes monthly CSV exports into your own Cloud Storage bucket; storebound reads them with the service account. Setup: Play Console > Download reports > Statistics > **Copy Cloud Storage URI** and put the bucket into `play.reportsBucket` (`pubsite_prod_rev_`); the service account needs "View app information and download bulk reports" (Play Console Help answer 6135870). Files appear with a few days' delay; the current month's file grows daily. Conversion = store listing acquisitions / store listing visitors. - **Not possible through any API**: Play store listing experiment results and App Store Product Page Optimization results (read them in the consoles; `storebound apple ppo status` shows the App Store test state), Play "Explore" search terms beyond the traffic source file. ## For a collector (store-only config, JSON on stdout) A script that reads many apps needs no project: a config with `apple.appId` (`bundleId` null or left out) and `play.packageName` is valid for `analytics` and `reports` reads (every write still needs the bundle id). - `storebound analytics snapshot --store apple|play --days 30 --config --account --json` prints the snapshot as one JSON document on stdout (logs go to stderr); the file in `--dir` is still written, add `--no-write` to skip it. A store that failed carries `{ "error": ... }`; when every store failed the JSON is printed and the exit code is 1. Non-fatal gaps are in `notes`. - Play bucket without `play.reportsBucket`: env `PLAY_REPORTS_BUCKET`, else the credential profile's `reportsBucket` (a bucket name, not a secret). Same for `reports play-*`. - Active users, per day. App Store: `sessions`, `activeDevices`, `sessionSeconds`, `crashes` from the App Usage reports (App Sessions, App Crashes; `activeDevices` is the sum of the rows' Unique Devices, so a device seen in two rows, e.g. two app versions, counts twice; totals: `sessions`, `crashes`, `activeDevicesAvg`). They appear a day or two after the report request exists; until then the snapshot has the rest and a `notes` line. Play: `installs` (Daily User Installs), `activeDeviceInstalls` (Active Device Installs) and `totalUserInstalls` (Total User Installs) from the installs overview file (all countries; else the country file summed). The last two are stock numbers: totals take the newest day, never a sum. - Revenue per day: `storebound reports apple-sales --days 30 --json` (or `--from 2026-10-01 [--to 2026-10-25]`) reads the daily SALES reports of the range (4 at a time): `days[]` with `summary` per SKU and currency and `totals` per currency, `totals` for the range per currency, `missing` (days with no report: no sales, or not out yet; never an error). `storebound reports play-sales --days 30 --json` groups the estimated sales by order charged date and currency of sale: `orders`, `refunds`, `itemPrice`, `taxes`, `charged` (tax included), `refunded` (negative), `net`, plus `emptyDays` and `missingMonths`. Play's earnings (proceeds after Google's fee) are monthly only. ## Flow 1. Once: `storebound analytics setup` (App Store) and `play.reportsBucket` (Play). 2. Weekly: `storebound analytics snapshot` (default the last 28 days; `--dry` prints totals only). 3. Change the listing as usual and commit it (`store/appstore.md`, `store/listing.md`, `store/shots/...`, the config). One change at a time, with a clear commit subject ("new first screenshot: boss fight"), makes the comparison readable. 4. `storebound analytics compare` (or `--md` for a report): for every commit that touched the store files, daily impressions (App Store) or visitors (Play) and conversion in the 7 days before vs the 7 days from the change (`--days 14` for small apps). ## Sales and finance reports `storebound reports ` downloads one report into `.storebound/reports/` (gitignored: these files hold per-order rows and buyer locations) and prints a summary per product and currency plus `.summary.csv` (or `--csv `); totals are never added across currencies. `--dry` shows the request only. MCP: `reports`. - **App Store**: `apple-sales [--type SALES] [--frequency DAILY|WEEKLY|MONTHLY|YEARLY] [--date ...]` (Sales and Trends; other types: PRE_ORDER, SUBSCRIPTION, SUBSCRIPTION_EVENT, SUBSCRIBER, SUBSCRIPTION_OFFER_CODE_REDEMPTION, WIN_BACK_ELIGIBILITY, each with Apple's fixed frequency and version) and `apple-finance --month YYYY-MM` (Apple fiscal month; consolidated region ZZ, `--detail` for the Financial Detail report, region Z1). Needs a **team** API key (not an individual key) with access to reports, and the vendor number (App Store Connect > Payments and Financial Reports) as `ASC_VENDOR_NUMBER` in the environment, a credential profile (`--account`) or `.storebound/credentials.json`, never in the config. A 404 means "no report for that date" (no sales, or not out yet: daily reports come the next day). - **Google Play**: `play-earnings --month YYYY-MM` / `play-sales --month YYYY-MM` read `earnings/earnings_YYYYMM*.zip` and `sales/salesreport_YYYYMM*.zip` from `play.reportsBucket` and unzip them. The service account needs "View financial data" in Play Console (Users and permissions). Earnings arrive early in the next month; estimated sales update daily. - Money numbers are the owner's: never paste them into issues, commits or public text. ## Read it honestly - Small numbers swing: under a few hundred page views a week, a 20 % change is noise. Say so. - Seasonality, featuring, ads campaigns, a rating drop and price changes move the same numbers. Look at the same weekday span and mention other things that changed in that window. - A before / after comparison is not an experiment. For a real answer use the store tests: Play store listing experiments (Console) and App Store Product Page Optimization (`storebound apple ppo`, store-screenshots skill). - Never present estimates as facts to the owner, and never put these numbers into public text (listings, replies).