This example is taken directly from the official YouTube Data API documentation, and shows how to perform the following functions:

  1. It retrieves the channel name and thumbnail of the authenticated user's channel using the API's channels.list method.
  2. It handles the video upload to YouTube using the resumable upload protocol.
  3. It polls for the uploaded video's upload and processing status using the API's videos.list method by setting the part parameter value to status.

Using the Javascript Client API:

/*
Copyright 2015 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var signinCallback = function (result){
  if(result.access_token) {
    var uploadVideo = new UploadVideo();
    uploadVideo.ready(result.access_token);
  }
};

var STATUS_POLLING_INTERVAL_MILLIS = 60 * 1000; // One minute.


/**
 * YouTube video uploader class
 *
 * @constructor
 */
var UploadVideo = function() {
  /**
   * The array of tags for the new YouTube video.
   *
   * @attribute tags
   * @type Array.<string>
   * @default ['google-cors-upload']
   */
  this.tags = ['youtube-cors-upload'];

  /**
   * The numeric YouTube
   * category id.
   *
   * @attribute categoryId
   * @type number
   * @default 22
   */
  this.categoryId = 22;

  /**
   * The id of the new video.
   *
   * @attribute videoId
   * @type string
   * @default ''
   */
  this.videoId = '';

  this.uploadStartTime = 0;
};


UploadVideo.prototype.ready = function(accessToken) {
  this.accessToken = accessToken;
  this.gapi = gapi;
  this.authenticated = true;
  this.gapi.client.request({
    path: '/youtube/v3/channels',
    params: {
      part: 'snippet',
      mine: true
    },
    callback: function(response) {
      if (response.error) {
        console.log(response.error.message);
      } else {
        $('#channel-name').text(response.items[0].snippet.title);
        $('#channel-thumbnail').attr('src', response.items[0].snippet.thumbnails.default.url);

        $('.pre-sign-in').hide();
        $('.post-sign-in').show();
      }
    }.bind(this)
  });
  $('#button').on("click", this.handleUploadClicked.bind(this));
};

/**
 * Uploads a video file to YouTube.
 *
 * @method uploadFile
 * @param {object} file File object corresponding to the video to upload.
 */
UploadVideo.prototype.uploadFile = function(file) {
  var metadata = {
    snippet: {
      title: $('#title').val(),
      description: $('#description').text(),
      tags: this.tags,
      categoryId: this.categoryId
    },
    status: {
      privacyStatus: $('#privacy-status option:selected').text()
    }
  };
  var uploader = new MediaUploader({
    baseUrl: 'https://www.googleapis.com/upload/youtube/v3/videos',
    file: file,
    token: this.accessToken,
    metadata: metadata,
    params: {
      part: Object.keys(metadata).join(',')
    },
    onError: function(data) {
      var message = data;
      // Assuming the error is raised by the YouTube API, data will be
      // a JSON string with error.message set. That may not be the
      // only time onError will be raised, though.
      try {
        var errorResponse = JSON.parse(data);
        message = errorResponse.error.message;
      } finally {
        alert(message);
      }
    }.bind(this),
    onProgress: function(data) {
      var currentTime = Date.now();
      var bytesUploaded = data.loaded;
      var totalBytes = data.total;
      // The times are in millis, so we need to divide by 1000 to get seconds.
      var bytesPerSecond = bytesUploaded / ((currentTime - this.uploadStartTime) / 1000);
      var estimatedSecondsRemaining = (totalBytes - bytesUploaded) / bytesPerSecond;
      var percentageComplete = (bytesUploaded * 100) / totalBytes;

      $('#upload-progress').attr({
        value: bytesUploaded,
        max: totalBytes
      });

      $('#percent-transferred').text(percentageComplete);
      $('#bytes-transferred').text(bytesUploaded);
      $('#total-bytes').text(totalBytes);

      $('.during-upload').show();
    }.bind(this),
    onComplete: function(data) {
      var uploadResponse = JSON.parse(data);
      this.videoId = uploadResponse.id;
      $('#video-id').text(this.videoId);
      $('.post-upload').show();
      this.pollForVideoStatus();
    }.bind(this)
  });
  // This won't correspond to the *exact* start of the upload, but it should be close enough.
  this.uploadStartTime = Date.now();
  uploader.upload();
};

