What ITMS-91053 actually means
ITMS-91053, "Missing API declaration," means your app binary calls at least one API from Apple's Required Reason API list, and your privacy manifest does not declare an approved reason for it. It shows up two ways: as a warning email after you upload a build to App Store Connect, or, less often now, as an outright rejection at review. Either way it names the API category, not the line of code, which is why it feels harder to fix than it is.
This is not a bug in your app. Apple added this requirement so every app has to account for a short list of APIs that are commonly abused for fingerprinting, even when 95% of the apps calling them are doing something completely ordinary, like caching a file or reading a saved setting. Expo apps hit it constantly because the React Native runtime and its common dependencies use exactly those ordinary APIs.
The five Required Reason API categories
Apple's Required Reason list covers five categories. Any dependency that touches one of these needs a declared, approved reason code or your build gets flagged.
| Category | What commonly triggers it in an Expo app |
|---|---|
| UserDefaults access | AsyncStorage and most local key-value storage libraries |
| File timestamp APIs | Image caching, file download, and asset libraries |
| System boot time APIs | Some networking and analytics SDKs use it for retry/backoff timing |
| Disk space APIs | Libraries that check available storage before a download or cache write |
| Active keyboard APIs | Custom keyboard or input-related native modules |
Where the declaration lives in a managed Expo project
You do not hand-edit a native PrivacyInfo.xcprivacy file in a managed Expo project. The declaration lives in app.json (or app.config.js) under expo.ios.privacyManifests.NSPrivacyAccessedAPITypes, as an array of entries pairing an API type with the reason codes that apply to your use of it.
- expo.ios.privacyManifests.NSPrivacyAccessedAPITypes takes an array of objects
- Each object sets NSPrivacyAccessedAPIType to a category constant, e.g. NSPrivacyAccessedAPICategoryUserDefaults
- Each object also sets NSPrivacyAccessedAPITypeReasons to the array of reason codes that apply
When your ios/ folder is not committed, which is the default for a managed project, EAS Build runs expo prebuild before every native build. Prebuild regenerates the Xcode project from app.json fresh, including writing this config into the resulting PrivacyInfo.xcprivacy file. That is why the fix is a config.json edit, not a native code change, and why the declaration survives every rebuild instead of getting wiped.
Finding which dependency is actually the culprit
The App Store Connect warning names an API category, not a package. Here is the order I check, fastest first.
- Search node_modules for PrivacyInfo.xcprivacy files: most well-maintained packages that touch a Required Reason API already ship their own manifest inside their ios/ folder.
- If a dependency ships one, copy its declared NSPrivacyAccessedAPITypes entries into your own app.json privacyManifests block. Apple does not reliably parse every manifest bundled inside a third-party static CocoaPods dependency, so even a package that did the right thing can still get flagged at your app's top level.
- If no dependency manifest exists, upload the build to TestFlight anyway and wait a few minutes. Apple's processor often emails you directly naming the exact missing API category, which is faster than guessing from the App Store Connect warning alone.
- Cross-reference that category against what you actually added recently: a new analytics SDK, a new storage library, a new image-caching package are the usual suspects.
The declare, verify, resubmit loop
I run the same three-step loop on every Expo app before it goes anywhere near App Store Connect, because catching this pre-submission is much cheaper than catching it at review.
- Declare: after adding any new native dependency, check its ios folder for a shipped PrivacyInfo.xcprivacy and merge its reasons into app.json before your next build.
- Verify: run a TestFlight internal build after any dependency change, not just before the final submission. The processing email surfaces missing declarations in minutes.
- Resubmit: fix the app.json entry, rebuild, and re-upload. Because prebuild regenerates the manifest from config every time, you never have to touch native files by hand.
A mistake worth avoiding
The mistake I see most is treating ITMS-91053 as a one-time fix. You clear it, ship, and move on, then six weeks later you add a new SDK for push notifications or in-app purchases and the warning is back, because that new dependency touches a different Required Reason API. Treat the declare-verify-resubmit loop as a permanent part of adding any native dependency, not a checkbox you tick once before your first launch.
Short, practical drops on app ideas, validating fast, store listings, and passing app review. No spam, unsubscribe anytime.
Frequently asked questions
What does ITMS-91053 mean for an Expo app?
It means a dependency in your app calls a Required Reason API (UserDefaults, file timestamp, system boot time, disk space, or active keyboard) and your privacy manifest has no declared reason for it. Apple flags the category, not the exact line of code, so you have to trace which dependency is responsible.
Where do I add the privacy manifest declaration in a managed Expo project?
In app.json (or app.config.js) under expo.ios.privacyManifests.NSPrivacyAccessedAPITypes. You do not hand-edit a native PrivacyInfo.xcprivacy file - EAS Build's prebuild step regenerates the native project from this config on every build.
Does Expo Go trigger this error?
No. Expo Go is a shared client that never gets submitted to the App Store, so it is never checked against Required Reason API declarations. The warning only shows up on a real EAS Build binary once you upload it to App Store Connect or TestFlight.
Why does AsyncStorage trigger a Required Reason API warning?
AsyncStorage and most local key-value storage libraries read and write through UserDefaults under the hood, which is one of Apple's five Required Reason API categories. Almost every React Native app uses some form of local storage, which is why this specific category is the most common trigger.
How do I find which dependency is causing the warning?
Search node_modules for PrivacyInfo.xcprivacy files first - many packages already ship one you can copy the reasons from. If nothing turns up, upload a TestFlight build; Apple's processing email often names the exact API category within minutes, which narrows it down faster than guessing.
Can I ignore ITMS-91053 if it is only a warning, not a rejection?
You can ship past a warning in the short term, but it does not go away and can escalate to a hold at review once Apple tightens enforcement on your category. Treat it as a required fix, not an optional cleanup, and declare the reason properly the first time.
Last reviewed by David on August 8, 2026


