Skip to main content

Documentation Index

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

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

The InnerTube SDK exposes the full YouTube Music library surface: browsing saved playlists and albums, reading and clearing listening history, liking and unliking content, subscribing to artists, managing individual songs in the library, and uploading or deleting privately owned tracks. Every method in this guide requires an authenticated session — set YouTube.cookie to a valid session cookie string before making any calls.

Library overview

YouTube.library(browseId, tabIndex) fetches the main library landing page for a given browse ID. Use it with the predefined LibraryFilter continuation constants to apply view filters to the library grid or shelf.

LibraryFilter constants

ConstantDescription
LibraryFilter.FILTER_RECENT_ACTIVITYMost recently interacted-with content across all types
LibraryFilter.FILTER_RECENTLY_PLAYEDRecently played items (songs, albums, playlists)
LibraryFilter.FILTER_PLAYLISTS_ALPHABETICALLiked and saved playlists, alphabetically sorted
LibraryFilter.FILTER_PLAYLISTS_RECENTLY_SAVEDLiked and saved playlists, most recently saved first
library() returns Result<LibraryPage> with items: List<YTItem> and continuation: String?.
import com.metrolist.innertube.YouTube
import com.metrolist.innertube.YouTube.LibraryFilter

suspend fun browseLibrary() {
    YouTube.library(
        browseId = "FEmusic_liked_playlists",
        tabIndex = 0,
    ).onSuccess { page ->
        println("Library items: ${page.items.size}")
        page.items.forEach { item ->
            println("  ${item.title} [${item::class.simpleName}]")
        }
        // page.continuation is non-null if there are more items
    }
}

// Applying a filter: recently played
suspend fun recentlyPlayed() {
    YouTube.libraryRecentActivity().onSuccess { page ->
        page.items.forEach { println(it.title) }
    }
}

Library continuation

When LibraryPage.continuation is non-null, call YouTube.libraryContinuation(continuation) to fetch the next page of items. The result is a LibraryContinuationPage with items: List<YTItem> and continuation: String?.
import com.metrolist.innertube.YouTube
import com.metrolist.innertube.models.YTItem

suspend fun fetchAllLibraryItems(browseId: String): List<YTItem> {
    val allItems = mutableListOf<YTItem>()

    val firstPage = YouTube.library(browseId).getOrThrow()
    allItems += firstPage.items

    var continuation = firstPage.continuation
    while (continuation != null) {
        val nextPage = YouTube.libraryContinuation(continuation).getOrThrow()
        if (nextPage.items.isEmpty()) break
        allItems += nextPage.items
        continuation = nextPage.continuation
    }

    return allItems
}

Liked albums

YouTube.library("FEmusic_liked_albums") is the standard way to browse saved albums, but the SDK also provides the dedicated libraryAlbums() pattern through the general library() call with the albums browse ID.
import com.metrolist.innertube.YouTube
import com.metrolist.innertube.models.AlbumItem

suspend fun browseAlbums() {
    YouTube.library("FEmusic_liked_albums").onSuccess { page ->
        val albums = page.items.filterIsInstance<AlbumItem>()
        albums.forEach { album ->
            println("${album.title} (${album.year}) — ${album.artists?.joinToString { it.name }}")
        }

        // Paginate if needed
        page.continuation?.let { token ->
            YouTube.libraryContinuation(token).onSuccess { more ->
                more.items.filterIsInstance<AlbumItem>().forEach { println(it.title) }
            }
        }
    }
}

Listening history

YouTube.musicHistory() returns a Result<HistoryPage>. HistoryPage contains sections, where each section groups tracks played within a time window (e.g. “Today”, “Yesterday”). To remove entries from history, collect the historyRemoveToken from the SongItem objects and pass them to YouTube.removeHistoryItems(feedbackTokens).
import com.metrolist.innertube.YouTube
import com.metrolist.innertube.models.SongItem

suspend fun clearRecentHistory() {
    val historyPage = YouTube.musicHistory().getOrThrow()

    // Collect remove tokens from all sections
    val removeTokens = historyPage.sections
        ?.flatMap { section ->
            section.songs.mapNotNull { it.historyRemoveToken }
        }
        ?: emptyList()

    if (removeTokens.isNotEmpty()) {
        YouTube.removeHistoryItems(removeTokens).onSuccess { processed ->
            println("History items removed: $processed")
        }
    }
}

Like and unlike

Videos

YouTube.likeVideo(videoId, like) sends a like or remove-like signal for a video. Pass like = true to like, like = false to undo.
import com.metrolist.innertube.YouTube

suspend fun likeVideo(videoId: String) {
    YouTube.likeVideo(videoId, like = true).onSuccess {
        println("Liked video $videoId")
    }
}

suspend fun unlikeVideo(videoId: String) {
    YouTube.likeVideo(videoId, like = false).onSuccess {
        println("Unliked video $videoId")
    }
}

Playlists

YouTube.likePlaylist(playlistId, like) saves or unsaves a playlist from your library.
import com.metrolist.innertube.YouTube

suspend fun savePlaylist(playlistId: String) {
    YouTube.likePlaylist(playlistId, like = true).onSuccess {
        println("Playlist $playlistId saved to library")
    }
}

