What goes in an app store performance dashboard?
An app store dashboard joins two things the stores report separately: acquisition (impressions, product page views, installs) and money (proceeds after commission, subscription starts and renewals, refunds). Both sides arrive as pre-aggregated daily reports, which shapes everything you can ask of them — there are no user-level joins here.
Which cards belong on this dashboard?
- Impressions to product page views to installs, with the conversion rate at each step
- Install trend by store, week over week
- Installs by country, top 20
- Proceeds after store commission, by month and store
- Proceeds per install
- Subscription starts, renewals, and cancellations
- Refund rate on paid downloads and in-app purchases
- Crash rate against the install trend
- Average rating and rating volume by version
What data does this dashboard need?
- A daily metrics table by store, country, and app version: impressions, product page views, installs, proceeds
- A subscription table with starts, renewals, cancellations, and trial conversions at daily grain
- Refunds reported separately from gross sales, dated when the store processed them
- Crash counts and sessions from your own telemetry, since store crash reporting is opt-in and partial
- Rating counts and averages, versioned so a release's effect is visible
How do you build it?
- Land the source data in a database — via a managed connector, scheduled API pulls, or a CSV loaded with
mb upload csv— keeping raw IDs, timestamps, currencies, and fee lines intact. - Build models at the grain this dashboard needs, with gross, fee, tax, and net amounts as explicit columns rather than per-card arithmetic.
- Create one saved question per card and certify the shared models so every dashboard downstream inherits the same definitions.
- Add dashboard filters for Date range, store (App Store / Google Play), country, app version, acquisition source type.
- Show data freshness on the dashboard — these sources lag, and every viewer should see by how much.
Example card SQL
WITH weekly AS (
SELECT
date_trunc('week', d.report_date)::date AS week,
d.store,
SUM(d.impressions) AS impressions,
SUM(d.product_page_views) AS page_views,
SUM(d.installs) AS installs,
SUM(d.proceeds_usd) AS proceeds
FROM app_store_daily_metrics d
-- The most recent days are still restating; cut them out rather
-- than letting a partial week read as a decline.
WHERE d.report_date >= CURRENT_DATE - INTERVAL '84 days'
AND d.report_date < CURRENT_DATE - INTERVAL '3 days'
GROUP BY 1, 2
), subs AS (
SELECT
date_trunc('week', s.report_date)::date AS week,
s.store,
SUM(s.new_subscriptions) AS new_subs,
SUM(s.renewals) AS renewals,
SUM(s.cancellations) AS cancellations
FROM app_store_subscriptions s
GROUP BY 1, 2
)
SELECT
w.week,
w.store,
w.impressions,
w.page_views,
w.installs,
ROUND(
100.0 * w.page_views / NULLIF(w.impressions, 0), 1
) AS impression_to_page_view_pct,
ROUND(
100.0 * w.installs / NULLIF(w.page_views, 0), 1
) AS page_view_to_install_pct,
ROUND(w.proceeds, 2) AS proceeds,
ROUND(w.proceeds / NULLIF(w.installs, 0), 2) AS proceeds_per_install,
s.new_subs,
s.renewals,
s.cancellations,
ROUND(
100.0 * (
w.installs::numeric
/ NULLIF(LAG(w.installs) OVER (
PARTITION BY w.store ORDER BY w.week
), 0) - 1
), 1
) AS installs_wow_pct
FROM weekly w
LEFT JOIN subs s ON s.week = w.week AND s.store = w.store
ORDER BY w.week DESC, w.store;