InnerTubeX is an extended Android, JVM, and iOS Kotlin Multiplatform client for YouTube's InnerTube APIs. It retains standard browse, search, account, playlist, and player operations while adding:
- SABR/UMP audio and video streaming with seekable segmented sources.
- SABR video representation selection for host video playback.
- Player cipher deobfuscation through Zemer, Faraday, EJS, and QuickJS.
- Layered remote, parsed-script, and cached cipher fallbacks.
- Isolated authenticated sessions with generation-safe cancellation.
- Channel discovery, account switching support, and streaming music uploads.
- Bounded remote bodies and protocol state with strict media URL validation.
- Injectable logging, player configuration, diagnostics, and token providers.
repositories {
maven("https://jitpack.io") {
content { includeGroup("com.github.MetrolistGroup.innertubex") }
}
}
dependencies {
implementation("com.github.MetrolistGroup.innertubex:innertubex:0.7.1")
}val client = InnerTube(
httpClient = HttpClient(OkHttp),
)
val response = client.search(YouTubeClient.WEB, query = "query").body<SearchResponse>()
client.close()The caller owns the supplied HttpClient. InnerTube.close() cancels only
library-owned visitor-data work and session-bound requests. Authenticated
sessions contain sensitive cookies and SAPISID values; follow
SECURITY.md when storing or logging them.
The reusable extraction stack handles client selection, player configuration, cipher transforms, PO-token contracts, direct/HLS/SABR transport selection, format selection, and bounded media probing:
val configStore = RemotePlayerConfigStore(
httpClient = client.httpClient,
repository = PlayerConfigRepository.disabled(),
)
val cipher = YouTubeCipherService(client.httpClient, configStore)
val extractor = InnerTubeExtractor(
configParser = YtConfigParserImpl(client.httpClient, client, configStore),
cipherService = cipher,
innerTube = client,
)
val stream = extractor.extract(
videoId = "dQw4w9WgXcQ",
hints = ContentHints(wantVideo = false),
audioQuality = AudioQuality.HIGH,
)Hosts with an independently confirmed Premium entitlement may opt into one additional, scoped TV comparison:
val stream = extractor.extractWithAuthenticatedTvDiscovery(
videoId = "dQw4w9WgXcQ",
confirmedPremium = hostConfirmedPremium,
credentialProvider = object : TvBearerCredentialProvider {
override suspend fun getCredential(videoId: String, sessionGeneration: Long) =
hostAcquireShortLivedTvCredential(sessionGeneration)
override suspend fun isCredentialCurrent(credential: TvBearerCredential) =
hostCredentialIsStillCurrent(credential)
},
hints = ContentHints(wantVideo = false),
audioQuality = AudioQuality.HIGH,
)The provider owns consent, acquisition, secure storage, refresh, and revocation.
It should return a short-lived credential scoped to the existing TVHTML5 or
TVHTML5_DOWNGRADED manifest and current session generation. The library calls
this path only when explicitly requested, isolates the bearer from cookies and
media requests, and keeps the ordinary result unless the TV audio result is
strictly higher quality. This is experimental and audio-only: video requests
use ordinary extraction, and Premium does not guarantee a better
response, and no OAuth or credential harvesting is provided.
The catalog also exposes four explicit, unverified probes through the existing
playbackClientOverrideId hint: IOS_MUSIC (26), ANDROID_KIDS (18),
ANDROID_PRODUCER (91), and MEDIA_CONNECT_FRONTEND (95). They are PROBE_ONLY, never
automatic fallbacks, and their content/transport support remains conservative.
No DASH playback is implemented by these probe profiles.
Applications can inject TokenProvider, ClientHealthMonitor,
ClientFallbackStrategy, and InnerTubeLogger implementations. Platform token
minting, persistence, playback engines, UI, and application settings remain
host-owned. Never log an ExtractedStream, PO-token values, signed URLs, or
authenticated session fields; library model toString() implementations redact
those values as defense in depth.
Run ./gradlew :harness:run --args='--help' to see the offline CLI help, or
--args='--list-clients' for the offline profile catalog. The source-built
JVM harness performs opt-in 90-second paced audio playback with real backward/forward
transport seeks, HLS/direct/SABR profiles, a quick --seconds smoke mode and
sanitized local reports. Run --help for options. Live checks are never part
of allTests.
./gradlew ktlintFormat
./gradlew allTests ktlintCheck apiCheck assemble publishToMavenLocalEnable the repository hook with git config core.hooksPath .githooks.
Contributions and releases are documented in CONTRIBUTING.md
and RELEASING.md.
InnerTubeX uses experimental 0.x versioning. YouTube can change private APIs,
player scripts, and SABR behavior without notice, so pin exact versions and
review CHANGELOG.md before upgrading.
InnerTubeX is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by YouTube or Google. YouTube and Google are trademarks of their respective owners. Users are responsible for complying with applicable terms of service and law.