Revenue stream comparison

When Ad Revenue, IAP, and Subscription LTV Become Final

Ad revenue, in-app purchases, and subscriptions settle on three different clocks. What AdMob, MAX, LevelPlay, AppsFlyer, Google Play, and Apple document.

Before you forecast what a cohort will earn, find out when its revenue number stops changing. Ad revenue, in-app purchases, and subscriptions each settle on a different clock, and none of them settles at the moment the money appears in your report.

That is not a modelling problem. It is a reporting fact, documented by the platforms themselves, and it decides which forecast origin your model can honestly use. A team that treats all three streams as one revenue column will keep retuning a model whose input was still moving.

This page maps the finality clock for each stream from current official documentation for AdMob, AppLovin MAX, Unity LevelPlay, AppsFlyer, Google Play, Apple, and RevenueCat, verified on August 17, 2026. It also records what those documents leave undefined.

Three timelines share one day scale. Ad revenue appears within hours but is restated on days 1, 2, 3, 7 and 14. In-app purchase revenue is complete at the transaction but stays reversible for 30 days. Subscription revenue barely exists at day 7 and accumulates through renewals for months.
Three revenue streams on one day scale. The stream decides how early a forecast origin can be, and how long the input keeps moving after it.

Three streams, three different clocks

The table below is the short answer. Each row is sourced in the sections that follow.

Ad revenue In-app purchases Subscriptions
Who produces the user-level number The mediation platform, then usually your MMP Your app and the store The store, usually via a subscription platform
First available Seconds to hours At the transaction At the first conversion
Documented as an estimate Yes, explicitly on AdMob No No, but most of the value has not happened yet
Still changing after first delivery Yes, on a published restatement schedule Only downward, through refunds Yes, every renewal period
Practically final Day 14 on AppsFlyer’s versioned report 30 days for Play refund visibility Months, and RevenueCat models to 24
Main trap Mixing report versions Refunds that never appear Comparing cohorts that are too young

Ad revenue is an estimate with a restatement schedule

Ad revenue is the only one of the three streams where the platform hands you a quality label alongside the number, and most pipelines throw it away.

AdMob’s impression-level ad revenue delivers each value with a precision type. The Android documentation defines four: PRECISE is “The precise value paid for this ad,” ESTIMATED is “An ad value estimated from aggregated data,” PUBLISHER_PROVIDED is “A publisher provided ad value, such as manual CPMs,” and UNKNOWN is “An ad value that’s unknown.” AdMob further notes that it “tries to provide an ESTIMATED value for ad sources that are optimized,” while non-optimized sources return the publisher-provided value.

Read that carefully. A revenue row tagged PUBLISHER_PROVIDED is not revenue. It is your own manual CPM played back to you. If your LTV model treats it identically to a PRECISE value, you have quietly trained on a configuration setting.

The mediation platforms also disagree about how long they will keep the data. AppLovin MAX’s User-Level Ad Revenue API states that “User-level revenue data is available eight hours after UTC day end,” and that “This API has a request window of 45 days.” You can request it aggregated per user or as one row per impression. Unity LevelPlay offers real-time impression revenue through its SDK API, but its server-side API gives you “offline files which include ILR data up to 14 days back.”

Those retention windows set a hard floor on backfills. A pipeline that breaks for three weeks can recover ad revenue from MAX and cannot recover it from the LevelPlay server-side API.

The same day of revenue has nine versions

The restatement schedule is where forecasts actually break, and AppsFlyer publishes it.

Its ROI360 ad revenue guide enumerates the versions of a single revenue day in the versioned daily ad revenue report: four in-day versions built from SDK data covering 00:00 to 04:00, 08:00, 12:00 and 24:00 on day X, then full-day versions built from server-to-server data on days X+1, X+2, X+3, X+7 and X+14. Nine versions of one day, the last arriving two weeks later.

The reason is a deliberate trade. AppsFlyer describes the mixed setup as one that “Allows impression-level data that arrives via SDK to be attributed and reported on Day X, and be switched with device-level data that arrives via API on Day 1 and on,” giving you “the benefit of data freshness for Day X with the data accuracy for Days X+1 and on.” Fresh first, accurate later.

