Four App Store Connect API gotchas that cost us a day each

2026-07-30 · Written from our own App Store work

We wired the App Store Connect API into an internal dashboard so we could see downloads without waiting for Apple's once-a-day report. Four things in that API behave in ways the documentation does not prepare you for. Each one cost us hours, so here they are in full.

1. Sales reports return 406 until you ask for gzip

The obvious request to /v1/salesReports fails. You authenticate correctly, your JWT is valid, every parameter is right — and you get HTTP 406 Not Acceptable with no useful body.

The reason is that this endpoint does not return JSON. It returns a gzipped TSV file, and it refuses the request unless your Accept header says you will take one:

Accept: application/a-gzip, application/json

That a-gzip media type is not a typo and it is not a standard MIME type. Miss it and you will spend an afternoon re-checking your ES256 signing code, which is exactly what we did.

2. The sort parameter is rejected on appStoreVersions

Most collection endpoints in this API accept sort. /appStoreVersions does not — passing it returns 400 rather than ignoring it. Fetch the collection unsorted and pick the entry whose state is READY_FOR_SALE yourself.

The general lesson: query parameters that work on one collection are not guaranteed on the next. Treat each endpoint as its own API.

3. Product type identifiers decide whether your numbers are real

This is the one that actually matters, because getting it wrong does not throw an error — it just gives you numbers that are wrong in your favour, which is the worst kind of bug.

Every row in a sales report carries a Product Type Identifier, and summing units without reading it counts updates and re-downloads as new installs. The prefixes:

  • 1 and F — first-time download
  • 3 — redownload (same account, downloading again)
  • 7 — update
  • IA — in-app purchase or subscription event

We classified by prefix and then checked a 30-day window against the figure shown in App Store Connect's own interface: 800 against Apple's 797. Close enough to trust; the small gap is timezone boundaries at the edges of the window.

One trap inside the trap: 3F starts with a 3, not an F. Order your prefix checks so that redownloads are tested before the F case, or every redownload lands in your first-install bucket.

4. Group by Apple ID, not by app name

We renamed an app. For weeks afterwards our totals showed it twice — once under each name — and the sum looked healthy while the truth was half of it.

The app name in a sales report is whatever the app was called on that day. The stable key is the Apple Identifier. Group on that and keep the most recent name for display.

Metadata endpoints need a different key

Worth knowing before you build: a Sales-role API key reads reports fine but returns 403 on appInfos and appStoreVersionLocalizations. If you only need public metadata — icons, descriptions, screenshots — the unauthenticated iTunes lookup endpoint is simpler and has no role requirements.

Related: as of mid-2026 that lookup endpoint returns an empty screenshotUrls array for many apps regardless of country parameter. We tested four variants and got zero every time. If you need screenshots programmatically, the App Store product page is currently the only reliable source.

0%

Who writes this: BIR is an independent software studio with 44 apps on the App Store, built and shipped in-house. These notes come from problems we hit ourselves — the numbers are ours and you can check the reasoning against your own account.