UploadVideo.prototype.handleUploadClicked = function() {
  $('#button').attr('disabled', true);
  this.uploadFile($('#file').get(0).files[0]);
};

UploadVideo.prototype.pollForVideoStatus = function() {
  this.gapi.client.request({
    path: '/youtube/v3/videos',
    params: {
      part: 'status,player',
      id: this.videoId
    },
    callback: function(response) {
      if (response.error) {
        // The status polling failed.
        console.log(response.error.message);
        setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_INTERVAL_MILLIS);
      } else {
        var uploadStatus = response.items[0].status.uploadStatus;
        switch (uploadStatus) {
          // This is a non-final status, so we need to poll again.
          case 'uploaded':
            $('#post-upload-status').append('<li>Upload status: ' + uploadStatus + '</li>');
            setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_INTERVAL_MILLIS);
            break;
          // The video was successfully transcoded and is available.
          case 'processed':
            $('#player').append(response.items[0].player.embedHtml);
            $('#post-upload-status').append('<li>Final status.</li>');
            break;
          // All other statuses indicate a permanent transcoding failure.
          default:
            $('#post-upload-status').append('<li>Transcoding failed.</li>');
            break;
        }
      }
    }.bind(this)
  });
};

The associated HTML, CSS and JavaScript file (defining the MediaUploader class).

A complete Fiddle can be found here. Follow these steps to create a project and replace YOUR_CLIENT_ID_HERE in the Fiddle with the corresponding client id from the project.

Regarding AngularJS, take a look at this question to help you get started building a service for Angular.

Answer from Renzo Poddighe on Stack Overflow
🌐
GitHub
github.com › youtube › api-samples › blob › master › javascript › upload_video.html
api-samples/javascript/upload_video.html at master · youtube/api-samples
data-scope="https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/youtube"> ... <p><span id="percent-transferred"></span>% done (<span id="bytes-transferred"></span>/<span id="total-bytes"></span> bytes)</p> ...
Author: youtube
Top answer
1 of 1
14

This example is taken directly from the official YouTube Data API documentation, and shows how to perform the following functions:

  1. It retrieves the channel name and thumbnail of the authenticated user's channel using the API's channels.list method.
  2. It handles the video upload to YouTube using the resumable upload protocol.
  3. It polls for the uploaded video's upload and processing status using the API's videos.list method by setting the part parameter value to status.

Using the Javascript Client API:

/*
Copyright 2015 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var signinCallback = function (result){
  if(result.access_token) {
    var uploadVideo = new UploadVideo();
    uploadVideo.ready(result.access_token);
  }
};

var STATUS_POLLING_INTERVAL_MILLIS = 60 * 1000; // One minute.


/**
 * YouTube video uploader class
 *
 * @constructor
 */
var UploadVideo = function() {
  /**
   * The array of tags for the new YouTube video.
   *
   * @attribute tags
   * @type Array.<string>
   * @default ['google-cors-upload']
   */
  this.tags = ['youtube-cors-upload'];

  /**
   * The numeric YouTube
   * category id.
   *
   * @attribute categoryId
   * @type number
   * @default 22
   */
  this.categoryId = 22;

  /**
   * The id of the new video.
   *
   * @attribute videoId
   * @type string
   * @default ''
   */
  this.videoId = '';

  this.uploadStartTime = 0;
};


