Google Play API: 403 “The caller does not have permission”
403 The caller does not have permission androidpublisher.googleapis.com 403 permissionDenied
The short answer
First, make sure you have the right 403. Searching this phrase mostly
returns people locked out of the Play Console web UI, which is an unrelated problem. This
page is about the API 403 — the one you get from
androidpublisher.googleapis.com when a CI job or a submit command tries to
upload a build.
It means your service account authenticated successfully and then was refused. Almost always, one of these three links in the chain is missing:
- The service account was never invited into Play Console. Creating it
in Google Cloud is not enough. Go to Play Console → Users and permissions →
Invite user, and paste the service account's email — the
...@....iam.gserviceaccount.comaddress from your JSON key. - It has no release permission. Grant Release apps to testing tracks, and Release to production if that is the track you are targeting.
- The grant is not on this app. Permissions can be account-wide or per-app. If you chose per-app, the specific app has to be in the list.
If all three look right and it still fails, see the propagation delay below before you change anything else.
Why this happens
Publishing to Play through the API involves two separate systems that do not share a permission model, and the 403 comes from the gap between them.
Google Cloud owns the identity. That is where the service account exists and where its JSON key is issued. Nothing you do there grants any access to Play — the roles in the Cloud IAM console are about Cloud resources, and it is a common and expensive misreading to add roles there expecting Play to notice.
Play Console owns the authorisation, and treats your service account as if it were a person. It has to be invited as a user, and then given permissions, exactly like a human teammate. Until that invitation exists, your perfectly valid credentials describe somebody Play has never heard of.
The delay nobody mentions
Play permission changes are not immediate. After inviting the service account or changing its grants, it is normal for API calls to keep returning 403 for a while — often minutes, sometimes hours, and occasionally up to about a day. This is the single biggest source of wasted debugging on this error, because the fix was correct and the feedback said otherwise, so people undo it and try something else.
If you have just changed permissions: change nothing further, and retry later before you start rearranging the service account.
The first upload cannot be done by API
A related failure that produces a confusingly similar dead end: Google will not accept an API upload for an app that has never had a build uploaded through the Play Console by hand. The very first release has to be manual. If this is a brand new app and permissions look correct, this is likely your actual problem — and it usually surfaces as a 404 rather than a 403, which is the tell.
Proving it, rather than guessing
Whether the account can actually release is testable without shipping anything. Opening an edit and immediately abandoning it exercises the exact permission a submit needs and leaves nothing behind:
# POST .../edits succeeds only if the account may release to this app
POST https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.your.app/edits
DELETE https://androidpublisher.googleapis.com/androidpublisher/v3/applications/com.your.app/edits/{editId}
A 403 here is a permission problem. A 404 means Play has no such app, which is the first-upload case above. A 401 means the key itself was rejected, which is a different problem entirely.
Catching it before you build
Because that probe is read-only in effect, it can be run as a preflight. leas doctor does exactly that — opens an edit, deletes it, and reports which of the three failures you have, rather than making you infer it from a status code:
▸ Android
✓ Play service account leas-ci@kaya.iam.gserviceaccount.com
✗ Play Developer API authenticated, but not permitted on this app (403)
→ Invite leas-ci@kaya.iam.gserviceaccount.com in Play Console → Users and
permissions and grant "Release apps to testing tracks".Set up leas in 10 minutesFree, MIT licensed, and it never receives your signing keys.
If that didn’t fix it
- Everything looks right and it still 403s. Wait. Propagation can take hours. Changing more settings during that window is how people end up with a broken configuration and a delay.
- Permissions were granted to the wrong account. A Google Cloud project
can hold several service accounts. Confirm the
client_emailin the JSON key you are actually using matches the address invited into Play Console. - The developer account has not accepted the invitation. Service account invitations occasionally sit pending. Check the Users and permissions list shows it active rather than invited.