https://github.com/LuanRT/YouTube.js Skip to content Sign up * Product + Features + Mobile + Actions + Codespaces + Packages + Security + Code review + Issues + Integrations + GitHub Sponsors + Customer stories * Team * Enterprise * Explore + Explore GitHub + Learn and contribute + Topics + Collections + Trending + Learning Lab + Open source guides + Connect with others + The ReadME Project + Events + Community forum + GitHub Education + GitHub Stars program * Marketplace * Pricing + Plans + Compare plans + Contact Sales + Education [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this user All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up {{ message }} LuanRT / YouTube.js Public * * Notifications * Fork 27 * Star 425 full-featured wrapper around YouTube's private API www.npmjs.com/package/youtubei.js MIT License 425 stars 27 forks Star Notifications * Code * Issues 2 * Pull requests 0 * Discussions * Actions * Projects 0 * Wiki * Security * Insights More * Code * Issues * Pull requests * Discussions * Actions * Projects * Wiki * Security * Insights This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. main Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags 1 branch 17 tags Code Latest commit @LuanRT LuanRT chore(tests): use results from ytmusic ... 0734093 Apr 13, 2022 chore(tests): use results from ytmusic 0734093 Git stats * 271 commits Files Permalink Failed to load latest commit information. Type Name Latest commit message Commit time .github examples lib test LICENSE README.md index.js package-lock.json package.json View code [ ] YouTube.js About Features Do I need an API key to use this? Getting Started Prerequisites Installation Usage Doing a simple search Get search suggestions: Get video info: Get comments: Get home feed: Get watch history: Get subscriptions feed: Get notifications: Get unseen notifications count: Get song lyrics: Get playlist: Interactions: Account Settings Notification settings: Privacy settings: Live chats: Downloading videos: Signing-in: OAuth: Cookies: Contributing Contact Disclaimer License README.md YouTube.js A full-featured wrapper around the Innertube API, which is what YouTube itself uses. Report Bug * Request Feature [badge] [6874747073] [6874747073] [6874747073] [6874747073] Table of Contents 1. About The Project + Features 2. Getting Started + Prerequisites + Installation 3. Usage + Interactions + Livechats + Downloading videos + Signing in 4. Contributing 5. License 6. Contact 7. Disclaimer About Innertube is an API used across all YouTube clients, it was made to simplify the internal structure of the platform and make it easy to push updates. This library takes advantage of that API, therefore providing a simple & efficient way to interact with YouTube programmatically. And big thanks to @gatecrasher777 for his research on the workings of the Innertube API! Features As of now, this is one of the most advanced & stable YouTube libraries out there, here's a short summary of its features: * Search videos, playlists, music, albums etc * Get detailed info about any video or playlist * Fetch live chat & live stats in real time * Change notification preferences for a channel * Subscribe/Unsubscribe/Like/Dislike/Comment etc * Easily sign in to any Google Account * Change an account's settings. * Get subscriptions/home feed * Get notifications * Get watch history * Download videos Do note that you must be signed-in to perform actions that involve an account, such as commenting, subscribing, sending messages to a live chat, etc. Do I need an API key to use this? No, YouTube.js does not use any official API so no API keys are required. Getting Started Prerequisites * NodeJS v14 or greater To verify things are set up properly, you can run this: node --version Installation * NPM: npm install youtubei.js@latest * Yarn: yarn add youtubei.js@latest Usage First of all we're gonna start by initializing the Innertube instance. And to make things faster, you should do this only once and reuse the Innertube object when needed. const Innertube = require('youtubei.js'); const youtube = await new Innertube(); Doing a simple search YouTube: const search = await youtube.search('Looking for life on Mars - Documentary'); YTMusic: const search = await youtube.search('Interstellar Main Theme', { client: 'YTMUSIC' }); YouTube Output { query: string, corrected_query: string, estimated_results: number, videos: [ { id: string, url: string, title: string, description: string, metadata:{ view_count: string, short_view_count_text: { simple_text: string, accessibility_label: string }, thumbnails: [Array], duration: { seconds: number, simple_text: string, accessibility_label: string }, published: string, badges:[Array], owner_badges:[Array] } } //... ] } YTMusic Output { query:string, corrected_query:string, results:{ top_result:[Array], // Can be anything; video, playlist, artist etc.. songs:[ { id:string, title:string, artist:string, album:string, duration:string, thumbnails:[ Array ] }, //... ], videos:[ { id:string, title:string, author:string, views:string, duration:string, thumbnails:[Array] }, //... ], albums:[ { id:string, title:string, author:string, year:string, thumbnails:[Array] }, //... ], featured_playlists:[ { id:string, title:string, author:string, channel_id:string, total_items:number }, //... ], community_playlists:[ { id:string, title:string, author:string, channel_id:string, total_items:number }, //... ], artists:[ { id:string, name:string, subscribers:string, thumbnails:[Array] }, //... ] } } Get search suggestions: const suggestions = await youtube.getSearchSuggestions('QUERY', { client: 'YOUTUBE' // Use YTMUSIC if you want music search suggestions }) Output [ { text: string, bold_text: string }, //... ] Get video info: const video = await youtube.getDetails('VIDEO_ID'); Output { title: string, description: string, thumbnail: { url: string, width: number, height: number }, metadata: { embed: { iframeUrl: string, flashUrl: string, width: number, height: number, flashSecureUrl: string }, likes: number, dislikes: number, view_count: number, average_rating: number, length_seconds: number, channel_id: string, channel_url: string, external_channel_id: string, allow_ratings: boolean, is_live_content: boolean, is_family_safe: boolean, is_unlisted: boolean, is_private: boolean, is_liked: boolean, is_disliked: boolean, is_subscribed: boolean, subscriber_count: string, current_notification_preference: string, likes: { count: number, short_count_text: string }, publish_date_text: string, has_ypc_metadata: boolean, category: string, channel_name: string, publish_date: string, upload_date: string, keywords: [Array] } } Get comments: const response = await youtube.getComments('VIDEO_ID'); Alternatively you can use: const video = await youtube.getDetails('VIDEO_ID'); const response = await video.getComments(); Output { comments: [ { text: string, author: { name: string, thumbnail: [ { url: string, width: number, height: number } ], channel_id: string }, metadata:{ published: string, is_liked: boolean, is_disliked: boolean, is_pinned: boolean, is_channel_owner: boolean, like_count: number, reply_count: number, id: string } }, //... ], comment_count: string // not available in continuations } Reply to, like and dislike comments: await response.comments[0].like(); await response.comments[0].dislike(); await response.comments[0].reply('Nice comment!'); Get comment replies: const replies = await response.comments[0].getReplies(); Get comments/replies continuation: const continuation = await response.getContinuation(); const replies_continuation = await replies.getContinuation(); Get home feed: const homefeed = await youtube.getHomeFeed(); Output { videos: [ { id: string, title: string, description: string, channel: { id: string, name: string, url: string }, metadata: { view_count: string, short_view_count_text: { simple_text: string, accessibility_label: string }, thumbnail: { url: string, width: number, height: number }, moving_thumbnail: { url: string, width: number, height: number }, published: string, duration: { seconds: number, simple_text: string, accessibility_label: string }, badges: string, owner_badges: [Array] } }, // ... ] } Get continuation: const continuation = await homefeed.getContinuation(); Get watch history: const history = await youtube.getHistory(); Output { items: [ { date: string, videos: [ { id: string, title: string, channel: { id: string, name: string, url: string }, metadata: { view_count: string, short_view_count_text: { simple_text: string, accessibility_label: string }, thumbnail: { url: string, width: number, height: number }, moving_thumbnail: { url: string, width: number, height: number }, published: string, badges: [Array], owner_badges: [Array] } }, //... ] }, //... ] } Get continuation: const continuation = await history.getContinuation(); Get subscriptions feed: const mysubsfeed = await youtube.getSubscriptionsFeed(); Output { items: [ { date: string, videos: [ { id: string, title: string, description: string, channel: { id: string, name: string, url: string }, metadata: { view_count: string, short_view_count_text: { simple_text: string, accessibility_label: string }, thumbnail: { url: string, width: number, height: number }, moving_thumbnail: { url: string, width: number, height: number }, published: string, badges: [Array], owner_badges: [Array] } }, //... ] }, //... ] } Get continuation: const continuation = await mysubsfeed.getContinuation(); Get notifications: const notifications = await youtube.getNotifications(); Output { items: [ { title: string, sent_time: string, channel_name: string, channel_thumbnail: { url: string, width: number, height: number }, video_thumbnail: { url: string, width: number, height: number }, video_url: string, read: boolean, notification_id: string }, //... ] } Get continuation: const continuation = await notifications.getContinuation(); Get unseen notifications count: const notifications = await youtube.getUnseenNotificationsCount(); Get song lyrics: const search = await youtube.search('Never give you up', { client: 'YTMUSIC' }); const lyrics = await youtube.getLyrics(search.results.songs[0].id); Get playlist: YouTube (default): const playlist = await youtube.getPlaylist('PLAYLIST_ID'); YouTube Music: const playlist = await youtube.getPlaylist('PLAYLIST_ID', { client: 'YTMUSIC' }); YouTube Output { title: string, description: string, total_items: string, last_updated: string, views: string, items: [ { id: string, title: string, author: string, duration: { seconds: number, simple_text: string, accessibility_label: string }, thumbnails: [Array] }, //... ] } YouTube Music Output { title: string, description: string, total_items: number, duration: string, year: string, items: [ { id: string, title: string, author: string, duration: { seconds: number, simple_text: string }, thumbnails: [Array] }, //... } Interactions: --------------------------------------------------------------------- The library makes it easy to interact with YouTube programmatically. However, don't forget that you must be signed in to use the following features! * Subscribe/Unsubscribe: await youtube.interact.subscribe('CHANNEL_ID'); await youtube.interact.unsubscribe('CHANNEL_ID'); * Like/Dislike: await youtube.interact.like('VIDEO_ID'); await youtube.interact.dislike('VIDEO_ID'); await youtube.interact.removeLike('VIDEO_ID'); * Comment: await youtube.interact.comment('VIDEO_ID', 'Haha, nice video!'); * Change notification preferences: // Options: ALL | NONE | PERSONALIZED await youtube.interact.changeNotificationPreferences('CHANNEL_ID', 'ALL'); These methods will always return { success: true, status_code: 200 } if successful. Account Settings It is also possible to manage an account's settings: * Get account info: await youtube.account.info(); Output { name: string, photo: [ { url: string, width: number, height: number } ], country: string, language: string; } Notification settings: * Subscription notifications: await youtube.account.settings.notifications.setSubscriptions(true); * Recommended content notifications: await youtube.account.settings.notifications.setRecommendedVideos(true); * Channel activity notifications: await youtube.account.settings.notifications.setChannelActivity(true); * Comment replies notifications: await youtube.account.settings.notifications.setCommentReplies(true); * Channel mention notifications: await youtube.account.settings.notifications.setSharedContent(true); Privacy settings: * Subscriptions privacy: await youtube.account.settings.privacy.setSubscriptionsPrivate(true); * Saved playlists privacy: await youtube.account.settings.privacy.setSavedPlaylistsPrivate(true); Live chats: --------------------------------------------------------------------- YouTube.js isn't able to download live content yet, but it does allow you to fetch live chats plus you can also send messages! const Innertube = require('youtubei.js'); async function start() { const youtube = await new Innertube(); const search = await youtube.search('Lofi girl live'); const video = await youtube.getDetails(search.videos[0].id); const livechat = video.getLivechat(); // Updated stats about the livestream livechat.on('update-metadata', (data) => { console.info('Info:', data); }); // Fired whenever there is a new message or other chat events livechat.on('chat-update', (message) => { console.info(`- ${message.author.name}\n${message.text}\n\n`); if(message.text == '!info') { livechat.sendMessage('Hello! This message was sent from YouTube.js'); } }); } start(); Stop fetching the live chat: livechat.stop(); Delete a message: const msg = await livechat.sendMessage('Nice livestream!'); await msg.deleteMessage(); Downloading videos: --------------------------------------------------------------------- YouTube.js provides an easy-to-use and simple downloader: const fs = require('fs'); const Innertube = require('youtubei.js'); async function start() { const youtube = await new Innertube(); const search = await youtube.search('Looking for life on Mars - documentary'); const stream = youtube.download(search.videos[0].id, { format: 'mp4', // Optional, defaults to mp4 and I recommend to leave it as it is unless you know what you're doing quality: '360p', // if a video doesn't have a specific quality it'll fall back to 360p, also ignored when type is set to audio type: 'videoandaudio' // can be "video", "audio" and "videoandaudio" }); stream.pipe(fs.createWriteStream(`./${search.videos[0].title}.mp4`)); stream.on('start', () => { console.info('[DOWNLOADER]', 'Starting download now!'); }); stream.on('info', (info) => { // { video_details: {..}, selected_format: {..}, formats: {..} } console.info('[DOWNLOADER]', `Downloading ${info.video_details.title} by ${info.video_details.metadata.channel_name}`); }); stream.on('progress', (info) => { process.stdout.clearLine(); process.stdout.cursorTo(0); process.stdout.write(`[DOWNLOADER] Downloaded ${info.percentage}% (${info.downloaded_size}MB) of ${info.size}MB`); }); stream.on('end', () => { process.stdout.clearLine(); process.stdout.cursorTo(0); console.info('[DOWNLOADER]', 'Done!'); }); stream.on('error', (err) => console.error('[ERROR]', err)); } start(); You can also specify a range: const stream = youtube.download(VIDEO_ID, { //... type: 'videoandaudio', range: { start: 0, end: 1048576 * 5 } }); Cancel a download: stream.cancel(); Alternatively, you can get the deciphered streaming data and handle the download yourself: const streaming_data = await youtube.getStreamingData(search.videos[0].id, { format: 'mp4', quality: '360p', type: 'videoandaudio' }); Output { selected_format: { itag: number, mimeType: string, bitrate: number, initRange: { start: string, end: string }, indexRange: { start: string, end: string }, lastModified: string, contentLength: string, quality: string, projectionType: string, averageBitrate: number, highReplication: boolean, audioQuality: string, approxDurationMs: string, audioSampleRate: string, audioChannels: number, loudnessDb: number, url: string, has_audio: boolean, has_video: boolean }, formats: [ { itag: number, mimeType: string, bitrate: number, initRange: { start: string, end: string }, indexRange: { start: string, end: string }, lastModified: string, contentLength: string, quality: string, projectionType: string, averageBitrate: number, highReplication: boolean, audioQuality: string, approxDurationMs: string, audioSampleRate: string, audioChannels: number, loudnessDb: number, url: string, has_audio: boolean, has_video: boolean } //... ] } Signing-in: --------------------------------------------------------------------- When signing in to your account, you have two options: * Use OAuth 2.0; easy, simple & reliable. * Cookies; usually more complicated to get and unreliable. OAuth: const fs = require('fs'); const Innertube = require('youtubei.js'); const creds_path = './yt_oauth_creds.json'; async function start() { const creds = fs.existsSync(creds_path) && JSON.parse(fs.readFileSync(creds_path).toString()) || {}; const youtube = await new Innertube(); youtube.ev.on('auth', (data) => { if (data.status === 'AUTHORIZATION_PENDING') { console.info(`Hello!\nOn your phone or computer, go to ${data.verification_url} and enter the code ${data.code}`); } else if (data.status === 'SUCCESS') { fs.writeFileSync(creds_path, JSON.stringify(data.credentials)); console.info('Successfully signed-in, enjoy!'); } }); youtube.ev.on('update-credentials', (data) => { fs.writeFileSync(creds_path, JSON.stringify(data.credentials)); console.info('Credentials updated!', data); }); await youtube.signIn(creds); //... } start(); Cookies: const Innertube = require('youtubei.js'); async function start() { const youtube = await new Innertube(COOKIE_HERE); //... } start(); Contributing Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate. Contact LuanRT - @lrt_nooneknows - 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 their affiliates or subsidiaries. All trademarks, logos and brand names are the property of their respective owners. Should you have any questions or concerns please contact me directly via email. License Distributed under the MIT License. (back to top) About full-featured wrapper around YouTube's private API www.npmjs.com/package/youtubei.js Topics youtube like youtube-dl comment youtube-downloader dislike private-api youtube-comments innertube like-dislike-system innertubeapi Resources Readme License MIT License Stars 425 stars Watchers 7 watching Forks 27 forks Releases 17 v1.4.0 Latest Apr 13, 2022 + 16 releases Sponsor this project * ko_fi ko-fi.com/luanrt Packages 0 No packages published Used by 44 * @baztroXdd * @RyannKim327 * @kevinoctavian * @Eximinati * @syahrilzzzu * @syahrilzzzu * @ai-man-123 * @Gimenz + 36 Contributors 5 * @LuanRT * @dependabot[bot] * @stranothus * @fossabot * @UnbreakCode Languages * JavaScript 100.0% * (c) 2022 GitHub, Inc. * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time. You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.