UploadVideo.prototype.ready = function(accessToken) {
  this.accessToken = accessToken;
  this.gapi = gapi;
  this.authenticated = true;
  this.gapi.client.request({
    path: '/youtube/v3/channels',
    params: {
      part: 'snippet',
      mine: true
    },
    callback: function(response) {
      if (response.error) {
        console.log(response.error.message);
      } else {
        $('#channel-name').text(response.items[0].snippet.title);
        $('#channel-thumbnail').attr('src', response.items[0].snippet.thumbnails.default.url);

        $('.pre-sign-in').hide();
        $('.post-sign-in').show();
      }
    }.bind(this)
  });
  $('#button').on("click", this.handleUploadClicked.bind(this));
};

/**
 * Uploads a video file to YouTube.
 *
 * @method uploadFile
 * @param {object} file File object corresponding to the video to upload.
 */
UploadVideo.prototype.uploadFile = function(file) {
  var metadata = {
    snippet: {
      title: $('#title').val(),
      description: $('#description').text(),
      tags: this.tags,
      categoryId: this.categoryId
    },
    status: {
      privacyStatus: $('#privacy-status option:selected').text()
    }
  };
  var uploader = new MediaUploader({
    baseUrl: 'https://www.googleapis.com/upload/youtube/v3/videos',
    file: file,
    token: this.accessToken,
    metadata: metadata,
    params: {
      part: Object.keys(metadata).join(',')
    },
    onError: function(data) {
      var message = data;
      // Assuming the error is raised by the YouTube API, data will be
      // a JSON string with error.message set. That may not be the
      // only time onError will be raised, though.
      try {
        var errorResponse = JSON.parse(data);
        message = errorResponse.error.message;
      } finally {
        alert(message);
      }
    }.bind(this),
    onProgress: function(data) {
      var currentTime = Date.now();
      var bytesUploaded = data.loaded;
      var totalBytes = data.total;
      // The times are in millis, so we need to divide by 1000 to get seconds.
      var bytesPerSecond = bytesUploaded / ((currentTime - this.uploadStartTime) / 1000);
      var estimatedSecondsRemaining = (totalBytes - bytesUploaded) / bytesPerSecond;
      var percentageComplete = (bytesUploaded * 100) / totalBytes;

      $('#upload-progress').attr({
        value: bytesUploaded,
        max: totalBytes
      });

      $('#percent-transferred').text(percentageComplete);
      $('#bytes-transferred').text(bytesUploaded);
      $('#total-bytes').text(totalBytes);

      $('.during-upload').show();
    }.bind(this),
    onComplete: function(data) {
      var uploadResponse = JSON.parse(data);
      this.videoId = uploadResponse.id;
      $('#video-id').text(this.videoId);
      $('.post-upload').show();
      this.pollForVideoStatus();
    }.bind(this)
  });
  // This won't correspond to the *exact* start of the upload, but it should be close enough.
  this.uploadStartTime = Date.now();
  uploader.upload();
};

UploadVideo.prototype.handleUploadClicked = function() {
  $('#button').attr('disabled', true);
  this.uploadFile($('#file').get(0).files[0]);
};

UploadVideo.prototype.pollForVideoStatus = function() {
  this.gapi.client.request({
    path: '/youtube/v3/videos',
    params: {
      part: 'status,player',
      id: this.videoId
    },
    callback: function(response) {
      if (response.error) {
        // The status polling failed.
        console.log(response.error.message);
        setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_INTERVAL_MILLIS);
      } else {
        var uploadStatus = response.items[0].status.uploadStatus;
        switch (uploadStatus) {
          // This is a non-final status, so we need to poll again.
          case 'uploaded':
            $('#post-upload-status').append('<li>Upload status: ' + uploadStatus + '</li>');
            setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_INTERVAL_MILLIS);
            break;
          // The video was successfully transcoded and is available.
          case 'processed':
            $('#player').append(response.items[0].player.embedHtml);
            $('#post-upload-status').append('<li>Final status.</li>');
            break;
          // All other statuses indicate a permanent transcoding failure.
          default:
            $('#post-upload-status').append('<li>Transcoding failed.</li>');
            break;
        }
      }
    }.bind(this)
  });
};

The associated HTML, CSS and JavaScript file (defining the MediaUploader class).

