Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ZemerTeam/zemer-cipher/llms.txt

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

PoTokenGenerator generates BotGuard proof-of-origin (PoToken) pairs for YouTube’s web client. It runs the BotGuard JavaScript challenge inside an Android WebView (via PoTokenWebView) and returns a PoTokenResult containing both a session-bound token for InnerTube requests and a video-bound token for CDN stream URLs. Package: com.zemer.cipher.potoken PoTokenGenerator is not a singleton — create one instance per use-case. Each instance manages a single PoTokenWebView internally and tracks the current session state.
getWebClientPoToken is a blocking call that uses runBlocking internally. Do not call it on the Android main thread. Always dispatch to a background thread (e.g. Dispatchers.IO).

Constructor

class PoTokenGenerator()
No parameters. Allocates internal state but does not create a WebView until getWebClientPoToken is first called.

Functions

getWebClientPoToken

fun getWebClientPoToken(videoId: String, sessionId: String): PoTokenResult?
Generates a PoTokenResult containing both BotGuard tokens. This is a blocking call.
videoId
String
required
The YouTube video ID (e.g. dQw4w9WgXcQ). Used to generate the video-bound streamingDataPoToken. The CDN validates that this token is bound to the specific video ID — using a token minted for a different video results in CDN truncation past ~1 MiB.
sessionId
String
required
The visitor’s visitorData string from an InnerTube API response. Used to generate the session-bound playerRequestPoToken. The session token is minted once per sessionId and reused across videos — the WebView is only recreated when the sessionId changes, when the existing token expires, or when the renderer dies.
return
PoTokenResult?
A PoTokenResult holding both tokens on success, or null in the following cases:
  • The device does not support WebView (webViewSupported = false)
  • The system WebView has a broken implementation (a BadWebViewException was raised — permanently flagged for this instance; all subsequent calls will also return null)
  • Generation times out after 8 seconds (POTOKEN_TIMEOUT_MS = 8_000L)
Throws:
  • PoTokenException — A JavaScript-level error that persists even after the WebView is recreated and the operation retried. This indicates a durable failure (e.g. BotGuard script changes) rather than a transient one.
Retry behavior: If the per-video generatePoToken(videoId) call fails and the PoTokenWebView has not already been freshly recreated in this call, the function retries once by closing and fully recreating the PoTokenWebView from scratch — including re-minting the session token. If that retry also fails, the exception propagates (as PoTokenException or another throwable). Failures during session-token generation or on a freshly recreated WebView are not retried. Timeout and cleanup: If generation exceeds 8 seconds (which can happen when the sandboxed WebView renderer is culled by the OS under memory pressure), the call returns null and the PoTokenWebView is closed and reset so the next call starts fresh. WebView lifecycle: The internal PoTokenWebView is recreated automatically when:
  • sessionId changes
  • The existing WebView reports it has expired (isExpired)
  • The renderer has died (isDead — OOM kill by the OS)
  • forceRecreate is triggered by an internal retry

Constants

ConstantValueDescription
POTOKEN_TIMEOUT_MS8_000LMaximum milliseconds to wait for token generation before returning null. Cold-start (WebView spin-up + BotGuard JS load + token mint) typically takes 2–5 seconds.

Example

// Instantiate once per use-case (e.g. per player session)
val poTokenGenerator = PoTokenGenerator()

// Call from a background thread
val result = withContext(Dispatchers.IO) {
    poTokenGenerator.getWebClientPoToken(
        videoId = "dQw4w9WgXcQ",
        sessionId = visitorData
    )
}

if (result != null) {
    // Include in the InnerTube /player request body:
    // serviceIntegrityDimensions.poToken = result.playerRequestPoToken

    // Append to the CDN stream URL:
    val streamUrl = "${baseStreamUrl}&pot=${result.streamingDataPoToken}"
}

Exceptions

PoTokenException

class PoTokenException(message: String) : Exception(message)
Thrown when the BotGuard JavaScript challenge returns an error that persists after a retry. This is a durable JS-level failure, distinct from transient failures (which are retried silently) and BadWebViewException (which disables the WebView for this instance permanently).

BadWebViewException

class BadWebViewException(message: String) : Exception(message)
Thrown (and caught internally) when the system WebView returns a SyntaxError from the BotGuard JS. This indicates the installed system WebView is fundamentally broken (e.g. an outdated or corrupt WebView APK). Once caught, the PoTokenGenerator instance sets webViewBadImpl = true and all subsequent calls to getWebClientPoToken immediately return null.

Build docs developers (and LLMs) love