03-29-2026 18:47
03-29-2026 18:47
Hi everyone,
I'm getting a 403 PERMISSION_DENIED error on all Google Health API (health.googleapis.com/v4) endpoints. Has anyone else encountered this?
Error response:
{ "error": { "code": 403, "message": "com.google.apps.framework.request.ForbiddenException: Could not mint UberMint from GaiaMint", "status": "PERMISSION_DENIED" } }
Example request:
GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints?filter=steps.interval.start_tim... >= "YYYY-MM-DDT00:00:00Z" Authorization: Bearer {access_token}
What I've verified:
The same error occurs on every endpoint I've tried (GET dataPoints, POST dailyRollUp, etc.). The error message looks like an internal token issue rather than a configuration problem on my end.
Is anyone else experiencing this, or has anyone successfully made calls to the Google Health API?
Thanks!
03-30-2026 08:42
03-30-2026 08:42
We've done the exact same as you and are getting the same issue.
04-06-2026 14:44
04-06-2026 14:44
Hey mate, i had the same 403 issue a while back. What actually helped me was regenerating the access token and making sure the test user was fully added to the OAuth consent screen. I also double-checked that all the sensitive scopes were approved for that user. After that, the App API calls started working without the permission errors.
04-06-2026 16:47
Fitbit Developers oversee the SDK and API forums. We're here to answer questions about Fitbit developer tools, assist with projects, and make sure your voice is heard by the development team.
04-06-2026 16:47
Hi @dandaso
Thanks for sharing your questions.
We are currently investigating the issues. We'll get back to you as soon as we have an update.
Best Answer04-06-2026 17:36
04-06-2026 17:36
The issue mentioned above has been confirmed to be resolved as of April 4.
Since no changes were made to this program, it is assumed that the issue was resolved due to an internal update on Google’s side.
Thanks!
Best Answer04-07-2026 12:02
Fitbit Developers oversee the SDK and API forums. We're here to answer questions about Fitbit developer tools, assist with projects, and make sure your voice is heard by the development team.
04-07-2026 12:02
Hi @dandaso
Thanks for letting us know!
Best Answer04-08-2026 10:19
04-08-2026 10:19
Hi @DorisFitbit,
I'm hitting the same issue and it doesn't appear to be resolved for my account. Here's my setup:
- Google Health API enabled in GCP Console
- Web Application OAuth client (not Desktop)
- All health scopes added to OAuth consent screen (Data Access) and confirmed granted via Google's tokeninfo endpoint
- Test user added to OAuth consent screen
- Fitbit account signed in with Google, Fitbit Charge 6 with continuous sync and plenty of data
- Token has refresh_token (access_type=offline, prompt=consent)
Every endpoint returns the same error:
{
"error": {
"code": 403,
"message": "com.google.apps.framework.request.ForbiddenException: Could not mint UberMint from GaiaMint",
"status": "PERMISSION_DENIED"
}
}
The identity endpoint returns a slightly different error:
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}
Calendar API works fine with the same token, confirming the token itself is valid.
Is there an additional enrollment or provisioning step required on Google's side for new GCP projects? The April 4 internal fix mentioned by @dandaso doesn't seem to have applied here.
Thanks
Best Answer04-13-2026 15:33
Fitbit Developers oversee the SDK and API forums. We're here to answer questions about Fitbit developer tools, assist with projects, and make sure your voice is heard by the development team.
04-13-2026 15:33
Hi @newbiedev
Thanks for posting your question.
We need more details from you. I will email you and we can move the discussion to issue tracker.
Best Answer04-15-2026 04:51
04-15-2026 04:51
Hi @DorisFitbit
I am facing the same error.
Request:
GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints?filter=...&pageSize=25
Authorization: Bearer <access_token>
Response:
{
"message": "com.google.apps.framework.request.ForbiddenException: Could not mint UberMint from GaiaMint",
"googleStatus": "PERMISSION_DENIED",
"googleDetails": null
}
Best Answer04-16-2026 14:15
Fitbit Developers oversee the SDK and API forums. We're here to answer questions about Fitbit developer tools, assist with projects, and make sure your voice is heard by the development team.
04-16-2026 14:15
Hi @ammarg
We're happy to provdie supprot. I will email you for futher details. Please check your email. Thank you!
Best Answer04-20-2026 14:03
Fitbit Developers oversee the SDK and API forums. We're here to answer questions about Fitbit developer tools, assist with projects, and make sure your voice is heard by the development team.
04-20-2026 14:03
One cause of this error is the account which consented to share their data is a legacy Fitbit account. The Google Health API does not support legacy Fitbit accounts. It only supports Google accounts. Here are suggestions to try if you get this error message.
If you have followed these steps, please let us know.
Best Answer04-22-2026 19:36
04-22-2026 19:36
@GordonFitbitThat's not the issue. I am logged in via my Google account.
Best Answer04-29-2026 12:36
04-29-2026 12:36
Hi @DorisFitbit @GordonFitbit,
I'm hitting a persistent 403 PERMISSION_DENIED on every Google Health API endpoint (health.googleapis.com/v4/...) — same error as @dandaso, @stevey.sepiida, @newbiedev, and
@ammarg in this thread. I've followed Gordon's troubleshooting steps from the reply and the official Google Health API docs with no change. Could you please put me on
the issue tracker (same as @newbiedev)?
Error response (consistent across all endpoints):
{
"error": {
"code": 403,
"message": "com.google.apps.framework.request.ForbiddenException: Could not mint UberMint from GaiaMint",
"status": "PERMISSION_DENIED"
}
}
Account details:
- Email: nextmab gmail.com
- Fitbit account: created via "Continue with Google" several months ago (never a legacy email/password account)
- Device: Pixel Watch with continuous sync — data is healthy on fitbit.com (600k lifetime steps, recent sleep/activity logs confirmed today)
GCP project:
- Project ID: chet-bot
- Project Number: 875231707536
OAuth setup:
- Google Health API enabled in the project
- OAuth Client type: Desktop app
- OAuth consent screen: Testing mode; my email is in the Test users list
- Scopes granted on the issued token:
- googlehealth.health_metrics_and_measurements.readonly
- googlehealth.activity_and_fitness.readonly
- googlehealth.sleep.readonly
- googlehealth.profile.readonly
- Other Google APIs (Calendar, Drive, Tasks) work fine with the same project and the same token, confirming token validity.
Steps already tried (none changed the error):
1. Revoked the OAuth grant at myaccount.google.com/permissions and re-minted brand-new access + refresh tokens.
2. Signed out of the Fitbit mobile app and signed back in via "Continue with Google" (per Gordon's troubleshooting).
3. Installed Health Connect and enabled "Sync with Health Connect" inside the Fitbit app.
4. Verified my email is in the Test users list on the OAuth consent screen.
Example request that 403s:
GET https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints
?filter=sleep.interval.civil_end_time >= "20260428" AND sleep.interval.civil_end_time < "20260429"
&pageSize=100
Authorization: Bearer <access_token>
Happy to provide request IDs, raw token info, or anything else off-list.
Thanks!
Best Answer04-29-2026 15:52
04-29-2026 15:52
Ok i managed to solve it, if this helps anyone.
TL;DR
If every call to [health.googleapis.com/v4/](https://health.googleapis.com/v4/)... returns:
HTTP/1.1 403 Forbidden
{
"error": {
"code": 403,
"status": "PERMISSION_DENIED",
"message": "com.google.apps.framework.request.ForbiddenException: Could not mint UberMint from GaiaMint"
}
}
…and the same OAuth token works fine against other Google APIs (Gmail, Calendar, Drive, Tasks), the cause is most likely scope-mix in your OAuth grant: the Google Health backend rejects access tokens whose grant also includes consumer scopes (Gmail/Drive/Calendar/etc.) for the same Google identity.
Fix: mint two separate tokens for the same Google account: one with only the 4 googlehealth.*.readonly scopes, and one with everything else. Use them in parallel from your application. Both grants coexist on the same Google account.
This took me from 100% 403 to a working /v4/users/me/dataPoints:list returning real watch data.
---
Symptom
my setup:
- One Google Cloud project with the Google Health API enabled.
- A single OAuth consent screen (External, Testing) with the test user added.
- A single OAuth client (Desktop app type).
- A single token file containing a refresh token granted 11 scopes in one consent flow:
[https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly](https://www.googleapis.c...)
[https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonly](https://www.g...)
[https://www.googleapis.com/auth/googlehealth.sleep.readonly](https://www.googleapis.com/auth/googleh...)
[https://www.googleapis.com/auth/googlehealth.profile.readonly](https://www.googleapis.com/auth/googl...)
[https://www.googleapis.com/auth/spreadsheets](https://www.googleapis.com/auth/spreadsheets)
[https://www.googleapis.com/auth/documents](https://www.googleapis.com/auth/documents)
[https://www.googleapis.com/auth/drive](https://www.googleapis.com/auth/drive)
[https://www.googleapis.com/auth/gmail.modify](https://www.googleapis.com/auth/gmail.modify)
[https://www.googleapis.com/auth/contacts](https://www.googleapis.com/auth/contacts)
[https://www.googleapis.com/auth/calendar](https://www.googleapis.com/auth/calendar)
[https://www.googleapis.com/auth/tasks](https://www.googleapis.com/auth/tasks)
Behavior:
- Gmail / Calendar / Drive / Tasks: 200 OK, returns expected data.
- Anything under [health.googleapis.com/v4/](https://health.googleapis.com/v4/😞 403 PERMISSION_DENIED - Could not mint UberMint from GaiaMint.
The token itself was clearly valid (other APIs accept it). The 403 is not a missing-scope problem - it surfaces from Google's internal identity-link layer ("UberMint" = Health-specific identity, "GaiaMint" = the Google account identity it's being minted from).
---
What did NOT fix it
I exhausted every client-side angle that the official Google Health troubleshooting docs and the Fitbit forum thread mention:
1. Revoking the OAuth grant at [myaccount.google.com/permissions](https://myaccount.google.com/permissions), deleting the local token, and re-running the OAuth flow to mint a brand-new access + refresh token. Same 403.
2. Signing out of the Fitbit mobile app and signing back in via "Continue with Google" (Gordon's official advice for legacy-Fitbit-account cases). The account had been Google-native from the start, so this was a no-op — and the 403 didn't change.
3. Installing Health Connect on the phone and enabling "Sync with Health Connect" inside the Fitbit app. Sync confirmed working (data flowing into Health Connect), 403 unchanged.
4. Verifying the test user is in the OAuth consent screen Test users list. Confirmed.
5. Verifying all 4 googlehealth.*.readonly scopes are saved on the consent screen Data Access page. Confirmed (multiple times - the consent dialog's "Save" button needs to be clicked at the bottom).
6. Confirming the GCP project has the Google Health API enabled in the API library. Confirmed.
I also cross-checked our request shapes against the v4 discovery doc ([https://health.googleapis.com/$discovery/rest?version=v4](https://health.googleapis.com/$discovery/r...)) — URL paths use kebab-case data type IDs (e.g. heart-rate), filter expressions use snake-case (data_type_id="heart_rate"), correct.
So: token valid, scopes granted, project configured, app correct. Yet 403 every time.
---
The root cause: scope-mix in the OAuth grant
OAuth grants on Google work like this: when you complete a consent flow with a set of scopes S, Google records a grant (account, client_id, S). A subsequent flow with a different scope set S' for the same (account, client_id) replaces or merges the grant - the most-recent flow wins for any overlapping scope, and disjoint scope sets coexist.
my hypothesis (which the experiment below confirms): the Google Health backend's identity-link step refuses to mint an UberMint when the inbound access token's grant also contains non-Health consumer scopes for that same Gaia identity. The grant looks "mixed" to it, and it returns 403 instead of issuing the Health identity.
i have not seen Google document this anywhere. But the empirical behavior is clean: pure-Health grant works, mixed grant doesn't, on the same Google account, same OAuth client, same project.
---
The fix: two scope-pure tokens, same account
I split the single 11-scope grant into two separate grants on the same (account, client_id):
- Health token — 4 scopes only:
googlehealth.profile.readonly
googlehealth.sleep.readonly
googlehealth.activity_and_fitness.readonly
googlehealth.health_metrics_and_measurements.readonly
- Main token — the other 7 (Gmail / Drive / Calendar / Tasks / Docs / Sheets / Contacts).
Both are minted by running the same OAuth installed-app flow twice, with different scope lists and writing to different token files. The order matters: mint the main token first, then the health token, because the second flow will revoke any prior grant whose scopes overlap. (Two flows with disjoint scope sets do not overlap, so they coexist.)
The application then loads each token from its own path and uses it for the appropriate API:
- Gmail/Drive/Calendar/Tasks calls -> main token.
- Anything under [health.googleapis.com/v4/](https://health.googleapis.com/v4/) -> health token.
---
Empirical verification
After the split, with both tokens freshly minted:
Gmail with the main token (no Health scopes):
$ curl -H "Authorization: Bearer $MAIN_TOKEN" \
[https://gmail.googleapis.com/gmail/v1/users/me/profile](https://gmail.googleapis.com/gmail/v1/users/...)
{
"emailAddress": "nextmab gmail.com",
"messagesTotal": 10875,
...
}
Google Health with the health-only token (4 scopes):
$ curl -X POST -H "Authorization: Bearer $HEALTH_TOKEN" \
-H "Content-Type: application/json" \
"[https://health.googleapis.com/v4/users/me/dataPoints:list](https://health.googleapis.com/v4/users/me...)" \
-d '{"filter":"data_type_id=\"resting_heart_rate\""}'
{
"dataPoints": [
{
"dataTypeId": "resting_heart_rate",
"restingHeartRate": { "bpm": 62 },
"civilEndTime": { "date": { "year": 2026, "month": 4, "day": 29 }, ... },
"metadata": { "dataOriginType": "DEVICE", ... }
}
]
}
Resting heart rate from a Pixel Watch 4, on the same Google account that had been getting 403'd for days.
Cross-verification - the failing case still fails:
Reissuing the original 11-scope token (Health + consumer scopes in one grant) reproduces the 403 immediately. Splitting them fixes it. The scope set is the only variable.
---
How to apply this fix
If you're stuck on UberMint 403 and your token also has non-Health scopes, follow this exact order. The order matters: you need to mint the Health token first while the consent
screen is restricted to Health scopes only, then add the rest.
1. Revoke any existing grant. Go to https://myaccount.google.com/permissions, find your OAuth client, click Remove access. Delete any local token files. You want to start clean -
no mixed grant lying around.
2. Restrict the consent screen to Health scopes only. In the Google Cloud Console -> APIs & Services -> OAuth consent screen -> Data Access, remove every non-Health scope. The only
scopes saved should be the 4 googlehealth.*.readonly ones:
googlehealth.profile.readonly
googlehealth.sleep.readonly
googlehealth.activity_and_fitness.readonly
googlehealth.health_metrics_and_measurements.readonly
Click update and Save at the bottom of the page.
3. Mint the Health-only token. Run your installed-app OAuth flow requesting only those 4 scopes, and write it to a dedicated path (e.g. google_health_token.json). The consent
dialog should show only the 4 Health scopes - if it shows anything else, the consent screen wasn't fully cleaned up in step 2.
4. Add the rest of your scopes back to the consent screen. Go back to the same Data Access page and add Gmail / Drive / Calendar / Tasks / Docs / Sheets / Contacts (whatever your
app needs). Click Save again.
5. Mint the main (non-Health) token. Run your OAuth flow a second time, this time requesting only the non-Health scopes you just added, and write it to your normal token path
(e.g. google_token.json). The consent dialog this time should show only consumer scopes.
6. Wire your app to use both. Health calls (anything under https://health.googleapis.com/v4/) load credentials from the Health token path. Everything else
(Gmail/Drive/Calendar/Tasks) uses the main token path.
7. Verify with curl. Both tokens should now work for their respective APIs, and https://health.googleapis.com/v4/users/me/dataPoints:list should stop 403'ing.
If you're using google-auth-oauthlib's InstalledAppFlow.from_client_secrets_file(client_secret, scopes), you just call it twice with two different scope lists and write to two
different paths.
Important: never re-run the main OAuth flow with Health scopes added back in. The moment the main flow's grant includes any googlehealth.* scope, it overwrites the scope-pure
Health grant and you're back to 403. Keep your main script's scope list Health-free permanently.
Best Answer04-30-2026 04:14
04-30-2026 04:14
Joining the still-open thread with @newbiedev and @ammarg — please add me to the same internal issue tracker.
Symptom: Persistent HTTP 500 INTERNAL on GET https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints since 20260425, still reproducing on 20260430.
{"error":{"code":500,"message":"Internal error encountered.","status":"INTERNAL"}}Same OAuth grant: heart-rate, daily-resting-heart-rate, weight, body-fat, total-calories, distance, daily-HRV, daily-SpO2, respiratory-rate, steps, intraday HR, intraday HRV all return 200 OK with data. Only the sleep dataType list action 500s.
Ruled out (extensive matrix):
Account / setup:
Happy to provide request IDs, full response headers, or cURL repros off-list.
Thanks!
04-30-2026 05:10
04-30-2026 05:10
Hi mehtadone,
Since 2026/04/25, and still as of 2026/04/30, I have been consistently getting HTTP 500 INTERNAL on the following GET request:
GET https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints
I’m seeing exactly the same issue on my side as well.
At this point, this looks very likely to be a regression on Google’s side. It might be faster to raise an issue with Google Health on GCP.
The official release of the integration is apparently planned for the end of May 2026, but in my opinion, it does not yet seem to be at production-quality level.
Thanks,
dandaso
Best Answer04-30-2026 07:38
04-30-2026 07:38
I tried but the support and forums on GCP are so confusing!
Best Answer04-30-2026 11:45
04-30-2026 11:45
Hi,
skip the OAuth-rebuild rabbit hole - that's the 403 fix, not the 500. The 500 is purely a :list server bug for sleep, i saw that aswell, fixed by routing the same query
through :reconcile.
Try to:
Stop calling :list. Call :reconcile instead. Same dataType, same response shape, doesn't 500.
GET …/sleep/dataPoints → 500 INTERNAL (broken server-side)
POST …/sleep/dataPoints:reconcile → 200 OK with the same dataPoints array
Concrete request
POST https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints:reconcile
Authorization: Bearer <access_token>
Content-Type: application/json
{ "consistencyToken": null }
- First call: consistencyToken: null.
- Subsequent calls: echo back the nextConsistencyToken from the previous response (or pass null again if you don't care about delta semantics you'll just get
the full snapshot every time).
Response shape
{
"dataPoints": [
{
"dataPointId": "...",
"interval": { "startTime": "...", "endTime": "..." },
"value": { "sleep": { ... stages, efficiency, etc. } }
},
...
],
"nextConsistencyToken": "..."
}
Identical to what :list would return, your existing parser keeps working unchanged. Filter to "last night" client-side by interval.endTime (this endpoint doesn't
accept startTime/endTime query params same constraint as :list).
GL
Best Answer04-30-2026 11:49