Inner is structured as three distinct layers that each own a single responsibility: sending bytes, describing requests, and converting JSON into typed Kotlin objects. This separation means you can swap or extend any one layer without disturbing the others, and every public surface is expressed as aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/faraasaaay/inner/llms.txt
Use this file to discover all available pages before exploring further.
suspend function returning Result<T>.
All public methods on
YouTube and PortableInnertube return Result<T> and are suspend functions. Errors from the HTTP layer or JSON parsing bubble up as failures in the Result.Layer overview
Layer 1 — Transport
InnerTube (Android) · PortableInnertube (KMP)The transport layer owns the Ktor HttpClient and is the only place in the library that makes network calls. It takes a fully-described InnerTubeRequestSpec<T> — containing the HTTP method, URL, target client, auth state, optional request body, and query parameters — and executes it.On Android, InnerTube uses the OkHttp engine with a 15-second request timeout, 10-second connect timeout, and automatic gzip/deflate content encoding. It also wraps every call in withRetry, which retries on IOException up to 3 attempts with an initial delay of 500 ms and an exponential backoff factor of 2.0 (500 ms → 1000 ms → 2000 ms). PortableInnertube uses the same Ktor plugin configuration (ContentNegotiation, ContentEncoding, HttpTimeout) but receives its HttpClient via constructor injection, making it suitable for iOS and shared KMP modules. It does not wrap calls in withRetry — each request is a single attempt.Layer 2 — Request building
RequestSpecs · RequestBodies · RequestMetadataBuilder functions in the utils package produce InnerTubeRequestSpec instances for every InnerTube endpoint: buildSearchRequest, buildPlayerRequest, buildBrowseRequest, buildNextRequest, buildGetQueueRequest, and more. Each builder calls a corresponding body builder (e.g. buildSearchBody) that serialises the JSON payload including the full InnerTube context object.Header assembly lives in buildInnerTubeRequestMetadata, which emits the following headers for every request:| Header | Source |
|---|---|
X-YouTube-Client-Name | client.clientId |
X-YouTube-Client-Version | client.clientVersion |
X-Goog-Visitor-Id | authState.visitorData (when present) |
X-Origin / Referer | derived from client.requestOrigin() |
Authorization: SAPISIDHASH … | computed from SAPISID cookie (when login is enabled) |
cookie | raw cookie string (when login is enabled) |
Layer 3 — Parsing
Page objects —
AlbumPage · ArtistPage · PlaylistPage · SearchPage · HomePage · …The parsing layer converts raw BrowseResponse, SearchResponse, PlayerResponse, and NextResponse JSON into strongly-typed Kotlin data classes. Page objects such as AlbumPage, ArtistPage, PlaylistPage, SearchPage, and HomePage contain companion-object factory methods (e.g. SearchPage.toYTItem) that walk the deeply-nested renderer tree and produce SongItem, AlbumItem, ArtistItem, and PlaylistItem values.This layer contains no networking or auth logic. It only reads the JSON structures that the transport layer already deserialised via kotlinx.serialization.Entry points
Inner exposes two entry points depending on your target platform:YouTube (Android) | PortableInnertube (KMP) | |
|---|---|---|
| Kind | object singleton | Regular class |
| HTTP engine | OkHttp (bundled) | Injected HttpClient |
| Retry | ✅ Exponential backoff | ❌ Single attempt |
| Proxy support | ✅ YouTube.proxy | ❌ Not available |
| Auth surface | Full PlaybackAuthState + individual setters | PlaybackAuthState + individual setters |
| Login actions | ✅ like, subscribe, playlist CRUD, account menu | ❌ Read-only subset |
| Auth state flow | ✅ authStateFlow: StateFlow<PlaybackAuthState> | ❌ Not available |
| Platforms | Android | Android, iOS, JVM, JS |
YouTube in your Android application module. Use PortableInnertube in shared KMP code or in your iOS target where you provide the HttpClient via createPortableInnertubeHttpClient().
Request flow
The lifecycle of a single API call from caller to parsed result:Public API call
YouTube.search(query = "Radiohead", filter = YouTube.SearchFilter.FILTER_SONG) — a suspend function wrapped in runCatching.Transport dispatch
YouTube delegates to the private InnerTube instance: innerTube.search(WEB_REMIX, query, filter.value).Request spec construction
buildSearchRequest(client, locale, authState, includeLoginContext, query, params, continuation) produces an InnerTubeRequestSpec containing the serialised request body from buildSearchBody(...).Header assembly
buildInnerTubeRequestMetadata(client, authState, setLogin) assembles X-YouTube-Client-Name, X-Goog-Visitor-Id, Authorization: SAPISIDHASH, and the cookie header as required.HTTP execution
InnerTube.executeRequest(spec) performs an HTTP POST to https://music.youtube.com/youtubei/v1/search, applying the retry wrapper on IOException.JSON deserialisation
The response body is decoded via
kotlinx.serialization into a SearchResponse object by calling .body<SearchResponse>() on the Ktor HttpStatement.Page parsing
SearchPage.toYTItem(renderer) converts each MusicResponsiveListItemRenderer in the shelf into a SongItem, AlbumItem, ArtistItem, or PlaylistItem.