A complete Fiddle can be found here. Follow these steps to create a project and replace YOUR_CLIENT_ID_HERE in the Fiddle with the corresponding client id from the project.

Regarding AngularJS, take a look at this question to help you get started building a service for Angular.

Discussions

node-apiless-youtube-upload :: Easily upload videos to Youtube using Node.js/Javascript!
package is gone :( More on reddit.com
🌐 r/node
1
0
January 13, 2021
javascript - Youtube data api - uploading video's to one channel - Stack Overflow
I'm assigned to create a node js app which is given to a few trusted people who need to submit videos. The budget is really small and they don't have the money for renting cloud storage. Now I was More on stackoverflow.com
🌐 stackoverflow.com
February 12, 2017
Uploading a YouTube Video Using YouTube Data API V3 using Google API Client JavaScript - Stack Overflow
I visited the Youtube Data v3 API docs to know about how to upload videos using the client library I got to this page https://developers.google.com/youtube/v3/docs/videos/insert but it doesn't seem... More on stackoverflow.com
🌐 stackoverflow.com
Problem with uploading video to youtube by Youtube Data API V3
for people who may have the same problem like me i did't do anything and it fixed by it self but what may cause the problem to fix IMO is: i waited after i finished the project. immediately after finishing project i started uploading but all the videos get restricted. so after a few weeks (right now) i tried again and it works. so you may need to wait for a certain time. even in the cloud.google.com where you create credentials for your API, it says it take time to verify that(i missed that, don't know how). i used another VPN to upload my videos i don't think reason number 2 is the right one but i mention it just in case. i hope this help people who have the same problem. More on reddit.com
🌐 r/learnpython
10
2
February 11, 2024
🌐
Byu
copyright-certificate.byu.edu › news › youtube-api-upload-videos-with-1761209978818
YouTube API: Upload Videos With JavaScript - Easy Guide
October 23, 2025 - Now for the main course: implementing the JavaScript code to upload videos. We'll break this down into manageable chunks to make it easier to digest. First, you'll need to load the Google API client library.
🌐
Educative
educative.io › home › courses › integration with youtube data api in javascript › upload videos
Uploading and Setting Thumbnails on YouTube Videos with API
Integration with YouTube Data API in JavaScript · Explore how to upload videos to your YouTube channel using the videos resource insert method and how to set custom thumbnails via the thumbnails set method. Understand the request parameters and response fields involved in these operations to effectively manage your video content with the YouTube Data API.
🌐
GitHub
gist.github.com › soygul › 42677432fa89df7fd783e0232a43a8cf
YouTube video uploader using JavaScript and Node.js · GitHub
Uploading the thumbnail now.') service.thumbnails.set({ auth: auth, videoId: response.data.id, media: { body: fs.createReadStream(thumbFilePath) }, }, function (err, response) { if (err) { console.log('The API returned an error: ' + err); return; } console.log(response.data) }) });
🌐
Quanticdev
quanticdev.com › articles › automating-my-youtube-uploads-using-nodejs
Automating My YouTube Uploads Using Node.js
Upload my videos to YouTube programmatically using YouTube API in Node.js. All the video metadata needs to be parsed from the video’s script, which is written in Microsoft Word. ... Read and parse the video metadata from the video’s script, which is a Word document.
🌐
GitHub
github.com › paulzmuda › youtube-react-upload
GitHub - paulzmuda/youtube-react-upload: Youtube API v3 - Javascript Client Upload · GitHub
Youtube API v3 - Javascript Client Upload. Contribute to paulzmuda/youtube-react-upload development by creating an account on GitHub.
Author: paulzmuda
Find elsewhere
🌐
DEV Community
dev.to › emmykolic › how-to-build-a-youtube-api-connection-project-using-vanilla-javascript-31
How To Build A YouTube API Connection (Project) Using Vanilla JavaScript - DEV Community
November 25, 2024 - So Note that the YouTube Data API is used to retrieve video data and present it on a webpage when building a YouTube API connection project using vanilla JavaScript.
🌐
YouTube
youtube.com › watch
Node.js Express Youtube Data API V3 Upload Video to Youtube in Javascript Full Project For Beginners - YouTube
Buy the full source code of application here:https://procodestore.com/index.php/product/node-js-express-youtube-data-api-v3-upload-video-to-youtube-in-javasc...
Published: July 11, 2020
🌐
Codementor
codementor.io › community › uploading videos to youtube using nodejs
Uploading Videos to YouTube Using NodeJS | Codementor
April 12, 2017 - For that, click the YouTube Data API link and then click the Enable API button. Before getting the credentials, you have to set the Consent screen information (the product name is mandatory): Now it's the time to get the application credentials.
🌐
YouTube
youtube.com › watch
Automating My YouTube Uploads Using Node.js - YouTube
In this video, you will learn how I automated my YouTube uploads using Node.js. I've used the YouTube API through Google's readily available Node.js client l...
Published: November 24, 2020
🌐
Apify
apify.com › dami_studio › youtube-video-uploader › api › javascript
YouTube Video Uploader - Upload & Schedule, No Code API in JavaScript · Apify
June 10, 2026 - The Apify API client for JavaScript is the official library that allows you to use YouTube Video Uploader - Auto-Publish From Any URL API in JavaScript or TypeScript, providing convenience functions and automatic retries on errors.
Rating: 5 ​ - ​ 1 votes
🌐
Postproxy
postproxy.dev › home › blog › youtube upload api: videos & shorts (2026)
YouTube Upload API: Videos & Shorts (2026) | Postproxy
March 12, 2026 - Upload videos and Shorts to YouTube via API: OAuth setup, resumable uploads, quota limits, and code examples in Python and Node.js.
🌐
npm
npmjs.com › package › youtube-api
youtube-api - npm
February 14, 2025 - # Using npm npm install --save youtube-api # Using yarn yarn add youtube-api · /** * This script uploads a video (specifically `video.mp4` from the current * directory) to YouTube, * * To run this script you have to create OAuth2 credentials and download them * as JSON and replace the `credentials.json` file.
      » npm install youtube-api
    
