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.

Overview

YouTube’s InnerTube API does not use page numbers or offset-based pagination. Instead, every large result set is paginated using opaque continuation tokens — base64-encoded strings that encode cursor state on YouTube’s servers. You never parse or construct these strings; you simply pass them back to the appropriate *Continuation method to receive the next batch of results. The pagination contract is simple:
  • Every page object that supports pagination exposes a continuation: String? field.
  • When continuation is non-null, there is at least one more page available.
  • When continuation is null, you have reached the last page.

Continuation Pairs

Each paginated resource has a dedicated pair of methods: an initial fetch and a continuation fetch.
// Initial fetch — returns up to ~20 results
val page: Result<SearchResult> = YouTube.search(query, SearchFilter.FILTER_SONG)

// Continuation fetch
val nextPage: Result<SearchResult> = YouTube.searchContinuation(continuation)
SearchResult carries:
data class SearchResult(
    val items: List<YTItem>,
    val continuation: String?,
)

Playlist Songs

// Initial fetch — returns the first batch of songs
val page: Result<PlaylistPage> = YouTube.playlist(playlistId)

// Continuation fetch — returns PlaylistContinuationPage
val nextPage: Result<PlaylistContinuationPage> = YouTube.playlistContinuation(continuation)
PlaylistContinuationPage carries:
data class PlaylistContinuationPage(
    val songs: List<SongItem>,
    val continuation: String?,
)

Artist Items

// Initial fetch — returns the first batch of items in an artist section
val page: Result<ArtistItemsPage> = YouTube.artistItems(browseEndpoint)

// Continuation fetch — returns ArtistItemsContinuationPage
val nextPage: Result<ArtistItemsContinuationPage> = YouTube.artistItemsContinuation(continuation)
ArtistItemsContinuationPage carries:
data class ArtistItemsContinuationPage(
    val items: List<YTItem>,
    val continuation: String?,
)

Library

// Initial fetch — returns a library tab page
val page: Result<LibraryPage> = YouTube.library(browseId, tabIndex)

// Continuation fetch — returns LibraryContinuationPage
val nextPage: Result<LibraryContinuationPage> = YouTube.libraryContinuation(continuation)
LibraryContinuationPage carries:
data class LibraryContinuationPage(
    val items: List<YTItem>,
    val continuation: String?,
)

Home Feed

The home feed uses an inline continuation — you pass the token as a parameter to the same method rather than calling a separate *Continuation function:
// Initial fetch (no continuation)
val homePage: Result<HomePage> = YouTube.home()

// Subsequent pages — pass the token to the same method
val nextPage: Result<HomePage> = YouTube.home(continuation = token)

Next / Up-Next Queue

Like the home feed, next() accepts a continuation parameter directly:
// Initial fetch
val result: Result<NextResult> = YouTube.next(endpoint = WatchEndpoint(videoId = "xxx"))

// Load more related items
val moreResult: Result<NextResult> = YouTube.next(
    endpoint = WatchEndpoint(videoId = "xxx"),
    continuation = token,
)

Comments

Comments use a three-level continuation chain: initial load → more comment pages → reply pages.
// Step 1: Fetch the first page of comment threads + a continuation token
val (threads, token) = YouTube.comments(videoId).getOrThrow()

// Step 2: Load the next page of comment threads
val (moreThreads, nextToken) = YouTube.commentContinuation(token!!).getOrThrow()

// Step 3: Load replies for a specific thread
val replyToken: String? = threads.firstOrNull()?.replies?.replyToken
if (replyToken != null) {
    val (replies, nextReplyToken) = YouTube.commentReplies(replyToken).getOrThrow()
}

Complete Example: Fetching All Songs in a Playlist

The following coroutine iterates through every page of a playlist by looping on playlistContinuation until no more pages remain:
1

Fetch the first page

Call YouTube.playlist(playlistId) to get the PlaylistPage. The first batch of songs and an initial continuation token (if there are more pages) are returned.
2

Collect songs and check continuation

Extract the songs from PlaylistPage and inspect playlist.continuation (or the continuation on the inner song shelf). If non-null, there are more songs to load.
3

Loop with playlistContinuation

Call YouTube.playlistContinuation(token) in a loop, accumulating results, until continuation is null.
suspend fun fetchAllPlaylistSongs(playlistId: String): List<SongItem> {
    val allSongs = mutableListOf<SongItem>()

    // Step 1 — initial page
    val playlistPage = YouTube.playlist(playlistId).getOrNull() ?: return allSongs
    allSongs.addAll(playlistPage.songs)

    // Step 2 — get the first continuation token from the page
    var continuation: String? = playlistPage.songsContinuation

    // Step 3 — loop until exhausted
    val seenTokens = mutableSetOf<String>()

    while (continuation != null) {
        // Guard against infinite loops: break if we've seen this token before
        if (!seenTokens.add(continuation)) {
            break
        }

        val continuationPage = YouTube.playlistContinuation(continuation).getOrNull() ?: break
        allSongs.addAll(continuationPage.songs)

        // null continuation means no more pages
        continuation = continuationPage.continuation
    }

    return allSongs
}

// Usage
viewModelScope.launch {
    val songs = fetchAllPlaylistSongs("PLxxxxxxxxxxxx")
    println("Total songs loaded: ${songs.size}")
}
Loop safety: Always check that continuation != null before making another request. Additionally, track seen tokens with a Set<String> to guard against the rare case where the API returns the same token twice in a cycle — this is the same pattern used internally by YouTube.albumSongs(), which also caps iterations at 50 requests (maxRequests = 50) to prevent runaway API consumption on very large collections.

Summary Table

Method pairToken fieldReturn type
search()searchContinuation(token)SearchResult.continuationSearchResult
playlist()playlistContinuation(token)PlaylistContinuationPage.continuationPlaylistContinuationPage
artistItems()artistItemsContinuation(token)ArtistItemsContinuationPage.continuationArtistItemsContinuationPage
library()libraryContinuation(token)LibraryContinuationPage.continuationLibraryContinuationPage
home(continuation = token)HomePage.continuationHomePage
next(continuation = token)NextResult.continuationNextResult
comments()commentContinuation(token)Second value of PairPair<List<CommentThreadRenderer>, String?>
commentContinuation(token)commentReplies(replyToken)Reply token from threadPair<List<CommentRenderer>, String?>

Build docs developers (and LLMs) love