Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/faraasaaay/inner/llms.txt

Use this file to discover all available pages before exploring further.

Inner operates in two modes. In anonymous mode it attaches only a visitorData string as X-Goog-Visitor-Id and can read most public content — home feed, search, albums, artists, and playlists. In authenticated mode it additionally sends a session cookie and a signed Authorization: SAPISIDHASH header, unlocking library management, playback history, like/subscribe actions, and personalised recommendations.
Never hardcode session cookies in source code. Load them from secure storage at runtime.

Anonymous browsing

Anonymous requests are identified by a visitorData string — a base64-encoded protobuf token YouTube uses for session continuity and recommendation context. YouTube fetches it from the sw.js bootstrap endpoint.
// Fetch and cache visitor data
val visitorData = YouTube.visitorData().getOrNull()
YouTube.visitorData = visitorData
Setting visitorData updates the internal PlaybackAuthState and causes it to be sent as the X-Goog-Visitor-Id header on every subsequent request. If visitorData is null, requests are sent without the header. To authenticate, you need the raw Cookie header from an active YouTube Music browser session.
1

Open YouTube Music in your browser

Navigate to music.youtube.com and sign in to the account you want to use.
2

Open DevTools Network tab

Press F12Network tab. Reload the page or click any element to trigger a request.
3

Copy the Cookie header

Find any request to music.youtube.com. In the Request Headers section, copy the full value of the Cookie header. It will contain keys like SAPISID, __Secure-3PSID, __Secure-3PAPISID, and others.
4

Apply the cookie to YouTube

YouTube.cookie = "SAPISID=xxxxx; __Secure-3PSID=xxxxx; __Secure-3PAPISID=xxxxx; ..."
YouTube.dataSyncId = "your_data_sync_id" // optional, improves personalisation
dataSyncId is used as onBehalfOfUser in the InnerTube context object. It can be obtained from the account-menu endpoint (YouTube.accountInfo()) or from the APISID cookie value.
Once a cookie containing SAPISID is set, YouTube.hasLoginCookie() returns true. If both a cookie and a non-blank dataSyncId are present, YouTube.hasPlaybackLoginContext() returns true, enabling full personalised playback context.

SAPISIDHASH signing

Inner automatically generates a signed Authorization header for every authenticated request — you never need to construct it yourself. The buildSapisidAuthorization function in RequestMetadata.kt computes:
SAPISIDHASH <timestamp>_<sha1("<timestamp> <SAPISID> <origin>")>
where <timestamp> is the current Unix time in seconds, <SAPISID> is the value extracted from the cookie string, and <origin> is either https://music.youtube.com (for most clients) or https://www.youtube.com (for TV clients). The SHA-1 is computed using Inner’s pure-Kotlin implementation in CommonAuthUtils.kt. This header is only attached when setLogin = true and the target YouTubeClient has loginSupported = true.

PlaybackAuthState

PlaybackAuthState is the single data class that holds all authentication context. It is immutable — every setter on YouTube creates a new normalised copy.
FieldTypePurpose
cookieString?Raw Cookie header string sent with authenticated requests
visitorDataString?Value of the X-Goog-Visitor-Id header
dataSyncIdString?Used as onBehalfOfUser in the InnerTube request context
poTokenString?Generic PoToken fallback when no specialised token is set
poTokenGvsString?Token appended as ?pot= to GVS stream URLs
poTokenPlayerString?Token placed in serviceIntegrityDimensions for player requests
webClientPoTokenEnabledBooleanMaster switch — PoTokens are only resolved when this is true
Two computed properties on PlaybackAuthState reflect login status:
  • hasLoginCookietrue when the cookie string contains a SAPISID key (determined by parsing the cookie into key-value pairs). This is the minimum requirement for attaching login headers.
  • hasPlaybackLoginContexttrue when hasLoginCookie is true and dataSyncId is non-blank. Required for personalised playback and library operations.
Two additional computed properties are available:
  • sessionId — returns dataSyncId when hasPlaybackLoginContext is true, otherwise returns visitorData. Use this as the identifier for PoToken generation.
  • fingerprint — a SHA-1 digest of all auth fields joined by \u0000 (null character). Useful for detecting state changes without exposing raw credential values.

Using PlaybackAuthState

You can update individual fields via the convenience setters on YouTube, or replace the entire state atomically using YouTube.authState:
import com.tamed.music.innertube.PlaybackAuthState

YouTube.authState = PlaybackAuthState(
    cookie = "SAPISID=xxx; __Secure-3PSID=xxx; ...",
    visitorData = "CgtAbCdEfGhIjK...",
    dataSyncId = "123456789",
    webClientPoTokenEnabled = true,
    poTokenPlayer = "generated_token_here",
    poTokenGvs = "generated_gvs_token_here"
)
Setting authState normalises all values (trims whitespace, strips "null" strings, handles ||-delimited dataSyncId formats) and propagates the new state to the internal InnerTube instance atomically. The current state is also exposed as a StateFlow<PlaybackAuthState> via YouTube.authStateFlow for reactive UI updates.

Proxy support

YouTube supports routing requests through an HTTP or SOCKS proxy via java.net.Proxy. Assigning a proxy recreates the underlying HttpClient with the new engine configuration. Proxy support is Android-only — PortableInnertube does not expose a proxy field.
import java.net.Proxy
import java.net.InetSocketAddress

YouTube.proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("127.0.0.1", 8080))

// To bypass the proxy for stream URL resolution only:
YouTube.streamBypassProxy = true
When streamBypassProxy is true, YouTube.streamProxy returns null, so stream URLs are fetched directly while all InnerTube API calls still go through the proxy.

Build docs developers (and LLMs) love