storebound/skills/store-analytics/SKILL.md

8.1 KiB

name description
store-analytics 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/<date>.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_<number>); 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 <virtual.json> --account <profile> --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 <apple-sales | apple-finance | play-earnings | play-sales> 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 <name>.summary.csv (or --csv <file>); 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).