Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/faraasaaay/innertube-v2/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through adding InnerTube to an Android project, configuring the YouTube object, and running your first real API calls.
Every YouTube method is a suspend function. All calls must be made from a coroutine scope — for example inside viewModelScope.launch { }, lifecycleScope.launch { }, or a runBlocking { } block during testing.
1

Add the Module

InnerTube is distributed as a local Android library module, not a Maven artifact. Clone or copy the innertube/ directory into your project root, then wire it up in your Gradle files.settings.gradle.kts — include the module:
settings.gradle.kts
include(":innertube")
app/build.gradle.kts — add the module dependency:
app/build.gradle.kts
dependencies {
    implementation(project(":innertube"))
}
The library requires core library desugaring (already configured in the module itself). Ensure your app module also has it enabled if your minSdk is below 26:
app/build.gradle.kts
android {
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
    }
}

dependencies {
    coreLibraryDesugaring(libs.desugaring)
    implementation(project(":innertube"))
}
After syncing Gradle, you can import com.music.innertube.YouTube anywhere in your app code.
2

Configure the YouTube Object

Before making any API calls, set the locale so responses return content in the right language and region. The locale defaults to the device’s system locale, but you should set it explicitly for predictable behaviour:
import com.music.innertube.YouTube
import com.music.innertube.models.YouTubeLocale

// Set locale: gl = ISO 3166-1 country code, hl = BCP-47 language tag
YouTube.locale = YouTubeLocale(gl = "US", hl = "en")
For guest browsing (no account), fetch a visitorData token. This is an anonymous session identifier that improves response quality and is required for some endpoints:
// In a coroutine scope:
YouTube.refreshVisitorData()
    .onSuccess { println("Visitor data refreshed") }
    .onFailure { it.printStackTrace() }
refreshVisitorData() fetches a fresh token from YouTube and automatically stores it in YouTube.visitorData — you don’t need to assign it manually.
3

Search for Music

Use YouTube.search() with one of the built-in SearchFilter constants to search for songs, albums, artists, videos, or playlists.
import com.music.innertube.YouTube
import com.music.innertube.models.SongItem

// Search for songs matching a query
val result = YouTube.search("Bohemian Rhapsody", YouTube.SearchFilter.FILTER_SONG)

result.onSuccess { searchResult ->
    searchResult.items.filterIsInstance<SongItem>().forEach { song ->
        println("ID:        ${song.id}")
        println("Title:     ${song.title}")
        println("Artists:   ${song.artists.joinToString { it.name }}")
        println("Thumbnail: ${song.thumbnail}")
        println("---")
    }

    // Paginate with the continuation token
    val continuation = searchResult.continuation
    if (continuation != null) {
        val nextPage = YouTube.searchContinuation(continuation)
        // handle nextPage.items ...
    }
}.onFailure {
    it.printStackTrace()
}
Available SearchFilter constants:
FilterContent type
SearchFilter.FILTER_SONGAudio tracks
SearchFilter.FILTER_VIDEOMusic videos
SearchFilter.FILTER_ALBUMAlbums & EPs
SearchFilter.FILTER_ARTISTArtist channels
SearchFilter.FILTER_FEATURED_PLAYLISTCurated playlists
SearchFilter.FILTER_COMMUNITY_PLAYLISTUser playlists
YouTube.search("Bohemian Rhapsody", YouTube.SearchFilter.FILTER_SONG)
4

Fetch an Artist Page

Pass an artist browseId (e.g. UCiMhD4jzUqG-IgPzUmmZifg) to YouTube.artist() to retrieve an ArtistPage containing the artist’s metadata and content sections.
import com.music.innertube.YouTube
import com.music.innertube.pages.ArtistPage

val browseId = "UCiMhD4jzUqG-IgPzUmmZifg" // Queen on YouTube Music

YouTube.artist(browseId).onSuccess { page: ArtistPage ->
    val artist = page.artist
    println("Name:       ${artist.title}")
    println("Thumbnail:  ${artist.thumbnail}")
    println("Subscribers: ${page.subscriberCountText}")
    println("Monthly listeners: ${page.monthlyListenerCount}")
    println("Bio:        ${page.description}")

    // Sections contain the artist's songs, albums, singles, videos, etc.
    page.sections.forEach { section ->
        println("Section: ${section.title} (${section.items.size} items)")
    }
}.onFailure {
    it.printStackTrace()
}
The ArtistPage.artist field also exposes shuffleEndpoint and radioEndpoint — pass these to YouTube.next() to start a shuffle or radio queue for the artist.
5

Resolve a Player Stream

Use YouTube.player() to fetch a PlayerResponse containing stream URLs and playback metadata for a given video ID.
import com.music.innertube.YouTube
import com.music.innertube.models.YouTubeClient
import com.music.innertube.models.response.PlayerResponse

val videoId = "tgbNymZ7vqY"

YouTube.player(
    videoId = videoId,
    playlistId = null,
    client = YouTubeClient.WEB_REMIX
).onSuccess { response: PlayerResponse ->
    if (response.playabilityStatus.status == "OK") {
        val streamingData = response.streamingData

        // Adaptive formats (separate audio/video streams)
        streamingData?.adaptiveFormats?.forEach { format ->
            println("itag=${format.itag}  mimeType=${format.mimeType}  url=${format.url}")
        }

        // Progressive formats (combined audio+video)
        streamingData?.formats?.forEach { format ->
            println("itag=${format.itag}  quality=${format.quality}  url=${format.url}")
        }
    } else {
        println("Not playable: ${response.playabilityStatus.reason}")
    }
}.onFailure {
    it.printStackTrace()
}
Some stream URLs are cipher-protected. If format.url is null and format.signatureCipher is present, pass the response through YouTube.newPipePlayer() or use YouTubeExtractor.decryptUrl() to resolve the final playable URL.
Available YouTubeClient constants for player():
ClientNotes
YouTubeClient.WEB_REMIXPrimary YouTube Music client. Supports login and PoToken.
YouTubeClient.ANDROID_VR_1_61_48No login required; useful for unauthenticated playback.
YouTubeClient.TVHTML5_SIMPLY_EMBEDDED_PLAYERBypasses age restrictions without login.
YouTubeClient.IOSiOS client; no signature timestamp required.

Warm Up the Extractor at App Start

Call YouTubeExtractor.ensureInitialized() on a background thread during Application.onCreate(). This pre-fetches and parses the YouTube player JS, so the very first song play resolves its stream URL instantly instead of waiting for a cold-start network round-trip.
import com.music.innertube.YouTubeExtractor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // Set the cache directory so parsed JS snippets survive process restarts
        YouTubeExtractor.cacheDir = cacheDir

        // Pre-initialize in the background — thread-safe, no-op if already ready
        GlobalScope.launch(Dispatchers.IO) {
            YouTubeExtractor.ensureInitialized()
        }
    }
}
The extractor caches parsed decipher snippets to disk for up to 24 hours, so subsequent app launches load from cache without any network call.

Next Steps

  • Configuration — Set locale, cookie authentication, proxy, and IP version.
  • The YouTube Object — Explore every method on the YouTube singleton.
  • Search API — Deep-dive into filters, pagination, and result types.

Build docs developers (and LLMs) love