Published: Feb 14, 2025
Version: 3.0.2
🌐
Reddit
reddit.com › r/node › node-apiless-youtube-upload :: easily upload videos to youtube using node.js/javascript!
r/node on Reddit: node-apiless-youtube-upload :: Easily upload videos to Youtube using Node.js/Javascript!
January 13, 2021 -

SUP! I've been coding a selenium-based youtube uploader for node.js, and now that it finally supports monetization, I wanted to share it with you! This is the kind of a package that's extremely simple to use. It's something I wish I had found when I was working on my youtube project, so hopefully this can now help you!

https://www.npmjs.com/package/node-apiless-youtube-upload

Why not just use Youtube APIv3? Well, in 2021 Youtube only supports large companies, not individual developers or private scripters. They made it impossible to use their insert()-endpoint (for uploading) if you haven't "verified" your app.

Thanks for reading. Also, it's my first package, so if you have ideas on how to make it more compact or better for npm, let me know! Thanks

🌐
Ayrshare
ayrshare.com › home › the share › ayrshare’s youtube api to upload videos
Ayrshare's YouTube API to Upload Videos · Ayrshare
July 1, 2026 - The video can be directly uploaded to YouTube either with our API or Dashboard. And we’ve made it is simple as can be. Check out our YouTube API docs for more information and examples. A JavaScript (Node.js) example is shown below.
Top answer
1 of 1
3

In google-api-nodejs-client repo, you have an example of youtube video upload code here with the sample doc here

It uses Youtube insert API used to upload a video to YouTube and sets video's metadata

Here is a minimal functional example (the authorization code need to be set manually in this example) :

var google = require('googleapis');
var util = require('util');
var fs = require('fs');
var http = require('http');
var spawn = require('child_process').spawn;
var querystring = require('querystring');
var url = require('url');