suspend fun unsavePlaylist(playlistId: String) {
    YouTube.likePlaylist(playlistId, like = false).onSuccess {
        println("Playlist $playlistId removed from library")
    }
}

Subscribe and unsubscribe

YouTube.subscribeChannel(channelId, subscribe, params?) subscribes to or unsubscribes from an artist channel. A default params value of "EgIIAhgA" is used automatically if none is provided.
import com.metrolist.innertube.YouTube

suspend fun subscribeArtist(channelId: String) {
    YouTube.subscribeChannel(channelId, subscribe = true).onSuccess {
        println("Subscribed to channel $channelId")
    }
}

suspend fun unsubscribeArtist(channelId: String, params: String? = null) {
    YouTube.subscribeChannel(channelId, subscribe = false, params = params).onSuccess {
        println("Unsubscribed from channel $channelId")
    }
}

Song library management

The SDK provides three methods for managing individual songs in the “Liked songs” / library collection. They all use fresh feedback tokens fetched from the /next endpoint to avoid stale-token failures.
1

Add a song to library

YouTube.addSongToLibrary(videoId) calls the /next endpoint to get a fresh libraryAddToken for the track, then sends it to /feedback.
import com.metrolist.innertube.YouTube

suspend fun addToLibrary(videoId: String) {
    YouTube.addSongToLibrary(videoId).onSuccess { success ->
        println("Song added to library: $success")
    }.onFailure { error ->
        println("Failed to add to library: ${error.message}")
    }
}
2

Remove a song from library

YouTube.removeSongFromLibrary(videoId) fetches a fresh libraryRemoveToken and sends it to /feedback.
import com.metrolist.innertube.YouTube

suspend fun removeFromLibrary(videoId: String) {
    YouTube.removeSongFromLibrary(videoId).onSuccess { success ->
        println("Song removed from library: $success")
    }
}
3

Toggle library status

YouTube.toggleSongLibrary(videoId, addToLibrary) is a convenience wrapper that calls addSongToLibrary or removeSongFromLibrary depending on the boolean flag.
import com.metrolist.innertube.YouTube

suspend fun toggleLibrary(videoId: String, inLibrary: Boolean) {
    // inLibrary = true  → adds the song
    // inLibrary = false → removes the song
    YouTube.toggleSongLibrary(videoId, addToLibrary = inLibrary).onSuccess {
        println("Library state toggled for $videoId")
    }
}

Upload and delete songs

YouTube Music allows users to upload their own music files. The SDK exposes two methods for managing uploaded tracks.

Upload a song

YouTube.uploadSong(filename, data, onProgress?) is a two-step process:
  1. Init upload — calls https://upload.youtube.com/upload/usermusic/http with X-Goog-Upload-Command: start to obtain a resumable upload URL.
  2. Upload bytes — POSTs the file bytes to the returned URL with X-Goog-Upload-Command: upload, finalize.
Returns Result<Boolean>true when the upload status header equals "final".
ConstantValue
YouTube.SUPPORTED_UPLOAD_TYPES["mp3", "m4a", "wma", "flac", "ogg"]
YouTube.MAX_UPLOAD_SIZE314572800L (300 MB)
import com.metrolist.innertube.YouTube
import java.io.File

suspend fun uploadTrack(file: File) {
    val extension = file.extension.lowercase()
    check(extension in YouTube.SUPPORTED_UPLOAD_TYPES) {
        "Unsupported file type: $extension"
    }
    check(file.length() <= YouTube.MAX_UPLOAD_SIZE) {
        "File exceeds 300 MB limit"
    }

    YouTube.uploadSong(
        filename = file.name,
        data = file.readBytes(),
        onProgress = { progress ->
            println("Upload progress: ${(progress * 100).toInt()}%")
        },
    ).onSuccess { success ->
        println("Upload complete: $success")
    }.onFailure { error ->
        println("Upload failed: ${error.message}")
    }
}

Delete an uploaded song

YouTube.deleteUploadedSong(entityId) removes a privately owned (uploaded) track from the library. Pass the uploadEntityId from the SongItem — this is populated for songs returned when browsing the uploaded music section.
import com.metrolist.innertube.YouTube
import com.metrolist.innertube.models.SongItem

suspend fun deleteUploadedTrack(song: SongItem) {
    val entityId = song.uploadEntityId
        ?: error("uploadEntityId is null — only uploaded songs can be deleted this way")

    YouTube.deleteUploadedSong(entityId).onSuccess { success ->
        println("Deleted uploaded track \"${song.title}\": $success")
    }
}

Feedback tokens

All library operations — adding/removing songs, clearing history, toggling episode save state — are ultimately routed through YouTube.feedback(tokens). This method posts a list of opaque feedback token strings to the /feedback endpoint and returns Result<Boolean> indicating whether all tokens were processed successfully.
import com.metrolist.innertube.YouTube

suspend fun sendFeedback(tokens: List<String>) {
    YouTube.feedback(tokens).onSuccess { allProcessed ->
        println("Feedback processed: $allProcessed")
    }
}
You rarely need to call YouTube.feedback() directly. Prefer the higher-level wrappers (addSongToLibrary, removeHistoryItems, etc.) which obtain fresh tokens before calling feedback. Direct calls are appropriate when you already hold a valid token string from a SongItem.libraryAddToken, SongItem.historyRemoveToken, or similar field.

Build docs developers (and LLMs) love