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.

PoTokenResult is a data container returned by PoTokenGenerator.getWebClientPoToken(). It holds two distinct BotGuard proof-of-origin tokens — one for InnerTube API requests and one for CDN stream URLs. The two tokens are bound to different identifiers (session vs. video) and must be used in the correct place. Using the wrong token in the wrong place causes the CDN to truncate streams past approximately 1 MiB. Package: com.zemer.cipher.potoken

Class Definition

class PoTokenResult(
    val playerRequestPoToken: String,
    val streamingDataPoToken: String,
)

Properties

playerRequestPoToken
String
Session-bound PoToken. Send this token in the InnerTube /player request body under serviceIntegrityDimensions.poToken.This token is bound to the visitor’s visitorData (sessionId). It is generated once per session and reused across all videos with the same sessionIdPoTokenGenerator does not re-mint it unless the session changes, the token expires, or the WebView is recreated. The /player endpoint validates the session binding and rejects tokens bound to a different identity.
streamingDataPoToken
String
Video-bound PoToken. Append this token to the CDN stream URL as a pot= query parameter.This token is bound to the specific YouTube video ID passed to getWebClientPoToken. The CDN (googlevideo) checks that the pot= token is bound to the video ID being served. A session-bound token (or no token at all) causes the CDN to serve only the first ~1 MiB of any stream segment, causing playback to stall or drop after the first segment boundary.
Do not swap the two tokens. Sending streamingDataPoToken in the /player request or sending playerRequestPoToken as pot= on the stream URL will both result in playback failures — the CDN will truncate streams past the ~1 MiB window.

Usage Example

val poTokenGenerator = PoTokenGenerator()

val result = withContext(Dispatchers.IO) {
    poTokenGenerator.getWebClientPoToken(
        videoId = videoId,
        sessionId = visitorData
    )
}

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

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

Token Binding Reference

PropertyBound toUsed inValidated by
playerRequestPoTokenvisitorData (session)InnerTube /player request bodyInnerTube API
streamingDataPoTokenVideo IDCDN stream URL as pot=<token>googlevideo CDN

Build docs developers (and LLMs) love