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
| Constant | Description |
|---|
LibraryFilter.FILTER_RECENT_ACTIVITY | Most recently interacted-with content across all types |
LibraryFilter.FILTER_RECENTLY_PLAYED | Recently played items (songs, albums, playlists) |
LibraryFilter.FILTER_PLAYLISTS_ALPHABETICAL | Liked and saved playlists, alphabetically sorted |
LibraryFilter.FILTER_PLAYLISTS_RECENTLY_SAVED | Liked 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.
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}")
}
}
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")
}
}
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:
- Init upload — calls
https://upload.youtube.com/upload/usermusic/http with X-Goog-Upload-Command: start to obtain a resumable upload URL.
- 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".
| Constant | Value |
|---|
YouTube.SUPPORTED_UPLOAD_TYPES | ["mp3", "m4a", "wma", "flac", "ogg"] |
YouTube.MAX_UPLOAD_SIZE | 314572800L (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.