There is a documented exception worth knowing before you reconcile Android totals: “For Android, data reported via SDK from unknown devices (without available device IDs) isn’t replaced by data that arrives via API.”

Which version trained your model?

Which of those nine versions reaches your model depends on how you pull the data, and the same document is explicit about it:

For Raw Data Export and Pull API, regardless of integration type or ROI360 package (Advanced or Standard), data for a given day becomes available the following day at Day X+1, 6 PM UTC. Data is not updated after this time for these tools.

Data Locker keeps restating through X+14. Pull API hands you the X+1 version and stops. Both are legitimate AppsFlyer ad revenue. They are not the same number, and they will not converge.

This produces a failure that looks exactly like model bias. Train on Pull API values, which are early and therefore usually incomplete, then score the model against mature Data Locker values, and every cohort will appear to have earned more than predicted. The natural response is to inflate the model’s output. The model was never wrong. The evaluation compared two different report versions.

The practical rule is the opposite of what most teams assume. An early number you use consistently is a better model input than an accurate number you mix with an early one. A frozen X+1 value carries a stable downward bias that a model can learn. A pipeline that silently upgrades some cohorts to their X+14 value and leaves others at X+1 introduces a bias that varies by cohort age, which nothing can learn.

So record the report version alongside every training row, and score against the same version you trained on. If you want the mature basis, use it on both sides and accept a forecast origin two weeks later.

One more trap in the same family: AppsFlyer’s discrepancy guide explains that “LTV data is related to installs that occurred during a specified date range. Activity data is events that occurred during a specified date range,” and that if a user installed before the specified range, “even if events occurred during the specified date range, they don’t show in LTV dashboards and reports.” Cohort forecasting needs the install-keyed view. Pulling the event-keyed view because it was the export already wired up will produce a revenue series that cannot be attributed to a cohort at all.

In-app purchase revenue is complete on arrival and reversible afterwards

IAP is the simplest stream to observe and the easiest to over-trust. The purchase event arrives at the transaction with a real amount attached. Nothing estimates it. The problem is that it can be taken back, and your visibility into that is narrower than most teams expect.

Google Play’s Voided Purchases API returns orders voided by a user refund request, a user cancellation, a chargeback, a developer-initiated refund with the revoke option enabled, or a Google-initiated refund. Two documented limits matter for forecasting.

First, the window: “The API can only show voided purchases that have occurred during the past 30 days,” with startTime defaulting to 30 days ago. A chargeback that lands on day 45 is invisible to this endpoint.

Second, and more consequential: “If a developer refunds a purchase without setting the revoke option, the order will not be returned by the API.” Your own support team can issue refunds that your revenue pipeline never learns about. That is not a rare edge case in an app with an active support desk, and it biases measured LTV upward in exactly the segments that generate the most support contact.

On the Apple side, a refund is an adjudication rather than an event. App Store Server Notifications carries separate notification types for a refund, a declined refund, and a reversed refund, and the transaction payload it delivers carries a revocation date and a revocation reason. A purchase can therefore move from earned, to refunded, to earned again.

Apple also runs a clock in the other direction. When a customer requests a refund, your server receives a consumption request, and Apple’s Send Consumption Information documentation instructs you to “Respond within 12 hours of receiving” it. That deadline belongs to your billing integration rather than your forecasting model, but it tells you the shape of the problem: IAP revenue has a dispute process attached, and disputes resolve on their own schedule.

For a forecast, the useful consequence is narrow and concrete: a D7 IAP number is nearly complete as a gross figure and is still provisional as a net one. Decide which of the two your model predicts, write it down, and make sure the mature actuals you score against use the same definition. A model trained on gross purchases and evaluated against refund-adjusted revenue will look pessimistic in the same way the Pull API model above looked optimistic, and for the same reason.

Subscription revenue at D7 has mostly not happened yet

Ad revenue and IAP have an observability problem. Subscriptions have an existence problem. At D7 the revenue you are trying to forecast has not occurred, because it arrives as renewals over the following months. What you observe instead is a trial start, an initial conversion, and at most one short-cycle renewal.

