Authentication
Sign in
Make sure you're passing all Security headers or you might stumble upon {"code":28,"message":"ACCOUNT_BANNED","profileId":null,"type":1,"reason":null,"isBanAutomated":true,"thirdPartyUserIdToShow":null,"banSubReason":null} — but don't fret — it's a fake error, your account isn't banned and API simply blocked your request, not account. Endpoint selection depends on the recaptcha_first_party assignment: when disabled (default) sign-in uses /v8/sessions; when enabled the client uses POST /v9/sessions with an extra captchaToken, sharing the same body otherwise.
POST /v8/sessionsBody:
email— string with emailpassword— string with password, don't specify if usingauthTokenauthToken— string obtained from login+password flow ornulltoken— FCM (push service) string ornullgeohash— geohash string ornull
Response:
profileId— string with numbers, account's IDsessionId— Session IDauthToken— string, Auth token for session refresh
Errors:
ACCOUNT_BANNED— could be malformed requestINVALID_INPUT_PARAMETERS— incorrect credentials
Sign in via reCAPTCHA
reCAPTCHA variant of POST /v8/sessions. The client uses this endpoint instead of /v8/sessions only when the recaptcha_first_party assignment is enabled. That assignment is sent by server and resolved before login. The body is identical to SessionCreateRequest plus a captchaToken (a reCAPTCHA Enterprise token for the login action). The official client always populates captchaToken here and aborts locally if it can't obtain one, so a null/absent token never reaches this endpoint from the app.
POST /v9/sessionsBody:
Response:
profileId— string with numbers, account's IDsessionId— Session IDauthToken— string, Auth token for session refresh
Errors:
ACCOUNT_BANNED— could be malformed requestINVALID_INPUT_PARAMETERS— incorrect credentials; on this endpoint a rejectedcaptchaToken(e.g. expired or low-score) is also a plausible cause — server behavior, not confirmed from the client
Login via third party, WIP
NOTE
This endpoint hasn't been researched yet
Single endpoint used for both initial third-party sign-in and third-party session refresh, distinguished by the request body shape (oneOf).
Google (vendor 2): the app obtains a Google server auth code on-device (GoogleSignInOptions.requestServerAuthCode(...)), exchanges it for a Google access_token via Exchange Google auth code (POST /v3/users/thirdparty/exchange), then sends that token here as thirdPartyToken (ThirdPartyRequest). Facebook (vendor 1) sends its access token directly.
When registered is true, authenticationResponse carries the session and the client stores authToken, sessionId (JWT), profileId, thirdPartyUserId and thirdPartyUserIdToShow. When registered is false, the identity has no account yet and the client continues to Create third party account.
Session refresh reuses this same URL with ThirdPartySessionRequest ({ thirdPartyUserId, authToken, geohash }): the provider token is not re-sent — only the stable thirdPartyUserId and the rolling Grindr authToken. The response is the same ThirdPartyAuthResponse, yielding a fresh sessionId (JWT) and a rotated authToken.
POST /v8/sessions/thirdpartyBody:
One of:
Response:
registered— boolean,trueif a Grindr account is already linked to this provider identity.thirdPartyUserInfo— UndocumentedObject, Provider profile info used to pre-fill account creation whenregisteredisfalse. ornullauthenticationResponse— ThirdPartyAuthenticationResponse, Session payload, present whenregisteredistrue. ornull
Exchange Google auth code, WIP
NOTE
This endpoint hasn't been researched yet
First step of the Google sign-in flow. The app sends the Google server auth code (serverAuthCode from the native Google Sign-In result, requested against server client id 1036042917246-68g7siev1clho9lhqevahao9hlbpvssf.apps.googleusercontent.com). The backend holds the matching web-client secret, performs the authorization-code → token exchange with Google, and returns the Google OAuth tokens. The client keeps access_token and passes it as thirdPartyToken to Login via third party. Session refreshes do not repeat this step.
POST /v3/users/thirdparty/exchangeBody:
code— string, Google OAuth 2.0 server auth code (serverAuthCodefrom the Google Sign-In result).
Response:
access_token— string, Google OAuth access token. Used asthirdPartyTokeninPOST /v8/sessions/thirdparty.token_type— string, OAuth token type, e.g."Bearer".id_token— Google OpenID Connect ID token (JWT).expires_in— long integer, Access token lifetime in seconds.
SessionCreateRequest
email— string with emailpassword— string with password, don't specify if usingauthTokenauthToken— string obtained from login+password flow ornulltoken— FCM (push service) string ornullgeohash— geohash string ornull
SessionCreateRequestV9
Request body for Sign in via reCAPTCHA (POST /v9/sessions). Identical to SessionCreateRequest plus one extra field, captchaToken. The client uses this shape only when the recaptcha_first_party assignment is enabled for the device.
- everything from SessionCreateRequest
captchaToken— string, reCAPTCHA Enterprise token for theFirstPartyLoginaction. Non-null for the email + password sign-in;nullfor theauthTokensession-refresh flow. The token is only fetched by device when therecaptcha_first_partyassignment is enabled.
AuthSessionResponse
profileId— string with numbers, account's IDsessionId— Session IDauthToken— string, Auth token for session refresh
GoogleAccessTokenRequest
Request body for Exchange Google auth code (POST /v3/users/thirdparty/exchange). Wraps the Google server auth code obtained on-device from native Google Sign-In.
code— string, Google OAuth 2.0 server auth code (serverAuthCodefrom the Google Sign-In result).
GoogleAccessTokenResponse
Response from Exchange Google auth code. Standard Google OAuth 2.0 token payload returned after the backend exchanges the server auth code. The client keeps only access_token, which it passes as thirdPartyToken to Login via third party.
access_token— string, Google OAuth access token. Used asthirdPartyTokeninPOST /v8/sessions/thirdparty.token_type— string, OAuth token type, e.g."Bearer".id_token— Google OpenID Connect ID token (JWT).expires_in— long integer, Access token lifetime in seconds.
ThirdPartyRequest
Request body for the initial Login via third party (POST /v8/sessions/thirdparty). For Google, thirdPartyToken is the access_token from Exchange Google auth code; for Facebook it is the Facebook access token.
thirdPartyVendor— integer,1— Facebook,2— GooglethirdPartyToken— string, Provider access token (Googleaccess_token/ Facebook token).geohash— geohash string ornull, optional
ThirdPartySessionRequest
Request body for session refresh via third party, sent to the same POST /v8/sessions/thirdparty endpoint as the initial login. Used once a thirdPartyUserId is stored (account created/logged in via Google or Facebook). The provider token is not re-sent — only the stable thirdPartyUserId plus the stored Grindr authToken.
thirdPartyUserId— string, Stable provider user id from the previous login (authenticationResponse.thirdPartyUserId).authToken— string, GrindrauthTokenfrom the previous session (rotated on every refresh).geohash— geohash string ornull, optional
ThirdPartyAuthenticationResponse
Session payload nested in ThirdPartyAuthResponse.authenticationResponse; present when registered is true. sessionId is the same JWT as in AuthSessionResponse.
profileId— string, Account ID.sessionId— Session IDauthToken— string, Auth token used for session refresh.thirdPartyUserId— string, Stable provider user id; persisted and re-sent asthirdPartyUserIdon refresh.thirdPartyUserIdToShow— Display variant of the provider user id (e.g. email/handle).
ThirdPartyAuthResponse
Response from Login via third party and from third-party session refresh. When registered is true, authenticationResponse carries the session. When registered is false, no account is linked yet and the client proceeds to Create third party account using thirdPartyUserInfo.
registered— boolean,trueif a Grindr account is already linked to this provider identity.thirdPartyUserInfo— UndocumentedObject, Provider profile info used to pre-fill account creation whenregisteredisfalse. ornull, optionalauthenticationResponse— ThirdPartyAuthenticationResponse, Session payload, present whenregisteredistrue. ornull, optional
Session ID
JWT obtained from authentication flow. Decoded JWT content:
Headers claims structure:
kid— key IDalg—"RS256"typ—"JWT"
Payload claims:
exp— number, unix timestamp in seconds defining token expiration dateprofileId— string with numbers, account's IDroles— unknown array, appears to be emptyfeatures— array of strings, e.g."HidePremiumStore","CreateVideoCall","VideoCallDailyFree"featureFlags— array of strings, e.g."profile-insights","online-until-updates","enable-account-filters-bulk-exposure","a-list-v3","discover-v2","boost_purchase_fixes","discover-studfinder-upsell"experiments— object with experiment name → variant string, known keys:explore-paywall-profiles— string, e.g."test"limiting_chat_credits_from_unlimited_to_5_for_xtra_users_in_explore— string, e.g."treatment"validation-aa-backend-profileid— string, e.g."test"grindr-core-day-pass— string, e.g."control"llm-age-verification-methods— string, e.g."variant_b"trans_tribe_filtering_changes— string, e.g."Test"green_dot_v2— string, e.g."treatment-1"sponsored-profiles-cascade-selection-mode— string, e.g."treatment"read-receipt-ad-reward— string, e.g."variant_2"taps-paywall— string, e.g."treatment-1"explore-insertables-v1— string, e.g."treatment-insertables-below-mpu"reduce_number_of_free_profiles_from_99_to_90— string, e.g."test"cascade-mpu-explore-studfinder-2026-03-12— string, e.g."control"reduce_number_of_results_with_age_filter_for_free_users— string, e.g."test"mpu-heuristic-algorithm-optimizations-q125— string, e.g."control"ships_in_the_night_v3— string, e.g."treatment"cascade-mpu-disable-poc-heuristic-2026-03-02— string, e.g."treatment"for-you-recsys-v1_1— string, e.g."control"cascade-mpu-studfinder-profile-limit-2026-03-12— string, e.g."treatment"reduce_free_user_results_with_any_filter— string, e.g."control"cascade-mpu-studfinder-unlimited-2026-02-02— string, e.g."treatment"mpu-rest-of-world— string, e.g."treatment"top_18_mpu_profiles_are_online_in_extended_cascade_and_explore— string, e.g."control"for-you-v2— string, e.g."Test"rewarded-ads-viewed-me-v2— string, e.g."variant_1"mega-boost-v1— string, e.g."mega-boost-low"
systemTime— unix timestamp in millisecondsupsells— unknown object, appears to be emptyrestrictionReason— unknown value, appears to benullgrit— unknown UUIDv4 string