Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/faraasaaay/innertube-v2/llms.txt

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

YouTube requires a Proof of Origin Token (PoToken) for stream access through the WEB_REMIX and TVHTML5 clients. The token proves the request originates from a genuine browser or WebView environment by running YouTube’s BotGuard integrity challenge inside a real Android WebView.
PoToken generation requires a real Android WebView. It will not work in pure JVM or server environments. If the system WebView is unavailable or broken, PoTokenGenerator returns null gracefully rather than throwing.

How PoTokens Work

Two distinct tokens are required for each play session:
TokenWhere It GoesDescription
playerRequestPoTokenYouTube.player(poToken = ...) parameterAuthenticates the player endpoint request itself
streamingDataPoTokenAppended as &pot= to the stream URLAuthenticates each CDN stream fetch
Both tokens are generated per-session. The streaming token is generated once per session ID; the player token is generated once per video ID.

class PoTokenGenerator

The high-level entry point. Manages a PoTokenWebView instance internally, including creation, expiry detection, and automatic recreation on failure.
PoTokenGenerator is designed to be instantiated once and reused for the lifetime of the app. It manages the WebView lifecycle internally — you do not need to create or destroy PoTokenWebView instances directly.

getWebClientPoToken(videoId: String, sessionId: String): PoTokenResult?

fun getWebClientPoToken(videoId: String, sessionId: String): PoTokenResult?
Generates both the player and streaming PoTokens for a given video and session. Parameters:
ParameterTypeDescription
videoIdStringThe YouTube video ID for which the player token is needed
sessionIdStringSession identifier — use YouTube.dataSyncId for logged-in users, YouTube.visitorData for guests
Returns: A PoTokenResult with both tokens, or null if:
  • The system WebView is unavailable (webViewSupported == false)
  • The system WebView has been detected as broken (webViewBadImpl == true)
Behavior:
  • Thread-safe via a Mutex — concurrent calls queue rather than creating duplicate WebViews.
  • On the first call (or when the session changes): creates a new PoTokenWebView, runs the BotGuard challenge, generates the streaming token for sessionId, then generates the player token for videoId.
  • On subsequent calls with the same session: reuses the existing WebView; only generates a new player token for videoId.
  • On PoTokenException: re-throws — the caller should handle this as a playback failure.
  • On BadWebViewException: sets webViewBadImpl = true and returns null — no further PoToken attempts are made.
  • On WebView expiry (isExpired == true): automatically recreates the WebView and regenerates the streaming token.

class PoTokenWebView

The low-level WebView that runs the BotGuard challenge. Used internally by PoTokenGenerator. You typically do not interact with this class directly.

companion object.getNewPoTokenGenerator(context: Context): PoTokenWebView

suspend fun getNewPoTokenGenerator(context: Context): PoTokenWebView
Suspend factory function. Creates and fully initializes a new PoTokenWebView instance. Must be called on the Main dispatcher. Uses suspendCancellableCoroutine internally and returns only after the BotGuard initialization sequence completes:
  1. Loads po_token.html from assets into the WebView
  2. Calls https://www.youtube.com/api/jnn/v1/Create to obtain a challenge
  3. Runs runBotGuard() inside the WebView JavaScript context
  4. Calls https://www.youtube.com/api/jnn/v1/GenerateIT with the BotGuard response
  5. Creates the PoToken minter with the resulting integrity token
Throws a PoTokenException or BadWebViewException on failure.

generatePoToken(identifier: String): String

suspend fun generatePoToken(identifier: String): String
Generates a PoToken for the given identifier (a video ID or session ID). Runs JavaScript inside the WebView on the Main dispatcher via suspendCancellableCoroutine. Returns: The PoToken as a URL-safe base64 string. Throws: PoTokenException or BadWebViewException on JavaScript error.

val isExpired: Boolean

val isExpired: Boolean
Returns true when the integrity token from the BotGuard challenge has expired. PoTokenWebView applies a 10-minute safety marginisExpired returns true 10 minutes before the actual token expiry to prevent using a token that could expire mid-request. When isExpired is true, PoTokenGenerator automatically discards the old WebView and creates a fresh one on the next getWebClientPoToken() call.

fun close()

@MainThread
fun close()
Destroys the WebView and cancels the internal coroutine scope. Must be called on the Main thread. Clears history, cache, and loads about:blank before calling destroy() to ensure the WebView is fully torn down. Called automatically by PoTokenGenerator when recreating the WebView.

class PoTokenResult

The result object returned by PoTokenGenerator.getWebClientPoToken().
FieldTypeDescription
playerRequestPoTokenStringPass this to YouTube.player(poToken = ...)
streamingDataPoTokenStringAppend this as &pot= to the stream URL after deobfuscation
class PoTokenResult(
    val playerRequestPoToken: String,
    val streamingDataPoToken: String,
)

class PoTokenException

class PoTokenException(message: String) : Exception(message)
Thrown when PoToken generation fails for a non-structural reason (e.g. BotGuard API returned an error, network failure during the challenge, JavaScript runtime error that is not a SyntaxError). The message contains the JavaScript error string.

class BadWebViewException

class BadWebViewException(message: String) : Exception(message)
Thrown when the system WebView is detected as fundamentally broken — specifically when a JavaScript SyntaxError is reported in the WebView console. This indicates the installed WebView APK does not support the JavaScript features required by BotGuard. When BadWebViewException is caught by PoTokenGenerator, it sets webViewBadImpl = true and stops attempting PoToken generation for the lifetime of the PoTokenGenerator instance.

fun buildExceptionForJsError(error: String): Exception

fun buildExceptionForJsError(error: String): Exception
Utility function that maps a JavaScript error string to the appropriate exception type:
  • Returns BadWebViewException if error contains "SyntaxError"
  • Returns PoTokenException for all other JavaScript errors
Used internally by PoTokenWebView to produce the right exception from WebView console error callbacks.

Integration Example

// In your playback manager — create once and reuse
private val poTokenGenerator = PoTokenGenerator()

suspend fun playVideo(videoId: String) {
    // Determine the session ID
    val sessionId = YouTube.dataSyncId ?: YouTube.visitorData ?: return

    // Generate tokens
    val poTokenResult = poTokenGenerator.getWebClientPoToken(videoId, sessionId)

    // Fetch the player response
    val playerResponse = YouTube.player(
        videoId = videoId,
        playlistId = null,
        poToken = poTokenResult?.playerRequestPoToken  // null is safe — token is optional
    ).getOrNull() ?: return

    // Resolve the stream URL and append the streaming token
    val format = playerResponse.streamingData?.adaptiveFormats
        ?.filter { it.isAudio && it.isOriginal }
        ?.maxByOrNull { it.bitrate } ?: return

    var streamUrl = if (format.url != null) {
        YouTubeExtractor.deobfuscateUrlNParam(format.url)
    } else {
        YouTubeExtractor.decryptUrl(format.signatureCipher ?: return)
    }

    // Append the streaming PoToken if available
    if (poTokenResult != null) {
        streamUrl += "&pot=${poTokenResult.streamingDataPoToken}"
    }

    // Start playback with streamUrl
}

Build docs developers (and LLMs) love