Skip to main content

Documentation 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.

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 a 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.
// InnerTubeRequestSpec carries everything needed for one HTTP call
internal data class InnerTubeRequestSpec<T>(
    val method: InnerTubeRequestMethod,   // GET or POST
    val url: String,
    val client: YouTubeClient,
    val authState: PlaybackAuthState,
    val setLogin: Boolean,
    val body: T? = null,
    val queryParameters: List<Pair<String, String>> = emptyList(),
    val useJsonContentType: Boolean = true,
)

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:
HeaderSource
X-YouTube-Client-Nameclient.clientId
X-YouTube-Client-Versionclient.clientVersion
X-Goog-Visitor-IdauthState.visitorData (when present)
X-Origin / Refererderived from client.requestOrigin()
Authorization: SAPISIDHASH …computed from SAPISID cookie (when login is enabled)
cookieraw 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)
Kindobject singletonRegular class
HTTP engineOkHttp (bundled)Injected HttpClient
Retry✅ Exponential backoff❌ Single attempt
Proxy supportYouTube.proxy❌ Not available
Auth surfaceFull PlaybackAuthState + individual settersPlaybackAuthState + individual setters
Login actions✅ like, subscribe, playlist CRUD, account menu❌ Read-only subset
Auth state flowauthStateFlow: StateFlow<PlaybackAuthState>❌ Not available
PlatformsAndroidAndroid, iOS, JVM, JS
Use 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:
1

Public API call

YouTube.search(query = "Radiohead", filter = YouTube.SearchFilter.FILTER_SONG) — a suspend function wrapped in runCatching.
2

Transport dispatch

YouTube delegates to the private InnerTube instance: innerTube.search(WEB_REMIX, query, filter.value).
3

Request spec construction

buildSearchRequest(client, locale, authState, includeLoginContext, query, params, continuation) produces an InnerTubeRequestSpec containing the serialised request body from buildSearchBody(...).
4

Header assembly

buildInnerTubeRequestMetadata(client, authState, setLogin) assembles X-YouTube-Client-Name, X-Goog-Visitor-Id, Authorization: SAPISIDHASH, and the cookie header as required.
5

HTTP execution

InnerTube.executeRequest(spec) performs an HTTP POST to https://music.youtube.com/youtubei/v1/search, applying the retry wrapper on IOException.
6

JSON deserialisation

The response body is decoded via kotlinx.serialization into a SearchResponse object by calling .body<SearchResponse>() on the Ktor HttpStatement.
7

Page parsing

SearchPage.toYTItem(renderer) converts each MusicResponsiveListItemRenderer in the shelf into a SongItem, AlbumItem, ArtistItem, or PlaylistItem.
8

Result returned

A SearchResult(items = …, continuation = …) is returned as Result.success(...). Any exception in any step surfaces as Result.failure(...).

Build docs developers (and LLMs) love