FunnelHound / Guides / Analytics Reports API
App Store Connect Analytics Reports API: every report, every call
The Analytics Reports API is how App Analytics numbers (impressions, page views, downloads, installs, deletions, sessions, crashes) leave App Store Connect without a browser. It is built from four objects: a request you create once per app, the reports Apple generates for it, the dated instances of each report, and the segments of each instance, each one a URL to a gzip CSV. Four GETs after one POST, and a 48-hour wait the first time.
Before the first call
You need an API key with a role that can see App Analytics (Developer is enough, Finance alone is not), the app's numeric ID from GET /v1/apps, and a signed ES256 token in the Authorization: Bearer header. The token recipe is in the Python guide; everything below assumes it is in $TOKEN.
Call 1: create the request
POST https://api.appstoreconnect.apple.com/v1/analyticsReportRequests
{
"data": {
"type": "analyticsReportRequests",
"attributes": { "accessType": "ONGOING" },
"relationships": {
"app": { "data": { "type": "apps", "id": "6793297146" } }
}
}
}
- ONGOING: Apple keeps generating a new instance every day until you delete the request. One per app is all you ever need.
- ONE_TIME_SNAPSHOT: a single backfill of the history Apple holds for the app. Use it once to seed a database, then rely on ONGOING.
- The response carries the request's id and a stoppedDueToInactivity flag. Apple stops an ONGOING request that nobody reads for a long stretch; the fix is to delete it and create it again.
- Already have one? GET /v1/apps/{appId}/analyticsReportRequests lists existing requests, so a script can look before it posts.
Call 2: list the reports
GET /v1/analyticsReportRequests/{requestId}/reports?filter[category]=APP_STORE_ENGAGEMENT
Each report has a name and a category. The five categories, and the reports inside them that matter for a store funnel:
| Category | Report | What it holds | Funnel stage |
|---|---|---|---|
| APP_STORE_ENGAGEMENT | App Store Discovery and Engagement | Impressions and product page views by source type, page type, device, territory; counts and unique counts | Impressions, page views |
| APP_STORE_ENGAGEMENT | App Store Downloads | First-time downloads, redownloads, updates, by source and territory | Installs |
| APP_STORE_ENGAGEMENT | App Store Installation and Deletion | Installations and deletions per day, by device and territory | Deletions |
| APP_STORE_ENGAGEMENT | App Store Pre-orders | Pre-order starts and cancellations | Launch week |
| COMMERCE | App Store Purchases | Purchases, proceeds, paying users, by product and territory | Purchases |
| APP_USAGE | App Sessions, App Crashes | Sessions, active devices, crash counts | Retention signals |
| FRAMEWORK_USAGE | various | Which Apple frameworks your binary calls | Not a funnel report |
| PERFORMANCE | various | Launch time, hang rate, disk writes and similar | Not a funnel report |
Many reports exist in a Standard and a Detailed variant. Detailed adds dimensions (page title, source info, platform version) and multiplies the row count. Start with Standard; move to Detailed only when a question needs the extra column.
Call 3: list the instances
GET /v1/analyticsReports/{reportId}/instances?filter[granularity]=DAILY&filter[processingDate]=2026-10-30
- granularity is DAILY, WEEKLY or MONTHLY. Daily instances arrive about two days behind the calendar.
- processingDate is the day Apple generated the instance, which for a daily report is the data day. Filter on it to fetch exactly yesterday-minus-two.
- No instances at all means the request is younger than 48 hours or was stopped for inactivity. An empty list is not an error.
Call 4: list the segments and download
GET /v1/analyticsReportInstances/{instanceId}/segments
→ { "attributes": { "url": "https://…", "checksum": "…", "sizeInBytes": 18244 } }
curl -sL "$URL" | gunzip > discovery-2026-10-30.csv
- The segment URL is pre-signed: fetch it without the bearer header, and soon, because it expires.
- Large apps get several segments per instance. Concatenate them; the header row repeats.
- Store the checksum next to the file if the data feeds anything that matters; a changed checksum on a re-download means Apple regenerated the instance.
The columns that build a funnel
- Discovery and Engagement: Date, App Name, App Apple Identifier, Event, Page Type, Source Type, Engagement Type, Device, Platform Version, Territory, Counts, Unique Counts. Event takes values such as Impression and Page View; Unique Counts is what the web dashboard calls unique device impressions.
- Downloads: Date, App Apple Identifier, Download Type (First-time download, Redownload, Manual update, Auto-update), Source Type, Device, Territory, Counts. Total Downloads on the dashboard is first-time plus redownloads.
- Installation and Deletion: Date, App Apple Identifier, Event (Install, Delete), Device, Platform Version, Territory, Counts, Unique Counts. The only place deletions exist in machine-readable form.
All App Store engagement reports count every device. Usage and purchase reports count only users who opted in to share analytics with developers, the gap described in the opt-in data guide. Dividing an opt-in number by an all-devices number produces a rate that is quietly too low.
Operational rules
- 48 hours after the first ONGOING request before any instance exists. Build the empty case into the script and do not re-post the request in the meantime; duplicates are rejected.
- One ONGOING request per app. Deleting it (DELETE /v1/analyticsReportRequests/{id}) stops generation; a new one restarts the 48-hour clock.
- 403 means the key's role or app access is wrong, not the token. A conflict error on POST means a request already exists for that app and access type; list first. 429 means slow down; the per-hour limit is generous but a four-level loop across many apps reaches it.
- Two-day lag on daily instances. "Yesterday" is never ready; "three days ago" always is.
- Segment URLs expire. List and download in the same run.
Reading this without writing it
FunnelHound is this reference compiled into an app: it creates the ONGOING request for each app, walks reports, instances and segments on your iPhone, gunzips the CSVs on the device, and draws Impression and Page View events, downloads, deletions and purchases as one funnel. The 48-hour wait is the only thing it cannot remove, which is why a fresh connection shows a waiting state instead of zeros.
Four calls, zero code
Connect an API key and FunnelHound runs the whole Analytics Reports chain for every app on the account, on your phone, every day. Impressions through purchases, with deletions and milestones on the same chart.
Get FunnelHoundData notes: endpoints, access types, categories and granularities follow Apple's App Store Connect API documentation (Analytics section) as of 2026. Report names and the columns listed are the ones FunnelHound parses from live CSVs; Apple adds columns and reports over time, so treat the column lists as a starting point and read the header row of your own files. Sample rows are illustrative, 6793297146 is FunnelHound's App Store ID.