YouTube.js
A full-featured wrapper around the InnerTube API
Special thanks to:
Table of Contents
-
About
-
Getting Started
-
Usage
- Extending the library
- Contributing
- Contact
- Disclaimer
- License
Description
InnerTube is an API used by all YouTube clients. It was created to simplify the deployment of new features and experiments across the platform1. This library handles all the low-level communication with InnerTube, providing a simple, and efficient way to interact with YouTube programmatically. It is designed to emulate an actual client as closely as possible, including how API responses are parsed.
If you have any questions or need help, feel free to reach out to us on our Discord server or open an issue here.
Getting Started
Prerequisites
YouTube.js runs on Node.js, Deno, and modern browsers.
It requires a runtime with the following features:
fetch
- On Node we use undici's fetch implementation which requires Node.js 16.8+. You may provide your fetch implementation if you need to use an older version. See providing your own fetch implementation for more information.
- The
Response
object returned by fetch must thus be spec compliant and return a ReadableStream
object if you want to use the VideoInfo#download
method. (Implementations like node-fetch
returns a non-standard Readable
object.)
EventTarget
and CustomEvent
are required.
Installation
npm install youtubei.js@latest
yarn add youtubei.js@latest
npm install github:LuanRT/YouTube.js
When using Deno, you can import YouTube.js directly from deno.land:
import { Innertube } from 'https://deno.land/x/youtubei/deno.ts';
Usage
Create an InnerTube instance:
import { Innertube } from 'youtubei.js';
const youtube = await Innertube.create();
Browser Usage
To use YouTube.js in the browser you must proxy requests through your own server. You can see our simple reference implementation in Deno in examples/browser/proxy/deno.ts
.
You may provide your own fetch implementation to be used by YouTube.js. Which we will use here to modify and send the requests through our proxy. See examples/browser/web
for a simple example using Vite.
import { Innertube } from 'youtubei.js/web';
await Innertube.create({
fetch: async (input: RequestInfo | URL, init?: RequestInit) => {
return fetch(request, init);
}
});
Streaming
YouTube.js supports streaming of videos in the browser by converting YouTube's streaming data into an MPEG-DASH manifest.
The example below uses dash.js
to play the video.
import { Innertube } from 'youtubei.js/web';
import dashjs from 'dashjs';
const youtube = await Innertube.create({ });
const videoInfo = await youtube.getInfo('videoId');
const manifest = videoInfo.toDash(url => {
return url;
});
const uri = "data:application/dash+xml;charset=utf-8;base64," + btoa(manifest);
const videoElement = document.getElementById('video_player');
const player = dashjs.MediaPlayer().create();
player.initialize(videoElement, uri, true);
A fully working example can be found in examples/browser/web
. Alternatively, you can view it live at ytjsexample.pages.dev.
Providing your own fetch implementation
You may provide your own fetch implementation to be used by YouTube.js. This can be useful in some cases to modify the requests before they are sent and transform the responses before they are returned (eg. for proxies).
const yt = await Innertube.create({
fetch: async (input: RequestInfo | URL, init?: RequestInit) => {
return new Response(
);
}
});
Caching
To improve performance, you may wish to cache the transformed player instance which we use to decode the streaming urls.
Our cache uses the node:fs
module in Node-like environments, Deno.writeFile
in Deno, and indexedDB
in browsers.
import { Innertube, UniversalCache } from 'youtubei.js';
const yt = await Innertube.create({
cache: new UniversalCache(false)
});
const yt = await Innertube.create({
cache: new UniversalCache(
true,
'./.cache'
)
});
API
-
Innertube
Objects
Methods
getInfo(video_id, client?)
Retrieves video info, including playback data and even layout elements such as menus, buttons, etc — all nicely parsed.
Returns: Promise<VideoInfo>
Param | Type | Description |
---|
video_id | string | The id of the video |
client? | InnerTubeClient | WEB , ANDROID , YTMUSIC , YTMUSIC_ANDROID or TV_EMBEDDED |
Methods & Getters
-
<info>#like()
-
<info>#dislike()
-
<info>#removeRating()
-
<info>#getLiveChat()
- Returns a LiveChat instance.
-
<info>#chooseFormat(options)
- Used to choose streaming data formats.
-
<info>#toDash(url_transformer?, format_filter?)
- Converts streaming data to an MPEG-DASH manifest.
-
<info>#download(options)
-
<info>#filters
- Returns filters that can be applied to the watch next feed.
-
<info>#selectFilter(name)
- Applies the given filter to the watch next feed and returns a new instance of
VideoInfo
.
-
<info>#getWatchNextContinuation()
- Retrieves the next batch of items for the watch next feed.
-
<info>#addToWatchHistory()
- Adds the video to the watch history.
-
<info>#page
- Returns original InnerTube response (sanitized).
getBasicInfo(video_id, client?)
Suitable for cases where you only need basic video metadata. Also, it is faster than getInfo()
.
Returns: Promise<VideoInfo>
Param | Type | Description |
---|
video_id | string | The id of the video |
client? | InnerTubeClient | WEB , ANDROID , YTMUSIC_ANDROID , YTMUSIC , TV_EMBEDDED |
search(query, filters?)
Searches the given query on YouTube.
Returns: Promise<Search>
Note
Search
extends the Feed
class.
Param | Type | Description |
---|
query | string | The search query |
filters? | SearchFilters | Search filters |
Methods & Getters
-
<search>#selectRefinementCard(SearchRefinementCard | string)
- Applies given refinement card and returns a new Search instance.
-
<search>#refinement_card_queries
- Returns available refinement cards, this is a simplified version of the
refinement_cards
object.
-
<search>#getContinuation()
- Retrieves next batch of results.
getSearchSuggestions(query)
Retrieves search suggestions for given query.
Returns: Promise<string[]>
Param | Type | Description |
---|
query | string | The search query |
Retrieves comments for given video.
Returns: Promise<Comments>
Param | Type | Description |
---|
video_id | string | The video id |
sort_by | string | Can be: TOP_COMMENTS or NEWEST_FIRST |
See ./examples/comments
for examples.
getHomeFeed()
Retrieves YouTube's home feed.
Returns: Promise<HomeFeed>
Note
HomeFeed
extends the FilterableFeed
class.
Methods & Getters
getGuide()
Retrieves YouTube's content guide.
Returns: Promise<Guide>
getLibrary()
Retrieves the account's library.
Returns: Promise<Library>
Note
Library
extends the Feed
class.
Methods & Getters
<library>#history
<library>#watch_later
<library>#liked_videos
<library>#playlists_section
<library>#clips
getHistory()
Retrieves watch history.
Returns: Promise<History>
Note
History
extends the Feed
class.
Methods & Getters
<history>#getContinuation()
- Retrieves next batch of contents.
getTrending()
Retrieves trending content.
Returns: Promise<TabbedFeed<IBrowseResponse>>
getSubscriptionsFeed()
Retrieves the subscriptions feed.
Returns: Promise<Feed<IBrowseResponse>>
getChannel(id)
Retrieves contents for a given channel.
Returns: Promise<Channel>
Note
Channel
extends the TabbedFeed
class.
Param | Type | Description |
---|
id | string | Channel id |
Methods & Getters
<channel>#getVideos()
<channel>#getShorts()
<channel>#getLiveStreams()
<channel>#getPlaylists()
<channel>#getHome()
<channel>#getCommunity()
<channel>#getChannels()
<channel>#getAbout()
<channel>#search(query)
<channel>#applyFilter(filter)
<channel>#applyContentTypeFilter(content_type_filter)
<channel>#applySort(sort)
<channel>#getContinuation()
<channel>#filters
<channel>#content_type_filters
<channel>#sort_filters
<channel>#page
See ./examples/channel
for examples.
getNotifications()
Retrieves notifications.
Returns: Promise<NotificationsMenu>
Methods & Getter
<notifications>#getContinuation()
- Retrieves next batch of notifications.
getUnseenNotificationsCount()
Retrieves unseen notifications count.
Returns: Promise<number>
getPlaylist(id)
Retrieves playlist contents.
Returns: Promise<Playlist>
Note
Playlist
extends the Feed
class.
Param | Type | Description |
---|
id | string | Playlist id |
Methods & Getter
<playlist>#items
- Returns the items of the playlist.
getHashtag(hashtag)
Retrieves a given hashtag's page.
Returns: Promise<HashtagFeed>
Note
HashtagFeed
extends the FilterableFeed
class.
Param | Type | Description |
---|
hashtag | string | The hashtag |
Methods & Getter
<hashtag>#applyFilter(filter)
- Applies given filter and returns a new
HashtagFeed
instance.
<hashtag>#getContinuation()
- Retrieves next batch of contents.
getStreamingData(video_id, options)
Returns deciphered streaming data.
Note
This will be deprecated in the future. It is recommended to retrieve streaming data from a VideoInfo
/TrackInfo
object instead if you want to select formats manually, see the example below.
const info = await yt.getBasicInfo('somevideoid');
const url = info.streaming_data?.formats[0].decipher(yt.session.player);
console.info('Playback url:', url);
Returns: Promise<object>
Param | Type | Description |
---|
video_id | string | Video id |
options | FormatOptions | Format options |
download(video_id, options?)
Downloads a given video.
Returns: Promise<ReadableStream<Uint8Array>>
Param | Type | Description |
---|
video_id | string | Video id |
options | DownloadOptions | Download options |
See ./examples/download
for examples.
resolveURL(url)
Resolves a given url.
Returns: Promise<NavigationEndpoint>
Param | Type | Description |
---|
url | string | Url to resolve |
call(endpoint, args?)
Utility to call navigation endpoints.
Returns: Promise<T extends IParsedResponse | IParsedResponse | ApiResponse>
Param | Type | Description |
---|
endpoint | NavigationEndpoint | The target endpoint |
args? | object | Additional payload arguments |
Extending the library
YouTube.js is completely modular and easy to extend. Almost all methods, classes, and utilities used internally are exposed and can be used to implement your own extensions without having to modify the library's source code.
For example, let's say we want to implement a method to retrieve video info manually. We can do that by using an instance of the Actions
class:
import { Innertube } from 'youtubei.js';
(async () => {
const yt = await Innertube.create();
async function getVideoInfo(videoId: string) {
const videoInfo = await yt.actions.execute('/player', {
videoId,
client: 'YTMUSIC',
parse: true
});
return videoInfo;
}
const videoInfo = await getVideoInfo('jLTOuvBTLxA');
console.info(videoInfo);
})();
Or perhaps there's a NavigationEndpoint
in a parsed response and we want to call it to see what happens:
import { Innertube, YTNodes } from 'youtubei.js';
(async () => {
const yt = await Innertube.create();
const artist = await yt.music.getArtist('UC52ZqHVQz5OoGhvbWiRal6g');
const albums = artist.sections[1].as(YTNodes.MusicCarouselShelf);
const button = albums.as(YTNodes.MusicCarouselShelf).header?.more_content;
if (button) {
const page = await button.endpoint.call(yt.actions, { parse: true });
console.info(page);
}
})();
Parser
YouTube.js' parser allows you to parse InnerTube responses and turn their nodes into strongly typed objects that can be easily manipulated. It also provides a set of utility methods that make working with InnerTube much easier.
Example:
import { Parser, YTNodes } from 'youtubei.js';
import { readFileSync } from 'fs';
const data = readFileSync('./artist.json').toString();
const page = Parser.parseResponse(JSON.parse(data));
const header = page.header?.item().as(YTNodes.MusicImmersiveHeader, YTNodes.MusicVisualHeader);
console.info('Header:', header);
const tab = page.contents?.item().as(YTNodes.SingleColumnBrowseResults).tabs.firstOfType(YTNodes.Tab);
if (!tab)
throw new Error('Target tab not found');
if (!tab.content)
throw new Error('Target tab appears to be empty');
const sections = tab.content?.as(YTNodes.SectionList).contents.as(YTNodes.MusicCarouselShelf, YTNodes.MusicDescriptionShelf, YTNodes.MusicShelf);
console.info('Sections:', sections);
Documentation for the parser can be found here.
Contributing
Contributions, issues, and feature requests are welcome.
Feel free to check the issues page and our guidelines if you want to contribute.
Thank you to all the wonderful people who have contributed to this project:
Contact
LuanRT - @thesciencephile - luan.lrt4@gmail.com
Project Link: https://github.com/LuanRT/YouTube.js
Disclaimer
This project is not affiliated with, endorsed, or sponsored by YouTube or any of its affiliates or subsidiaries.
All trademarks, logos, and brand names are the property of their respective owners and are used only to directly describe the services being provided, as such, any usage of trademarks to refer to such services is considered nominative use.
Should you have any questions or concerns please contact me directly via email.
License
Distributed under the MIT License.
(back to top)