YouTube’s web clients (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.
WEB, WEB_REMIX) require a proof-of-origin token — a PoToken — generated by Google’s BotGuard system. Without valid PoTokens, CDN delivery is restricted: the first request succeeds normally, but the stream is cut off or degraded after approximately the first 1 MiB of data. Zemer Cipher generates PoTokens by running the BotGuard challenge inside an Android WebView, producing tokens that are structurally identical to those the official YouTube web player would generate.
Two Tokens, Two Purposes
A completePoTokenResult carries two distinct tokens, each bound to a different identity:
playerRequestPoToken — bound to the session (visitorData). This token is minted once per session and reused across multiple videos in that session. It is sent in the InnerTube /player request body, alongside the video ID and client information. YouTube validates it server-side before returning stream metadata.
streamingDataPoToken — bound to the video ID. This token must be appended to each CDN stream URL as pot=<token>. The CDN enforces the video binding: a streamingDataPoToken that was minted for a different video, or a playerRequestPoToken used in its place, will cause the CDN to serve only the first ~1 MiB before stopping delivery.
How Generation Works
-
A
PoTokenWebViewis created by callingPoTokenWebView.getNewPoTokenGenerator(context). It loads the bundledpo_token.htmlasset, which contains the BotGuard JavaScript challenge. -
On first use (or when the sessionId changes), a session token is minted by calling
generatePoToken(sessionId). ThesessionIdis thevisitorDatastring from a prior InnerTube response. This session token becomesplayerRequestPoTokenand is reused for all subsequent videos in the same session. -
For each video,
generatePoToken(videoId)mints a video-bound token that becomesstreamingDataPoToken. This call is made once per video and is not cached — each video needs its own token. -
Both tokens are assembled into a
PoTokenResultand returned to the caller.
WebView Lifecycle
PoTokenGenerator holds a single PoTokenWebView instance and reuses it across calls. A new WebView is created only when necessary:
| Condition | Action |
|---|---|
| First call | Create and initialize a new WebView |
sessionId changed | Close old WebView, create a new one, re-mint session token |
isExpired is true | Same as above — WebView has exceeded its maximum age |
isDead is true | Renderer was killed (OOM); recreate before the next call |
null. The caller is expected to fall through to a non-web client that does not require PoTokens.
Fallback Behavior
PoToken generation may be unavailable in two situations:webViewSupported = false—CookieManager.getInstance()threw during initialization, indicating that WebView is not functional on this device (missing system component, TV OS, etc.).BadWebViewException— the WebView was present but behaved incorrectly. This flag is latched: once set,getWebClientPoToken()returnsnullimmediately on all future calls without attempting WebView creation.
getWebClientPoToken() returns null. Callers should treat a null result as a signal to fall through to an Android-native client such as ANDROID_VR or IOS, which do not require PoTokens and are not subject to the same CDN restrictions.
PoToken generation is only required for
WEB and WEB_REMIX client streams. Android-native client streams — ANDROID, ANDROID_VR, IOS — do not need PoTokens and will play normally without them.PoToken generation patterns based on BgUtils (MIT License).