Skip to content

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/sessions

Body:

  • email — string with email
  • password — string with password, don't specify if using authToken
  • authToken — string obtained from login+password flow or null
  • token — FCM (push service) string or null
  • geohashgeohash string or null

Response:

  • profileId — string with numbers, account's ID
  • sessionIdSession ID
  • authToken — string, Auth token for session refresh

Errors:

  • ACCOUNT_BANNED — could be malformed request
  • INVALID_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/sessions

Body:

SessionCreateRequestV9

Response:

  • profileId — string with numbers, account's ID
  • sessionIdSession ID
  • authToken — string, Auth token for session refresh

Errors:

  • ACCOUNT_BANNED — could be malformed request
  • INVALID_INPUT_PARAMETERS — incorrect credentials; on this endpoint a rejected captchaToken (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/thirdparty

Body:

One of:

Response:

  • registered — boolean, true if a Grindr account is already linked to this provider identity.
  • thirdPartyUserInfoUndocumentedObject, Provider profile info used to pre-fill account creation when registered is false. or null
  • authenticationResponseThirdPartyAuthenticationResponse, Session payload, present when registered is true. or null

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/exchange

Body:

  • code — string, Google OAuth 2.0 server auth code (serverAuthCode from the Google Sign-In result).

Response:

  • access_token — string, Google OAuth access token. Used as thirdPartyToken in POST /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 email
  • password — string with password, don't specify if using authToken
  • authToken — string obtained from login+password flow or null
  • token — FCM (push service) string or null
  • geohashgeohash string or null

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 the FirstPartyLogin action. Non-null for the email + password sign-in; null for the authToken session-refresh flow. The token is only fetched by device when the recaptcha_first_party assignment is enabled.

AuthSessionResponse

  • profileId — string with numbers, account's ID
  • sessionIdSession ID
  • authToken — 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 (serverAuthCode from 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 as thirdPartyToken in POST /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 — Google
  • thirdPartyToken — string, Provider access token (Google access_token / Facebook token).
  • geohashgeohash string or null, 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, Grindr authToken from the previous session (rotated on every refresh).
  • geohashgeohash string or null, 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.
  • sessionIdSession ID
  • authToken — string, Auth token used for session refresh.
  • thirdPartyUserId — string, Stable provider user id; persisted and re-sent as thirdPartyUserId on 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, true if a Grindr account is already linked to this provider identity.
  • thirdPartyUserInfoUndocumentedObject, Provider profile info used to pre-fill account creation when registered is false. or null, optional
  • authenticationResponseThirdPartyAuthenticationResponse, Session payload, present when registered is true. or null, optional

Session ID

JWT obtained from authentication flow. Decoded JWT content:

Headers claims structure:

  • kid — key ID
  • alg"RS256"
  • typ"JWT"

Payload claims:

  • exp — number, unix timestamp in seconds defining token expiration date
  • profileId — string with numbers, account's ID
  • roles — unknown array, appears to be empty
  • features — 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 milliseconds
  • upsells — unknown object, appears to be empty
  • restrictionReason — unknown value, appears to be null
  • grit — unknown UUIDv4 string

Open Grind is not affiliated with Grindr in any way.