Metrics
Every tile on the Metrics page with its exact formula: MRR, ARR, net MRR, active subscribers, trials, churn, ARPU, new customers, revenue and trial conversion.
The Metrics page (/metrics) is the full read of your subscription business: the MRR chart, App Store reach, revenue totals, the trial funnel, a year-to-date chart and MRR broken down by product and country. This page lists every tile, where its number comes from, and the exact formula behind it. If the code changes, this page changes with it.
Where the numbers come from
RevenueHog reads two kinds of Apple data, and they have different strengths.
Apple's daily reports (Sales and Trends, plus the Subscription and Subscription Event reports, pulled with your App Store Connect API key) are the authoritative once-a-day record: revenue per day, and a daily snapshot of your active subscriptions per product, state and country. They lag about one day, and on import they reach back about a year. That year is Apple's daily-report limit, not a RevenueHog limit.
App Store Server Notifications V2 (once wired, see server notifications) are the real-time stream: individual purchase, renewal, cancellation and refund events, each signed by Apple. They power the live feed, per-customer LTV, the intraday part of MRR, the live edge of revenue totals, and the cancelled-trial adjustment described below, which reports alone cannot make. Stripe events and Google Play notifications feed the same stream for those sources.
There are two kinds of metric, and keeping them apart matters:
| Kind | What it is | Gap days | Examples |
|---|---|---|---|
| Level | A point-in-time count or value, read from the daily snapshot | Last value carried forward | MRR, net MRR, ARR, active subscribers, trials, active customers |
| Flow | A count or sum per day | A missing day is zero | Revenue per day, new customers, trial starts, conversions, churn events |
Mixing the two up is the most common way MRR dashboards go wrong, so every metric below says which it is.
Scope and period
The page honors both topbar controls:
- App filter (
?app=): every tile is recomputed for that one app, except the four subscriber read-model tiles (NET MRR, ACTIVE CUSTOMERS, NEW CUSTOMERS, ARPU) and the MRR BY PRODUCT / MRR BY COUNTRY breakdowns, which are org-wide only and are hidden while an app is selected. The footnote then reads "Product & country breakdowns are org-wide. Clear the app filter to see them." - Period (
?period=24h|7d|30d|90d, default30d): sets the MRR chart span, the delta comparison point, the revenue window, the revenue-per-day bars and the funnel window. The YTD, all-time and ARR tiles do not depend on the period.
The header line reads Metrics · 30d, plus · filtered to one app when the app filter is set. The ↓ CSV link beside it downloads the revenue lines for the same scope (see exports).
MRR
MRR is a level: the monthly recurring value of every subscription in a paying state on the most recent snapshot day, plus the intraday change from live events since that day.
Each subscription's price covers its whole billing period, so the price is divided by the period's length in months to get a comparable monthly figure, then multiplied by the subscriber count:
# for every subscription snapshot row in a paying state on the day:
row_mrr = (price_per_billing_period / period_in_months) * quantity
MRR = Σ row_mrr # over all paying rows, all products
net MRR = same, using proceeds (after Apple's commission) instead of price
# a row whose period_in_months is 0 or less contributes 0
# examples
$4.99/mo monthly → 4.99 / 1 = $4.99 per subscriber
$39.99/yr annual → 39.99 / 12 ≈ $3.33 per subscriber
$1.99/wk weekly → 1.99 / 0.23 ≈ $8.65 per subscriberWhich subscriptions count as paying. The snapshot states active, intro_offer (paid intro offers), promo_offer and grace (billing grace period). Grace is in because Apple extended the term and the subscriber still has access.
Billing retry is out, deliberately. By the time a subscription enters retry the term has already ended and Apple has already cut access. It also keeps the two halves of RevenueHog consistent, because the live event stream drops MRR at the same moment (the EXPIRED and GRACE_PERIOD_EXPIRED notifications). Counting retry as paying made the snapshot and live MRR disagree by exactly the retrying subscriptions.
Free trials are excluded from MRR entirely. They contribute $0 and are counted as their own metric.
Live MRR. The headline value is the snapshot MRR on the anchor day plus the net monthly-recurring change from live server notifications received after that day:
headline MRR = snapshot MRR (anchor day) + live intraday MRR deltaOnly the headline moves. The chart series stays snapshot-derived. The same live value appears in the Overview MRR tile, the apps table and the feed header, so the surfaces agree.
Delta. The ▲ +x.x% / ▼ x.x% beside MRR compares the headline value with the gap-filled snapshot MRR one period earlier:
MRR delta % = (headline MRR − MRR at (anchor day − period days)) / MRR at that day × 100When the prior value is zero or less (a newly launched product), the delta is absent instead of a misleading +∞.
Family Sharing copies are worth $0
When a subscription is shared with a family, Apple sends a separate notification for every member who gains access, each one carrying the purchaser's price. Those copies are real entitlements but not real money: only the purchaser was ever charged. RevenueHog counts them as events and drops their revenue to zero everywhere it matters, so a subscription shared with five relatives is one sale, not six.
Gap days, anchors and deltas
Apple's reports lag about a day, so "today" usually has no snapshot yet. All level metrics are therefore anchored to the most recent day that has a snapshot. Per-app and per-product views anchor to the same org-wide day, so drill-downs always agree with the dashboard they came from.
Subscriber levels persist on days Apple did not report: a missing day does not mean zero subscribers. Daily level series therefore carry the last observed value forward across gaps (last observation carried forward, LOCF):
day reported series value
07-01 $1,204 $1,204
07-02 (no rows) $1,204 ← carried forward, not zero
07-03 (no rows) $1,204 ← carried forward
07-04 $1,310 $1,310Days before the first observation are 0. Flow series (new customers, revenue per day) are the opposite: a missing day really is zero and is never filled.
Deltas compare the current level against the gap-filled level one window earlier. The generic delta rule, used by every relative delta on the dashboard:
delta = (current − prior) / prior # null when prior ≤ 0Per-product deltas are built from each product's own gap-filled series, so a day Apple skipped for one product cannot fake a jump.
App Store reach tiles
A row of four tiles appears only once Apple has delivered App Store analytics for the current scope. Until then it is hidden, rather than showing a row of zeros.
| Tile | Value | Subtitle |
|---|---|---|
| IMPRESSIONS | Total App Store impressions in the period | N unique (unique impressions) |
| PAGE VIEWS | Total product page views in the period | N unique |
| DOWNLOADS | First-time downloads in the period | +N redownloads, or First-time when there were none |
| STORE DATA | The last day Apple analytics covers | How far behind that is from your local day: today, 1 day behind, N days behind. Amber when more than 4 days behind. Falls back to Apple analytics lags ~2d. |
These come from Apple's Analytics Reports, which run about two to three days behind live. See data freshness.
Stat tiles
| Tile | Value | Subtitle | Kind |
|---|---|---|---|
| ACTIVE SUBS | Paying-state subscribers on the anchor day | Signed change vs one period earlier, e.g. +12 / 30d (red when negative) | Level |
| IN TRIAL | Unexpired trials on the anchor day | N expire 48h (amber), or None expiring 48h | Level |
| ARR | Live MRR × 12 | MRR × 12 | Level |
| THIS YEAR | Net proceeds since January 1 (UTC) of the current year | Net proceeds YTD | Flow total |
| GROSS / YTD | Customer-paid gross since January 1 | Customer-paid YTD | Flow total |
| ALL TIME | Net proceeds over every revenue day Apple has reported | Net · since Mon YYYY · $X gross | Flow total |
| AVG / DAY | Period revenue ÷ period days | Net proceeds | Flow average |
| NET MRR | MRR using proceeds instead of price | After Apple's cut | Level, all apps only |
| ACTIVE CUSTOMERS | Paying subscribers + trials | Paying + trialing | Level, all apps only |
| NEW CUSTOMERS | New subscription starts over the trailing 30 days | 30d window | Flow, all apps only |
| ARPU | MRR ÷ active subscribers | Per paying sub | Derived, all apps only |
| TRIAL CONV | Trial → paid conversion for the period | Trial → paid · 30d | Flow ratio |
ACTIVE SUBS
ACTIVE SUBS = Σ quantity over paying-state snapshot rows on the anchor day
subtitle = ACTIVE SUBS − ACTIVE SUBS at (anchor day − period days), shown as "+N / period"IN TRIAL
IN TRIAL = Σ quantity over "trial" snapshot rows on the anchor day
expire 48h = trial starts (SUBSCRIBED, free-trial offer, last 45 days, newest 500)
whose own expiry falls within the next 48 hoursOn the Overview and Metrics tiles, IN TRIAL is the whole unexpired-trial count from Apple's snapshot. The Count cancelled trials as active preference only decides whether cancelled-but-unexpired trials are left out of the 48-hour expiry nudge. See trials and cancelled trials.
The expiry count is approximate: a converted trial can still be counted until its conversion event arrives. It is good enough for the amber nudge.
ARR
ARR is annualized MRR, a run rate ("a year at today's MRR"), not a sum of signed contracts. On this tile it annualizes the live MRR shown above it, so the two always agree and the tile survives an app filter.
ARR = live MRR × 12THIS YEAR, GROSS / YTD, ALL TIME
THIS YEAR = Σ proceeds_usd over revenue days from Jan 1 (UTC) to today
GROSS / YTD = Σ gross_usd over the same days
ALL TIME = Σ proceeds_usd over every revenue day ever reported, plus the live edge
Apple's commission (YTD) = GROSS / YTD − THIS YEARThe days Apple's sales report has not reached yet (normally one or two) are filled from live events, the live edge, so a purchase made today counts right away. When Apple publishes the day, it moves from the live estimate to the report figure and the total barely moves. Live Stripe rows estimate proceeds with Stripe's blended processing cost, and live Google Play rows at the 15% service fee tier, until their daily rollup records the real figure.
AVG / DAY
AVG / DAY = REVENUE (period total, see below) / period days # 1, 7, 30 or 90NET MRR
NET MRR = Σ (proceeds_per_billing_period / period_in_months) × quantity # paying rows, anchor daySnapshot-only and org-wide: it does not include the intraday live delta.
ACTIVE CUSTOMERS
ACTIVE CUSTOMERS = active (paying) subscribers + current trialsHere the trial count honors the Count cancelled trials as active preference: with it off (the default), cancelled-but-unexpired trials are subtracted from the current value.
NEW CUSTOMERS
A flow, not a level: the count of brand-new subscription starts per day, summed over the trailing 30 days ending at the anchor day.
NEW CUSTOMERS = Σ quantity of Subscription Event rows with event type in
{ subscribe, trial_start, start_introductory_offer }
over days (anchor − 29) … anchor
delta = vs the 30 days before thatA first paid subscribe or a free-trial/intro-offer start counts. Renewals, trial conversions, plan changes and reactivations are existing customers and are excluded. Days with no starts count as zero and are never carried forward. The window is always 30 days, whatever the period control says.
ARPU
ARPU = MRR / active subscribers # snapshot MRR, org-wide; 0 when there are no paying subscribersARPU is 0, not a division error, when nobody is paying.
TRIAL CONV
trials = Σ quantity, event type in { trial_start, start_introductory_offer }, within the period
paid = Σ quantity, event type in { trial_convert,
paid_subscription_from_introductory_offer,
upgrade_from_introductory_offer }, within the period
TRIAL CONV = min(100, paid / trials × 100) # null (shown as a dash) when trials = 0This is a same-window approximation: paid and trials are different groups of people, because a trial converts roughly one trial period after it starts. When conversions from an earlier, larger cohort land in a window with fewer new starts, paid can exceed trials, so the rate is capped at 100%. For the real cohort rate, see the Funnel.
Revenue
revenue per day = Σ proceeds_usd over that UTC day's sales-report rows (+ live edge for unreported days)
REVENUE = Σ revenue per day over the period's days ending today
REVENUE delta % = (REVENUE − prior equal-length window) / prior window × 100
# null when the prior window had no revenueDaily revenue is the sum of developer proceeds (net of Apple's commission) across that day's sales-report rows. One-time purchases and annual prepays are included, which is exactly what MRR by definition hides. Periods end today, with the days Apple has not reported yet filled from the live edge.
The REVENUE / DAY card draws one bar per day over the selected window (up to 90), with the caption Nd · UTC and the last day's value underneath. Hover the label for "Net proceeds (after Apple's cut)". With nothing in the window it reads "No revenue in this window."
Funnel card
The FUNNEL / 30D card shows the App Store funnel for the same scope and period: Impressions, Product page views, Downloads, an optional Onboarding stage, Trials and Paid. Each bar is the share kept from the stage above, and the percentage beside each label is what dropped off. A negative drop-off (shown with a +) means the stage outran the one above it, which is real: a download needs no page view. When Apple has not delivered store analytics yet, the funnel starts at trials. Full details are on the Funnel page.
Year to date by month
Shown once there are at least two months of the current year. Each bar is a month's gross, with the net share filled inside it, on one shared scale, so the unfilled part above the net is Apple's commission. Hover a bar for Mar · gross $X · net $Y.
MRR by product and by country
Org-wide only (hidden with an app selected), from the anchor day's snapshot.
product MRR = Σ (price / period_in_months) × quantity over that product's paying rows
product delta = (product MRR − its gap-filled MRR 30 days earlier) / that prior value
country MRR = the same sum grouped by storefront countryMRR BY PRODUCT lists the top 10 products with N subs and the 30-day delta (absent for a product with no positive prior MRR). MRR BY COUNTRY lists every country.
Trials and cancelled trials
The trial count comes from Apple's daily snapshot, which lumps every unexpired trial into one bucket. Apple's reports carry no auto-renew breakdown, so the report alone cannot tell a live trial from one whose user already pressed cancel.
The live event stream can, for apps with notifications wired: a free-trial SUBSCRIBED whose latest lifecycle event is auto-renew being turned off (and whose trial period has not lapsed yet) is a cancelled-but-unexpired trial. With the Count cancelled trials as active preference off (the default, under Settings → Notifications → DATA), RevenueHog subtracts these where a current trial count feeds a metric that predates the separate callout: ACTIVE CUSTOMERS, the TRIALS column of the apps tables, and the expiring-in-48h nudge. Turning the preference on restores Apple's raw counting. The adjusted count is clamped at 0, since the report and the live stream run on different clocks.
Where the adjustment cannot apply
Historical trial series stay unadjusted: rebuilding day-by-day auto-renew status would require data Apple does not provide. Apps without server notifications wired get no adjustment at all: their counts match Apple's raw numbers, because RevenueHog does not know better and will not guess.
Churn
Churn shows up two ways. In the live feed it is event volume: each individual churn event. On the Overview and apps tables it is a rate:
churn % (per app) = churned in the last 30 days / active subscribers 30 days ago × 100
churned = Σ quantity of Subscription Event rows with event type in
{ cancel, expire, canceled_from_billing_retry }
churn delta (pts) = churn % − the same rate for the 30 days before that
(that window divides by the level 60 days ago)That is the standard definition, and the denominator matters: dividing by the current count would let subscribers who joined mid-window inflate the base, quietly flattering a growing app. It also means an app can exceed 100% when someone joins and churns inside the same window. That is real, and it is not capped. The rate is null (shown as a dash), not zero, for an app with no subscribers at the window start, including one whose snapshot history only begins mid-window.
From the live stream, churn events are: subscription expired, billing grace period expired without recovery, access revoked, and auto-renew turned off (cancellation intent: the subscriber keeps access until the period ends). From the report vocabulary: cancels, expirations, and billing retries that ran out. A renewal entering billing retry is churn risk and is deliberately not counted as churn. Most retries recover.
The trial funnel is intro-offer based
This is an Apple limitation worth being direct about: Apple's subscription-event report does not split free trials from paid introductory offers. One "start introductory offer" event covers both. So the funnel's trials are trial/intro starts, paid are intro → paid conversions (including converting early by upgrading), and the conversion percentage is paid ÷ starts within the selected period. It is a period ratio, not a tracked cohort rate: starts late in the window convert after it closes.
Because that ratio compares two groups of different people, the Funnel page also shows the real cohort for apps with server notifications wired.
Currency conversion
Apple reports revenue per territory in local currencies. Each non-USD amount is converted using the daily ECB reference rate for that sale's date. Only when no published rate exists does a static approximate rate apply, and the row is flagged as estimated (the estimated column in the CSV export). An unknown currency with no rate at all contributes $0 rather than a fake 1:1 conversion, so an unconvertible row can never inflate totals.
All dashboard values are in USD. There is no currency selector.
Source of truth
Converted figures are for day-to-day telemetry. Apple's payout reports remain the source of truth for exact amounts. See Payouts.
Small Business Program
Apple's daily reports state the Developer Proceeds column at your actual commission rate (Small Business Program members see the 85% share there, not the standard 70%), so every report-derived number here already reflects membership. RevenueHog applies no adjustment on top of Apple's figures, because none is needed: the proceeds column is stored as Apple states it, per proceeds currency, and never rederived from a price and a commission rate.
The live feed is the one estimated surface. Real-time events arrive through App Store Server Notifications, which carry the customer price but not your proceeds, so an event's detail page estimates proceeds from that price. Apple takes 30% by default, or 15% with the Small Business setting enabled (Settings → Preferences → SMALL BUSINESS). If you record your admission date alongside the setting, the reduced rate applies only to events on or after it, because enrolling does not retroactively repay old sales. That label cannot see territory taxes or year-two subscription rates, so treat per-event proceeds as an estimate; reports and payouts remain the truth.
Empty state
With no MRR, no active subscribers, no revenue and no subscriber data, the page shows "No data yet." and "Metrics appear after your first sync pulls Apple's subscription reports. Connect a key in Settings to get started." See Connect App Store Connect.
Related
Last updated on
Event detail
Event detail: the hero amount, every decoded field, refund request status, estimated proceeds, the raw payload, and links to the customer and Apple record.
Funnel
The Funnel page: impressions to paid from Apple's reports, net installs, the log and linear silhouette, step conversion math and the real trial cohort from S2S.