You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Valid DI token gets 403 on every connectapi call: API session uses plain requests, which Cloudflare rejects (curl_cffi works) #444
Version: garminconnect 0.3.16, curl_cffi 0.15.0, Python 3.12, Windows 11
Symptom: Portal login + MFA succeeds and a DI token is issued (scopes include CONNECT_READ), but every connectapi call returns 403 {"message":"HTTP 403 Forbidden","error":"ForbiddenException"}: socialProfile, user-settings, devices, hrv, training readiness, body battery and stress. With the default verify_login=True, _verify_token() treats this as a rejected token and discards it, so the login looks like it failed ("token rejected by API tier after MFA"). This happened on 4 consecutive MFA logins.
Cause: same saved token, same URL (/userprofile-service/socialProfile), same native headers:
requests.get(...) → 403
curl_cffi.requests.get(..., impersonate="chrome") → 200 with full profile
Login strategies already use curl_cffi, but Client._api_session (used by _run_request) is a plain requests.Session, so all data calls carry the requests TLS fingerprint.
After this, every endpoint returns 200 and token refresh works without re-login.
Suggested fix: when HAS_CFFI, build _api_session as a curl_cffi session (as the login strategies already do), so _verify_token() isn't testing through a different client than the one that got the token.
Related, smaller:_do_mobile_login reports every 429 as "IP rate limited by Garmin". With fake credentials, the same endpoint returned 429 to the library's fingerprints (iOS UA / safari_ios TLS) but a normal 401 invalid-password to other clients on the same IP, and the 429s also happened on IPs that had never been used. It seems to be fingerprint-based rather than per-IP, which sends users chasing IP cooldowns. Possibly the cause of the "follows me across IPs" reports in #369.
Thanks for the detailed analysis. API calls have always used plain requests and work fine for most users (including from my own connection today), so this looks environment-specific — are you on a VPN, datacenter or corporate network? I'd rather not switch the default session: curl_cffi doesn't support files=, so all uploads (activities, weight/body composition) would break — note that your workaround has the same limitation. If this turns out to affect more people, I'll look at an opt-in impersonation setting for API calls. Good point on the 429 message, that's reworded in #449 so it no longer suggests it's always IP-based.
Activity
cyberjunky commentedon Sep 29, 2026
Thanks for the detailed analysis. API calls have always used plain
requestsand work fine for most users (including from my own connection today), so this looks environment-specific — are you on a VPN, datacenter or corporate network? I'd rather not switch the default session: curl_cffi doesn't supportfiles=, so all uploads (activities, weight/body composition) would break — note that your workaround has the same limitation. If this turns out to affect more people, I'll look at an opt-in impersonation setting for API calls. Good point on the 429 message, that's reworded in #449 so it no longer suggests it's always IP-based.