Beta Distribution Report 2026
What actually happens to a mobile app build after somebody shares it with testers, measured on every one of the 22,031 uploads made through this service in 31 days — 4,496 from 1,853 signed-in accounts and 17,535 from people who never signed in at all.
Most measurement in mobile begins after an app is installed, so the interval between a build leaving a laptop and a tester opening it tends to be described from memory rather than from data. This report describes it from rows. Edition 2026-09.1, published 2026-09-03, queried 2026-09-03, covering 3 August 2026 to 2 September 2026.
- Uploads shared in 31 days
- 22,031
- Of those, shared without an account
- 17,535
- Accounts that uploaded in the window
- 1,853
- Deliveries recorded on those uploads
- 37,271
- Countries those uploads reached
- 164
- Install-page events on those uploads
- 69,899
One definition up front, because it decides what every figure below means. The unit here is an UPLOAD, not a build record: every row created in the window is counted whether or not it has since expired, and an upload made without an account counts the same as one made with an account, because it is the same product action. Retirement is reported as a fact about the population rather than used as a filter on it: 60.7% of the account uploads and 99.1% of the guest uploads had already been retired by expiry when the data was taken, and all of them are in the counts. Because nothing here depends on what was still live on a particular morning, the population does not decay: run the published query over the same dates next year and it returns these uploads, minus any account the exclusion list has picked up in the meantime. 4 things are still read as of the query date rather than fixed by the window, and the Methodology lists them rather than letting the word “reproducible” do more work than it can.
Correction: this edition replaces 2026-09
Edition 2026-09 measured only the builds that were still live on the query date and excluded uploads made without an account from the population entirely, so it described 1,908 of the window's 4,496 account uploads, none of its 17,535 anonymous ones, and about a tenth of the deliveries. The population predicate has been replaced: every upload created in the window is counted, retired or not, and uploads made without an account are first-class members of it. Every figure in this file was re-derived; none was carried over. The figures published on 2026-09-03 under edition 2026-09 should not be cited. This notice stays on the page for the life of edition 2026-09.1, because a page that promises to mark what changed has to actually mark it.
The four findings
- 79.6% of uploads are made by someone who never signed in. 17,535 of 22,031 uploads came from people without an account, 3.9 for every one made from an account, spread over 9,989 distinct devices. Any measurement of this category that starts from a signed-in user table is describing the smaller fifth of it.
- 31.6% of uploads are never delivered to anyone. 6,971 of 22,031 uploads had no recorded delivery at all. The rate barely moves between the two halves — 32.0% of account uploads and 31.6% of guest uploads — so it is a property of the act of uploading, not of how committed the uploader was.
- The top 1% of uploads carry 42.4% of all deliveries. 221 uploads, belonging to 156 distinct owners, account for 42.4% of the 37,271 deliveries recorded. The median upload is delivered 1 time and the 90th percentile 2.
- When an upload is delivered at all, 94.1% of first deliveries happen within an hour. And that is not an artifact of a short window: restricted to the 11,272 delivered uploads among the 16,614 made at least 7 days before the window closed, the first-hour share is 93.9%, which is the same answer. Both versions are charted below.
Four out of five uploads have no account behind them
17,535 uploads, 79.6% of the population, were made without signing in; 4,496 came from 1,853 accounts. That is 3.9 anonymous uploads for every account upload, and it is the single most consequential fact on this page: a study of this category built from account records would miss most of the behaviour it set out to describe.
The two halves are not the same population wearing different hats. 99.1% of guest uploads had already been retired by expiry when the data was taken, against 60.7% of account uploads, because an anonymous upload gets a short fixed lifetime and an account holder can choose a longer one. That difference is exactly why the previous edition, which kept only what was still live, saw almost none of this.
998 guest uploads in the window were later claimed into an account. Of those, 529 have their account row inside this window too, and those are counted ONCE, as the account upload they became, which is why the guest figure here is 17,535 and not the 18,064 guest rows the window contains. The Methodology below states that rule in full.
- Shared without an account17,535 (79.6%)
- Shared from an account4,496 (20.4%)
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). A guest upload claimed into an account inside the window is counted once, as the account upload it became.
Nearly a third of uploads are never delivered to anyone
6,971 uploads, 31.6% of the population, had no recorded delivery at all. That is an upper bound rather than a settled figure: an upload made near the end of the window had hours in which to be installed, not a month. Another 11,149 were delivered exactly once, which is the shape of a developer checking their own link before sending it.
The interesting part is what does NOT vary. 32.0% of account uploads and 31.6% of guest uploads were never delivered — 1,437 and 5,534 respectively. Signing up, choosing an expiry, and coming back to a dashboard make almost no difference to whether the artifact ever reaches a device.
A delivery here is a row in the install-event log, counted against the build and, for a build claimed from a guest link, against that guest link as well. The per-upload download counter is not used: a claim starts a fresh counter on a new row while the deliveries stay on the guest row, so counting the column reports uploads as untouched that had already been handed to a tester.
The useful reading is not that testers are lazy. It is that a large share of build sharing is not distribution at all: it is a developer producing an artifact, parking it somewhere addressable, and moving on. Any tool in this category that reports uploads as a success metric is counting that behaviour as delivery.
- Never delivered6,971
- Delivered once11,149
- 2 to 5 deliveries3,476
- 6 to 20 deliveries362
- 21 to 100 deliveries52
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). Buckets holding fewer than 50 uploads, or belonging to fewer than 5 distinct owners, are suppressed and absent from the chart, so the bars sum to 22,010 of 22,031 uploads, leaving 21 in buckets too small to publish. The suppressed bucket is uploads delivered more than 100 times.
The top 1% of uploads carry 42.4% of all deliveries
Deliveries per upload: median 1, 90th percentile 2, 37,271 in total. The busiest 221 uploads — held by 156 distinct owners, so this is a broad group and not one team's traffic — account for 42.4% of them. A small number of links behave like a release channel, and everything else behaves like a handoff between two people.
The two halves of the population carry almost the same load: 18,025 deliveries against 19,246, from 4,496 account uploads and 17,535 guest ones. An account upload is delivered more often on average, and there are far fewer of them.
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days).
For anyone sizing infrastructure for this workload, that concentration is the design constraint: capacity has to be planned for a handful of links, while the median link costs almost nothing to serve. No single upload's delivery count is published, because a maximum is one owner's exact value; the total is, so every percentage on this page can be recomputed from the counts beside it.
An upload that gets delivered is usually delivered within the hour
This population is the exact complement of the never-delivered one above: 6,971 uploads were never delivered and 15,060 were, which is every upload in the window, because both sections count the same log. Of the 15,060 uploads whose first delivery the window could time, 14,165 were delivered within an hour of upload.
That raw figure is censored by construction and cannot be trusted on its own: an upload made on the last day of the window had no opportunity to fall into a “more than 7 days” bucket, so the first bucket is inflated in a way no footnote fixes. The second chart is the fix. It keeps only uploads with at least 7 days of observable window — 11,272 of them were delivered — so every bucket up to 7 days was reachable for every upload in it. The first-hour share moves from 94.1% to 93.9%, which is the point: the concentration in the first hour is real, and the censoring was not what produced it.
- Within 1 hour of upload14,165
- 1 hour to 1 day805
- 1 to 3 days63
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). Buckets holding fewer than 50 uploads, or belonging to fewer than 5 distinct owners, are suppressed and absent from the chart, so the bars sum to 15,033 of 15,060 uploads, leaving 27 in buckets too small to publish. Right-censored: an upload made late in the window could not reach a later bucket. Uploads never delivered are outside this population by construction.
- Within 1 hour of upload10,589
- 1 hour to 1 day603
- 1 to 3 days55
BetaDrop, the 11,272 delivered uploads among the 16,614 made at least 7 days before the window closed, 3 August 2026 to 2 September 2026. Buckets holding fewer than 50 uploads, or belonging to fewer than 5 distinct owners, are suppressed and absent from the chart, so the bars sum to 11,247 of 11,272 uploads, leaving 25 in buckets too small to publish. This is the version that supports a claim about timing.
The operational reading: a link that breaks in the first hour breaks the common case, so expiry policy, notification timing and support response are aimed at the wrong interval if they assume a long tail. What neither chart can support is the stronger claim that a slow delivery never happens — only that it is rare within seven days of upload, which is as far as this window sees.
What a beta build actually weighs
iOS uploads are heavier than Android uploads at every point measured: a median of 52.8 MB against 38.6 MB, and a 90th percentile of 211.5 MB against 145.5 MB. This report measures the gap and does not explain it: packaging, per-device slicing and compression all differ between the two formats, and none of that is measured here. The cohorts differ too, in the opposite direction to the obvious guess — the median iOS upload from an account is 47.2 MB while the median anonymous one is 52.8 MB.
- iOS (.ipa), all uploads, median52.8 MB (n=14,217)
- iOS (.ipa), all uploads, 90th percentile211.5 MB
- Android (.apk), all uploads, median38.6 MB (n=7,814)
- Android (.apk), all uploads, 90th percentile145.5 MB
- iOS (.ipa), account uploads, median47.2 MB (n=2,428)
- iOS (.ipa), account uploads, 90th percentile223 MB
- Android (.apk), account uploads, median36.5 MB (n=2,068)
- Android (.apk), account uploads, 90th percentile131.6 MB
- iOS (.ipa), guest uploads, median52.8 MB (n=11,789)
- iOS (.ipa), guest uploads, 90th percentile207.9 MB
- Android (.apk), guest uploads, median39.5 MB (n=5,746)
- Android (.apk), guest uploads, 90th percentile152.5 MB
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). MB means 1024 x 1024 bytes. Every cohort shown cleared both floors; no upload in the window is missing a size.
- Under 10 MB3,654
- 10 to 50 MB7,592
- 50 to 100 MB4,207
- 100 to 200 MB4,612
- 200 to 350 MB1,468
- Over 350 MB498
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). Every bucket cleared both floors, so the bars sum to all 22,031 uploads.
The histogram, not the average, is the shape to design around. 70.1% of the population sits under 100 MB, while the heaviest bucket, over 350 MB, holds 498 uploads.
How far back uploads still reach
The minimum OS version recorded at upload is the deployment target the team chose, so it measures a decision rather than a device population. On iOS the largest single bucket is iOS 15 with 3,704 of the 13,562 uploads shown. On Android it is API 24 (Android 7.0) with 3,978 of 7,280. The long tail below iOS 10 is real: developers are still shipping betas against deployment targets a decade old.
- iOS 3181
- iOS 4146
- iOS 5158
- iOS 6159
- iOS 7270
- iOS 8487
- iOS 9282
- iOS 101,424
- iOS 11610
- iOS 12718
- iOS 131,494
- iOS 141,131
- iOS 153,704
- iOS 161,645
- iOS 17655
- iOS 18276
- iOS 2654
- iOS 27168
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). Buckets holding fewer than 50 uploads, or belonging to fewer than 5 distinct owners, are suppressed and absent from the chart, so the bars sum to 13,562 of 13,610 uploads, leaving 48 in buckets too small to publish. 826 uploads carry no recorded minimum and 2 carry one that does not parse; both are outside this chart's denominator.
- API 14 (Android 4.0)60
- API 16 (Android 4.1)110
- API 19 (Android 4.4)118
- API 21 (Android 5.0)806
- API 22 (Android 5.1)107
- API 23 (Android 6.0)683
- API 24 (Android 7.0)3,978
- API 25 (Android 7.1)93
- API 26 (Android 8.0)912
- API 28 (Android 9)158
- API 29 (Android 10)168
- API 30 (Android 11)87
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). Buckets holding fewer than 50 uploads, or belonging to fewer than 5 distinct owners, are suppressed and absent from the chart, so the bars sum to 7,280 of 7,593 uploads, leaving 313 in buckets too small to publish.
What testers are running, and the half of it that is not publishable
Tester OS is read from the user agent of the device that opened the install page. On iOS that value is real: iOS 18 accounts for 79.0% of the 9,119 iOS install events in this population, and the 6 major versions that clear both floors are the only ones published.
- iOS 15217
- iOS 16349
- iOS 17110
- iOS 187,202
- iOS 261,096
- iOS 27116
BetaDrop install events against the uploads in this report's population, 3 August 2026 to 2 September 2026. Rolled up to the major version FIRST, then suppressed: major versions with fewer than 50 events, or fewer than 5 distinct targets, are absent, leaving 29 events across 6 smaller versions. iPad reports itself as iOS in the user agent and is included.
Why there is no Android version chart here
Chrome on Android freezes the OS version it reports in its user agent, so those rows pile up on one value and describe the browser, not the fleet. The raw rows exist and they are large — 32,751 events in this population, more than three times the iOS total — and publishing them as a version distribution would be wrong rather than imprecise: they measure browser behaviour, not what testers run. Anyone building an Android version breakdown from web analytics has the same problem, which is the reason this section exists at all rather than being quietly dropped.
What happens on an install page
69,899 install events were recorded in the window against this report's population: 27,614 install pages opened, 37,271 builds delivered, and 5,014 requests refused before delivery. For scale, the platform recorded 117,319 install events in the same window against uploads of every age; this report covers a month of uploads, not a month of traffic, and those are different things.
Note what these three numbers do not permit: deliveries outnumber install-page opens, because a delivery does not require an install page to have been opened first. There is therefore no conversion rate to compute from them, and this page publishes none. The one rate the data does support is refusals as a share of delivery attempts: 11.9%.
- Install page opened27,614
- Build delivered37,271
- Refused before delivery5,014
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). Events against this report's population only, not across all platform traffic.
A refusal is worth splitting, because it is not one thing. The largest single reason is the one below reading “Link had expired”, at 2,572 events, which is a policy applying as intended. But 969 of them — 19.3% of all refusals — are the build file being missing on the server for a link that is still valid, which is this service failing rather than a policy applying. Publishing a service-failure rate as if it were a user outcome is the worst error this page could make, so the split is published rather than summarised.
- Link had expired2,572
- Upload had been deleted1,162
- Build file missing on the server969
- Password required and not supplied149
- Link had been disabled88
BetaDrop, 22,031 uploads, 3 August 2026 to 2 September 2026 (31 days). Reasons holding fewer than 50 events, or fewer than 5 distinct targets, are suppressed: the bars sum to 4,940 of the 5,014 refusals, leaving 74 across 3 smaller reasons.
What this report cannot tell you is how many of those 37,271 deliveries became working installs. The platform records a delivery, not an install outcome: the status column on a delivery row is written as a constant by the only code path that writes it, and iOS hands back no install-outcome callback, so no code path here can record a failed install. A failure count from this data would be zero by construction rather than by observation, which is why none is published. Nothing on this page says anything about install failure rates.
Reach, expiry and automation
- Uploads came from 172 countries and were delivered in 164, counting only the events that were deliveries; counting every touch of an install page instead gives 172, which is a different and larger claim. The anonymous half is the wider one: 169 upload countries against 123 for account uploads. All of these are counts of countries and nothing more — they say nothing about whether any particular upload crossed a border, which is a different measurement this report does not make.
- Expiry is an ACCOUNT-side setting, so this one covers the 4,496 account uploads only: a guest upload gets a fixed lifetime and never chooses. 4,394 of them, 97.7%, were set to expire on a clock rather than on a download or device cap. The remaining 102 split across 4 other settings, each under a floor, which is why no second bar appears even though the residual is larger than the row floor itself.
- 71 accounts held a live API token on 2026-09-03. That is a point-in-time count over all history rather than a windowed one, so it is not a rate against anything else on this page, and it is one of the 4 figures read as of the query date rather than fixed by the window. The windowed split of command-line against browser uploads is withheld in full: The upload-method split is all-or-nothing under three floors, and at least one of its buckets does not clear them in this window. Publishing the bucket that does clear them, beside the population total, would recover the other by subtraction.
- Uploads per uploader in the window: median 1 and 90th percentile 4 per account, against median 1 and 90th percentile 3 per anonymous device. Both halves are dominated by people who upload once.
Tool counters, which are running totals
These come from a counter table that holds one running total per tool and keeps no per-run rows, so they cannot be windowed and are not comparable with any figure above. They are published because the ordering between them is informative on its own: inspecting a build is a common enough job to be worth a tool.
- iOS build upload4,821
- Android build upload4,138
- App Store lookup1,448
- IPA inspector1,205
- APK inspector412
- CLI394
- UDID checker271
- Provisioning profile decoder134
Not windowed. Cumulative counters, published only as a ratio between tools. Counters under 50 runs are suppressed like every other bucket on this page, 1 of them here. The owner floor cannot be applied to this table at all: it carries no account column, so nothing in it can be attributed to a person and nothing about distinct owners is claimed.
Methodology
- Population
- The unit is an UPLOAD, and there are 22,031 of them: every row created inside the window, whether or not expiry has since retired it, from both halves of the product. 4,496 came from 1,853 signed-in accounts and 17,535 from 9,989 devices with no account at all. An upload made without signing in is the same product action as one made with an account, so it belongs in the population; leaving it out, as the previous edition did, hides four fifths of what the service does.
- Retirement is not a filter
- Expiry retires an upload by flagging its row; it does not delete it, and no production path issues a delete. This edition therefore counts retired uploads, and reports retirement as an attribute: 2,731 account uploads (60.7%) and 17,897 of the 18,064 guest rows (99.1%) had already been retired when the data was taken. The consequence is worth stating plainly: because nothing here depends on what was live on a particular morning, the population does not decay. Re-running the published query over the same dates returns these uploads rather than a shrinking remnant of them — minus any account the exclusion list has picked up since, which is what the next entry is about.
- What a re-run does and does not reproduce
- Everything else is fixed by the window: retirement is not a filter, so re-running the published query over the same dates returns these uploads rather than a shrinking remnant of them. But “reproducible” is not the whole truth and this page will not print it unqualified, because the second run of the query falsified it: two runs minutes apart returned populations six uploads apart. The following are read as of 2026-09-03 rather than fixed by the window, and a later re-run will move them:
- the exclusion list, which is evaluated at query time and can only grow, so a later re-run returns this population minus whatever has since been excluded
- the retired shares, which describe what expiry had already done when the data was taken
- the live API token count, which reads the clock
- the tool run counters, which are running totals
- The double-count rule, because one upload can have two rows
- Claiming an anonymous upload into an account inserts a NEW row against the same file and does not delete the anonymous one, so the same physical upload exists twice in the database. The rule is: an anonymous upload claimed into an account inside this window is counted once, as the account upload it became, and its pre-claim deliveries are attributed there too. The window contains 18,064 anonymous rows, of which 529 fold into an account upload, leaving 17,535; 4,496 plus 17,535 is 22,031. An anonymous upload claimed after the window closed, or by an account the exclusion rule removed, stays anonymous here, because there is no in-scope account upload for it to collapse into. The query emits both halves of that arithmetic as checks and the page fails to build if either is off by one.
- What a delivery is
- One row in the install-event log, of type “install”, timestamped inside the window, counted against the build and against the anonymous upload it was claimed from. The per-upload download counter is never used: for a claimed upload the pre-claim deliveries sit on the old row while the new row's counter starts at zero, so the column reports already-delivered uploads as untouched. Events are windowed as well as uploads, which is the other half of reproducibility — an unwindowed delivery count keeps growing after publication, and a reader re-running the query would have no way to tell an error from an older edition.
- Censoring
- An upload made on the last day of the window had hours in which to be delivered, not a month. So the never-delivered share is an upper bound, and the raw time-to-first-delivery distribution cannot place a late upload in a late bucket at all. That is why the timing section publishes two charts: the raw one, and one restricted to the 16,614 uploads made at least 7 days before the window closed, for which every bucket up to 7 days was reachable. The two agree to within a fraction of a point, which is a result rather than a reassurance.
- Exclusions
- 20 accounts are on the exclusion list and none of their uploads appear in any figure: any takedown, abuse strike, account suspension or refused upload screening, and on the guest side any upload named in a content takedown. That list is every such account in the database rather than a count of this window; 15 of them uploaded inside it, and 46 uploads were removed on that basis. The rule reaches the anonymous half too, where there is no account to exclude: those are removed by checking the takedown record directly against the upload, which removed 15 more. Abuse traffic is real traffic, and leaving it in would inflate the delivery and country figures with activity that has nothing to do with beta testing.
- Suppression
- Every bucketed breakdown must clear two floors in the query itself, not one: at least 50 rows AND at least 5 distinct owners, or distinct targets where the rows are events. An owner is the account for an account upload and the uploading device for an anonymous one; neither value leaves the query, only the count of distinct ones. A bucket that fails either floor is absent rather than rounded, and every affected chart names its own residual so nobody has to subtract. The second floor exists because the first cannot do the job alone: a 50-row bucket owned by two owners clears a row count and identifies a pair. The upload-source split carries a third floor — no single account may hold more than half of any published bucket — and is withheld in full in this edition, because publishing the bucket that clears the floors beside the population total would recover the other by subtraction. There are no exemptions, and the withheld bucket's size is not recorded anywhere that ships.
- Identifiers
- none: no app names, bundle identifiers, package names, accounts, e-mail addresses, IP addresses or subdomains. Country data is published as a count of countries and never as a per-country table, because a country with one upload plus a platform and a size is a re-identification path. No maximum is published anywhere, for the same reason: a maximum is one upload's exact value taken from inside a group too small to publish.
- The user agent caveat
- Chrome on Android freezes the OS version it reports in its user agent, so those rows pile up on one value and describe the browser, not the fleet. The Android version split is therefore withheld in full, at any threshold and with any footnote, and the query selects the iOS family only so a later edit cannot publish it by loosening a filter downstream. iOS user agents do not have this problem and that split is published.
- Units and arithmetic
- MB means 1024 x 1024 bytes, the unit the product prints. The window is 3 August 2026 to 2 September 2026 inclusive, 31 days, and its length is derived from its bounds rather than typed. Every percentage on this page is computed from the published counts at render time, so all of them can be recomputed from the numbers beside them — including the concentration share, whose denominator the previous edition withheld.
How this was measured
One committed SQL file produced every number here, run against the production database on 2026-09-03 over a read-only path documented at the top of that file. It runs inside a read-only transaction, which the database enforces rather than the author promising it, and it emits nothing but a section, a key and a count: the exclusion rule, both suppression floors and the window are applied in the query, before any data leaves the server.
The query also emits its own checks. Three of them must be zero — that the population partitions exactly into its two halves, that every in-scope delivery is attributed to exactly one upload, and that no upload's first delivery predates its own upload time — and the site refuses to build if any of them, or the 7 other identities a reader would check by hand, fails. That is not decoration: the previous edition shipped 45 individually correct figures, two of which could not both be true, because two sections were counting from different ledgers.
Its output is stored as a single data file in this site's repository, and this page renders from that file. No figure is typed into the prose, so a number cannot be right in the chart and wrong in the sentence. The report is refreshed quarterly with a new window; because retirement is no longer a filter, re-running the same file over the same dates returns this population rather than a shrinking remnant of it, with the as-of-query-date exceptions the Methodology lists. Each edition keeps its own window, and superseded editions are marked rather than silently rewritten — the notice at the top of this page is the current example. The query itself is published alongside the figures, as the SQL file that produced them, so the method can be read rather than taken on trust.
Reuse and citation
The figures are free to reuse with attribution under CC BY 4.0. Cite as: BetaDrop, Beta Distribution Report 2026, edition 2026-09.1, 2026-09-03, and link to this page. The full table is available as a CSV file, and the query that produced it is published here. Corrections are welcome through the contact page, and any figure that changes will be marked on the page rather than replaced quietly.
Where the data comes from
BetaDrop is the service these uploads were shared through. A developer uploads an .ipa or an .apk, with or without an account, gets one install link and a QR code, and testers install from a browser without accounts of their own. That is the whole surface the measurements above describe, which is also its limit: this is what beta distribution looks like on one service, not on every service.