Methodology
How the data is collected
Last updated
This page is for people who need to know where a number came from before they act on it. It describes what we read, how often, how each line is judged, and where the data runs out.
Sources
- App Store and Google Play: store sitemaps, public top charts, developer pages and each app's public listing.
- The
app-ads.txtfile on the developer website named in the store listing. - The
sellers.jsonfile of every network named in those files. robots.txton every host, which we obey.- For website ads.txt coverage (Business and up): the public Tranco top-sites list and each site's
ads.txt.
That's all. No SDK, no device panel, no bidstream, no data bought from third parties. The crawler is StoqlabBot, and it says so in its user agent.
Which apps we track
As of 10 October 2026, Stoqlab tracks 2.2M+ live apps (2M+ iOS, 250k+ Android), has checked the app-ads.txt of 500k+ developer hosts (89k+ files found) and reads the sellers.json of 1,300+ networks. An app counts as tracked once we have read its store listing and it is live in the store. Delisted apps keep their history and are counted apart; apps we have discovered but not read yet are not counted. Public figures are rounded down ("85k+"). New apps arrive from the store sitemaps, the charts and the developer pages of apps we already track, so the catalogue grows every day. Apple TV apps are picked up from the App Store data of tracked apps; no other connected-TV store is crawled.
How often we read each source
Apps are sorted into three tiers, recomputed every night. Top: on a store chart in the past seven days, or at least 1,000,000 installs on Google Play, or at least 50,000 ratings on the App Store. Middle: the developer has an app-ads.txt, or the listing says the app contains ads. Long tail: every other live app.
| Source | How often |
|---|---|
| App Store listings | Every tracked app, daily. |
| Google Play listings | Top apps daily. While the Android catalogue is still being built, first reads of new apps go first: apps with ads or an app-ads.txt are re-read every two weeks and the long tail every two months (every six months when the listing names no website). After that: top apps daily, apps with ads or an app-ads.txt weekly, everything else monthly (every three months when the listing names no website). |
| app-ads.txt | Same three tiers as Google Play: daily, weekly, monthly. A file that just changed is read again within 24 hours. A failed fetch is retried after 6 hours, then 12, then 24, and so on. |
| sellers.json | Daily for networks named in 20 or more app-ads.txt files, weekly for the rest (networks named in at least 2 files, plus a few large ones). |
| Store charts | Daily: App Store top charts in every storefront, plus genre charts and Google Play category pages in the 30 largest markets. |
| Per-country availability | App Store: top apps daily in the main storefronts and weekly in the others; top apps and apps with ads every 30 days in the 30 largest ad markets. Google Play: top apps weekly per country, then apps with an app-ads.txt every 30 days. |
| Domain checker | On demand. Usually done within a couple of minutes. |
| Coverage numbers | Recounted every 10 minutes. The site and the dashboard show the same counts. |
These are target intervals. Every host gets a small request budget, and when a store or a website slows us down, the crawler backs off. On a bad day a cycle finishes late. Every app and file shows when it was last read.
How we fetch and parse the file
- We take the website from the store listing and request
/app-ads.txton its host, over HTTPS first, then HTTP. - Redirects within the same root domain are followed. One redirect to another domain is allowed; a second one is treated as an error, as the IAB spec asks.
- The file has to be plain text. An HTML page is recorded as invalid, a 404 as missing. Downloads are capped at 5 MB.
- Every record and variable (OWNERDOMAIN, MANAGERDOMAIN and the rest) is parsed. Lines that don't parse are kept with the reason.
- Each distinct version of a file is stored with the dates we first and last saw it, so you can diff any two.
How each line is checked
For every line, we look up the account ID in the sellers.json of the network the line names, and compare the seller's type with the relationship the line declares. DIRECT should point at a PUBLISHER account, RESELLER at an INTERMEDIARY. Each line gets one of these statuses:
- Verified ok · Fine
- The account ID is listed in the ad system's sellers.json with a type matching the declared relationship.
- Seller ID not found id_missing · Error
- The account ID is not (or no longer) listed in the ad system's sellers.json. Often a stale line.
- DIRECT but intermediary direct_but_intermediary · Error
- Declared DIRECT, but sellers.json lists this account as an INTERMEDIARY.
- RESELLER but publisher reseller_but_publisher · Error
- Declared RESELLER, but sellers.json lists this account as a PUBLISHER.
- Seller type BOTH both · Warning
- sellers.json lists this account as BOTH publisher and intermediary; the relationship cannot be confirmed.
- Confidential seller confidential · Warning
- The seller is marked confidential in sellers.json.
- No sellers.json no_sellers_json · Warning
- The ad system does not publish a usable sellers.json.
- Unchecked unchecked · Pending
- Not cross-checked yet.
The API reference has the same table with what a publisher and a buyer should do about each status: check_status values.
How the supply tree is built
Each line of an app's app-ads.txt is a first hop. If sellers.json says the seller behind it is an intermediary, and that intermediary's domain is itself a network with a sellers.json, we follow it to the next hop. We stop at four hops, and a network never appears twice on one branch, so loops are cut. The tree belongs to the file, so every app of a developer that shares one app-ads.txt has the same tree.
We show the tree the way buyers read it, from the inventory outwards: the developer, then its direct sellers (the DIRECT lines), then the resellers buying from each of them. A RESELLER line goes under a direct seller when its account leads there in sellers.json: either the hops above reach the account the developer declared DIRECT, or the reseller's sellers.json entry names an intermediary whose domain is that direct seller (for example, a reseller account that its exchange lists as an intermediary with the domain start.io sits under the developer's start.io line). A reseller of a reseller goes one level deeper. Resellers we cannot place are listed apart, with the reason: the account is not in sellers.json, it is confidential, it has no domain, it is listed as a publisher, or its domain is not one of the app's direct sellers.
It is the declared path: what the files say is allowed. Whether a given auction actually went that way is something only the bid request can tell you.
How the supply path score is computed
Every app gets a supply path score from 0 to 100: one number for how short and clean the declared path to its inventory is. It is worked out from the app's app-ads.txt (shared by every app of the developer that uses the file), right after each line is checked in both directions, and it is the same formula for every app. Six parts, each between 0 and 1, weighted to add up to 100:
- DIRECT share direct · 25 points
- DIRECT lines out of all lines.
- Confirmed both ways verified · 30 points
- Lines whose sellers.json entry is listed with the right type and points back to the publisher or a known reseller, out of the checked lines.
- Live sellers sellers · 20 points
- Lines whose seller is still in the network's sellers.json and whose network publishes one.
- Reseller spread resellers · 10 points
- Distinct networks declared as RESELLER: no penalty up to 5, zero from 50.
- Chain depth depth · 10 points
- Average hops behind each line in the declared supply tree: 1 hop scores full marks, 4 hops none.
- Named sellers transparency · 5 points
- Lines whose seller is not marked confidential.
In full: score = 25 × DIRECT lines ÷ lines + 30 × confirmed lines ÷ checked lines + 20 × (1 − (lines whose seller is missing from sellers.json + lines whose network has no sellers.json) ÷ lines) + 10 × reseller spread + 10 × (1 − (average depth − 1) ÷ 3) + 5 × (1 − confidential sellers ÷ lines), rounded to the nearest whole number. Reseller spread is 1 up to 5 distinct networks declared as RESELLER and falls in a straight line to 0 at 50. A file with no lines has no score. Scores of 70 and up show green, 40 to 69 amber, below 40 red.
Chain depth. For each line we walk the declared supply tree below it (see above) and record the deepest hop it reaches: 1 when nothing sits behind the seller, up to 4. The app's chain depth is the deepest and the average of those values. On a network's page, the score and depth are the averages over the apps whose app-ads.txt names that network, weighted by app and recomputed once a day.
It is a summary of declarations, not a quality rating of the inventory: a long reseller list can be perfectly legitimate. Use it to sort and to decide where to look first. Every plan sees the score; the breakdown and the chain depth come with the supply tree (Professional and up).
Delistings
An app is marked delisted once its store page has been missing on at least two separate days. For the App Store we also require the app to be missing from a fallback storefront, and we hold off while Apple's own sitemap still lists it. Delisted apps keep their history, and you can see which sellers still declare them.
What the data can't tell you
- Traffic. We see authorisations, not impressions, bids or spend.
- Confidential sellers. When sellers.json hides a seller's name and domain, so do we.
- Networks without a usable sellers.json. Their lines can't be verified and are marked as such.
- Apps with no website in the store listing. There is nowhere to look for app-ads.txt.
- Sites that block StoqlabBot in robots.txt. We respect it and don't read the file.
- App Store installs. Apple doesn't publish them; install counts are Google Play only.
- History before we found a file. Change history starts on the day we first read it.
Something looks wrong?
If a status or a date looks wrong for your app or network, tell us which page and what you expected. Most of the time the file changed after our last read, and the next crawl fixes it. If it doesn't, we want to know.