var OAuth2 = google.auth.OAuth2;

var FILENAME = '/home/user/test.mp4';

// get these from https://console.developers.google.com/apis/credentials (don't forget to activate Youtube API)
var YOUR_CLIENT_ID = "XXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com";
var YOUR_CLIENT_SECRET = "XXXXX-XXXXXXXXXXXXXXXXXX";
var YOUR_REDIRECT_URL = "https://your-domain/redirect";

var oauth2Client = new OAuth2(
    YOUR_CLIENT_ID,
    YOUR_CLIENT_SECRET,
    YOUR_REDIRECT_URL
);

// initialize the Youtube API library
var youtube = google.youtube({
    version: 'v3',
    auth: oauth2Client
});

// very basic example of uploading a video to youtube
function uploadVideo() {

    var req = youtube.videos.insert({
        part: 'id,snippet,status',
        notifySubscribers: false,
        resource: {
            snippet: {
                title: 'Node.js YouTube Upload Test',
                description: 'Testing YouTube upload via Google APIs Node.js Client'
            },
            status: {
                privacyStatus: 'private'
            }
        },
        media: {
            body: fs.createReadStream(FILENAME)
        }
    }, function(err, data) {
        if (err) {
            console.error('Error: ' + err);
        }
        if (data) {
            console.log(util.inspect(data, false, null));
        }
        process.exit();
    });

    var fileSize = fs.statSync(FILENAME).size;

    // show some progress
    var id = setInterval(function() {
        var uploadedBytes = req.req.connection._bytesDispatched;
        var uploadedMBytes = uploadedBytes / 1000000;
        var progress = uploadedBytes > fileSize ? 100 : (uploadedBytes / fileSize) * 100;
        process.stdout.clearLine();
        process.stdout.cursorTo(0);
        process.stdout.write(uploadedMBytes.toFixed(2) + ' MBs uploaded. ' +
            progress.toFixed(2) + '% completed.');
        if (progress === 100) {
            process.stdout.write('\nDone uploading, waiting for response...\n');
            clearInterval(id);
        }
    }, 250);
}

var scopes = [
    'https://www.googleapis.com/auth/youtube.upload',
    'https://www.googleapis.com/auth/youtube'
];

var authorizeUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: scopes.join(' ')
});

console.log("go to " + authorizeUrl + " to get authorization code");

var AUTHORIZATION_CODE = "X/XXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXX";

oauth2Client.getToken(AUTHORIZATION_CODE, function(err, tokens) {
    // Now tokens contains an access_token and an optional refresh_token. Save them.
    if (!err) {
        oauth2Client.setCredentials(tokens);
        uploadVideo();
    } else {
        console.log(err);
    }
});

If the user get the authorization code with the right channel, the video will be uploaded to that same channel

🌐
GitHub
github.com › h2non › youtube-video-api
GitHub - h2non/youtube-video-api: Simplified programmatic and command-line interface for YouTube Video API. Built for node.js · GitHub
Straightforward node.js/io.js programmatic and command-line interface to easily upload, list, update, rate, thumbnail and delete videos from YouTube using OAuth2 and Google API v3.
🌐
Stack Overflow
stackoverflow.com › questions › 70728086 › uploading-a-youtube-video-using-youtube-data-api-v3-using-google-api-client-java
Uploading a YouTube Video Using YouTube Data API V3 using Google API Client JavaScript - Stack Overflow
so can you provide a working example of uploading a video using the client library that is fetched from the fetch API and show to structure the request body when using the client library? ... Save this answer. ... Show activity on this post. This is the request body I'm using. Code below was taken from this sample script: https://quanticdev.com/articles/automating-my-youtube-uploads-using-nodejs/
🌐
YouTube
youtube.com › watch
Upload Video On YouTube Channel Using YouTube API - YouTube
Everybody knows about YouTube. YouTube is the number one video-sharing website in the world. We can use YouTube to host our videos. This saves us a lot of se...
Published: April 19, 2020