The vendors that specialize in this are explicit about the consequence. RevenueCat’s Prediction Explorer documentation recommends “waiting until a cohort is at least 7-14 days old before comparing their predicted LTV with other cohorts,” and “at least 28 days before relying on long-term predictions for weekly products.” It predicts “up to 24 month LTV for paid subscriptions that are not cancelled or expired,” and ranks reliability by product term: “Yearly products are most reliable, followed by monthly, and then weekly.” It also notes that App Store products are generally more reliable than Play Store and Stripe products.

RevenueCat additionally reports that more than 75% of products with at least 2,000 paid subscriptions have 12-month LTV predictions it considers more than 90% accurate. Treat that as a vendor statement about its own model rather than an industry benchmark. The part worth borrowing is the condition rather than the percentage: even the vendor gates its accuracy claim on cohort size.

That documented ordering is worth sitting with, because it inverts the intuition. A yearly subscription looks like the hardest thing to predict at D7, since the first renewal is 358 days away. The likely reason it is the easiest is that almost nothing happens between the conversion and that renewal, and the conversion itself is observable immediately. A weekly product renews constantly, so churn compounds fast and a small error in the early renewal rate becomes a large error at the horizon. RevenueCat publishes the ordering; the mechanism here is our reading of it.

So a subscription cohort has a minimum age before it can even be compared, let alone forecast. If your growth team ranks yesterday’s media sources by predicted LTV on a weekly-subscription app, that ranking is noise no matter which model produced it.

Choosing a forecast origin your data can support

Put the three clocks together and the forecast origin stops being a modelling preference. It becomes a property of the app.

  1. Name the stream mix first. A hybrid app has two clocks running at once. The forecast origin has to satisfy the slower one, or the two streams have to be forecast separately and summed with their own origins recorded.
  2. Pick the delivery path before the model. For ad revenue this is the single highest-leverage decision, because it fixes whether your input is frozen or restated. Write the choice down next to the model version.
  3. Score on the basis you trained on. Same report, same version age, same gross-or-net definition on both sides. Most apparent model bias in mixed-monetization apps is a basis mismatch.
  4. Set a minimum cohort age per stream, not per app. Ad revenue needs the restatement window. IAP needs the refund window if you predict net revenue. Subscriptions need enough renewal history to be comparable at all.
  5. Keep the provenance on the row. Predicted, reported, and reconstructed values answer different questions, and a cohort table that blends them cannot be audited later. This is the same discipline our cohort forecast backtest audit applies to the evaluation, and the same one attribution windows require when comparing outcome columns across networks.

If you want the general version of the forecasting workflow that sits above all of this, start with cohort revenue forecasting for mobile apps.

What the documentation does not settle

Being honest about the gaps is part of the answer.

  • Unity LevelPlay does not document revision behaviour. It says ILR “accurately calculates ad revenue” and gives a 14-day server-side retention window. It does not state whether a value can change after delivery. Do not assume it cannot.
  • AppLovin MAX documents an estimate caveat only for one dataset. The note that numbers “may differ from actuals since Meta Audience Network Bidding data is calculated using estimated revenue data” is scoped to the Meta bidding dataset. It is not a general statement about MAX revenue, and quoting it as one would be wrong.
  • AdMob labels precision but not revision. The precision type tells you how the value was derived. The page does not say whether an impression’s value is later corrected.
  • Retention and latency figures are documented defaults, not guarantees. Several depend on package tier, and AppsFlyer notes that intra-day dashboard availability requires an ROI360 Advanced subscription.
  • All of this was verified on August 17, 2026. These are exactly the kinds of operational details providers change without announcement. Re-check them before you rebuild a pipeline on top of them.

Where Lemon AI fits

Lemon AI forecasts cohort revenue, ROAS, ARPU, and payback through named horizons up to D365, from purchase and ad revenue events, and it keeps the provenance visible rather than flattening it. Actual revenue, predicted remaining revenue, and the horizon each value belongs to stay separate on the surface, which is what makes a mixed-monetization cohort table auditable months after the decision. The measurement and forecasting methodology sets out the reconciliation, attribution, and backtesting rules in full, and Cohort Prediction shows the forecast surface itself.

None of that removes the work described above. Your reporting path still decides when your numbers settle. What it does remove is the ambiguity about which value you are looking at when the forecast and the actual finally meet.

Primary sources

Lemon AI

Book a demo

Loading available times…