Uploads a video to YouTube and optionally sets the video's metadata.

This method supports media upload. Uploaded files must conform to these constraints:

- **Maximum file size:** 256GB
- **Accepted Media MIME types:** `video/*`, `application/octet-stream`

**Quota impact:** 100 calls per day. A call to this method has a [quota cost](https://developers.google.com/youtube/data/getting-started.md.txt#quota) of 1 unit in the Video Uploads quota bucket.

## Common use cases

<iframe src="https://developers.devsite.google/frame/youtube/data/reference/rest/v3/videos/insert_9bafc243df7671d3b827dcb85b513aaf1a67154ebf05be861c99c2cdc5309e19.frame" class="framebox inherit-locale " allow="clipboard-write https://developers.devsite.google" allowfullscreen is-upgraded></iframe>

## Request

### HTTP request

```
POST https://www.googleapis.com/upload/youtube/v3/videos
```

### Authorization

This request requires authorization with at least one of the following scopes ([read more about authentication and authorization](https://developers.google.com/youtube/data/guides/authentication.md.txt)).

| Scope |
|---|
| `https://www.googleapis.com/auth/youtube.upload` |
| `https://www.googleapis.com/auth/youtube` |
| `https://www.googleapis.com/auth/youtubepartner` |
| `https://www.googleapis.com/auth/youtube.force-ssl` |

### Parameters

The following table lists the parameters that this query supports. All of the parameters listed are query parameters.

| Parameters ||
|---|---|---|
| **Required parameters** |||
| `part` | `string` The `part` parameter serves two purposes in this operation. It identifies the properties that the write operation will set as well as the properties that the API response will include. Note that not all parts contain properties that can be set when inserting or updating a video. For example, the `statistics` object encapsulates statistics that YouTube calculates for a video and does not contain values that you can set or modify. If the parameter value specifies a `part` that does not contain mutable values, that `part` will still be included in the API response. The following list contains the `part` names that you can include in the parameter value: - `brandPartner` - `contentDetails` - `fileDetails` - `id` - `liveStreamingDetails` - `localizations` - `paidProductPlacementDetails` - `player` - `processingDetails` - `recordingDetails` - `snippet` - `statistics` - `status` - `suggestions` - `topicDetails` |
| **Optional parameters** |||
| `notifySubscribers` | `boolean` The `notifySubscribers` parameter indicates whether YouTube should send a notification about the new video to users who subscribe to the video's channel. A parameter value of `True` indicates that subscribers will be notified of newly uploaded videos. However, a channel owner who is uploading many videos might prefer to set the value to `False` to avoid sending a notification about each new video to the channel's subscribers. The default value is `True`. |
| `onBehalfOfContentOwner` | `string` This parameter can only be used in a properly [authorized request](https://developers.google.com/youtube/data/guides/authentication.md.txt). **Note:** This parameter is intended exclusively for YouTube content partners. The `onBehalfOfContentOwner` parameter indicates that the request's authorization credentials identify a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value. This parameter is intended for YouTube content partners that own and manage many different YouTube channels. It allows content owners to authenticate once and get access to all their video and channel data, without having to provide authentication credentials for each individual channel. The CMS account that the user authenticates with must be linked to the specified YouTube content owner. |
| `onBehalfOfContentOwnerChannel` | `string` This parameter can only be used in a properly [authorized request](https://developers.google.com/youtube/data/guides/authentication.md.txt). This parameter can only be used in a properly [authorized request](https://developers.google.com/youtube/data/guides/authentication.md.txt). **Note:** This parameter is intended exclusively for YouTube content partners. The `onBehalfOfContentOwnerChannel` parameter specifies the YouTube channel ID of the channel to which a video is being added. This parameter is required when a request specifies a value for the `onBehalfOfContentOwner` parameter, and it can only be used in conjunction with that parameter. In addition, the request must be authorized using a CMS account that is linked to the content owner that the `onBehalfOfContentOwner` parameter specifies. Finally, the channel that the `onBehalfOfContentOwnerChannel` parameter value specifies must be linked to the content owner that the `onBehalfOfContentOwner` parameter specifies. This parameter is intended for YouTube content partners that own and manage many different YouTube channels. It allows content owners to authenticate once and perform actions on behalf of the channel specified in the parameter value, without having to provide authentication credentials for each separate channel. |

### Request body

Provide a [video resource](https://developers.google.com/youtube/data/reference/rest/v3/videos.md.txt#resource) in the request body.
For that resource:

- You can set values for these properties:

  - `snippet.title`
  - `snippet.description`
  - `snippet.tags[]`
  - `snippet.categoryId`
  - `snippet.defaultLanguage`
  - `localizations.(key)`
  - `localizations.(key).title`
  - `localizations.(key).description`
  - `status.embeddable`
  - `status.license`
  - `status.privacyStatus`: If you don't specify a value for this property, the uploaded video's privacy status defaults to `public`. Certain uploads might default to `private` based on creator account settings, as explained in the [YouTube Help Center](https://support.google.com/youtube/answer/157177). Videos uploaded from unverified API projects are not restricted to private viewing mode.
  - `status.publicStatsViewable`
  - `status.publishAt`
  - `status.selfDeclaredMadeForKids`
  - `status.containsSyntheticMedia`
  - `recordingDetails.recordingDate`

## Response

If successful, this method returns a [video resource](https://developers.google.com/youtube/data/reference/rest/v3/videos.md.txt#resource) in the response body.

## Examples

**Note:** The following code samples may not represent all supported programming languages. See the [client libraries](https://developers.google.com/youtube/data/libraries.md.txt) documentation for a list of supported languages.

### Go

This code sample calls the API's `videos.insert` method to upload a video to the channel associated with the request.

This example uses the [Go client library](https://github.com/google/google-api-go-client).

```go
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"

	"google.golang.org/api/youtube/v3"
)

var (
	filename    = flag.String("filename", "", "Name of video file to upload")
	title       = flag.String("title", "Test Title", "Video title")
	description = flag.String("description", "Test Description", "Video description")
	category    = flag.String("category", "22", "Video category")
	keywords    = flag.String("keywords", "", "Comma separated list of video keywords")
	privacy     = flag.String("privacy", "unlisted", "Video privacy status")
)

func main() {
	flag.Parse()

	if *filename == "" {
		log.Fatalf("You must provide a filename of a video file to upload")
	}

	client := getClient(youtube.YoutubeUploadScope)

	service, err := youtube.New(client)
	if err != nil {
		log.Fatalf("Error creating YouTube client: %v", err)
	}

	upload := &youtube.Video{
		Snippet: &youtube.VideoSnippet{
			Title:       *title,
			Description: *description,
			CategoryId:  *category,
		},
		Status: &youtube.VideoStatus{PrivacyStatus: *privacy},
	}

	// The API returns a 400 Bad Request response if tags is an empty string.
	if strings.Trim(*keywords, "") != "" {
		upload.Snippet.Tags = strings.Split(*keywords, ",")
	}

	call := service.Videos.Insert("snippet,status", upload)

	file, err := os.Open(*filename)
	defer file.Close()
	if err != nil {
		log.Fatalf("Error opening %v: %v", *filename, err)
	}

	response, err := call.Media(file).Do()
	handleError(err, "")
	fmt.Printf("Upload successful! Video ID: %v\n", response.Id)
}
```

### .NET

The following code sample calls the API's `videos.insert` method to upload a video to the channel associated with the request.

This example uses the [.NET client library](https://developers.google.com/api-client-library/dotnet).

```
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;

namespace Google.Apis.YouTube.Samples
{
  /// <summary>
  /// YouTube Data API v3 sample: upload a video.
  /// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
  /// See https://developers.google.com/api-client-library/dotnet/get_started
  /// </summary>
  internal class UploadVideo
  {
    [STAThread]
    static void Main(string[] args)
    {
      Console.WriteLine("YouTube Data API: Upload Video");
      Console.WriteLine("==============================");

      try
      {
        new UploadVideo().Run().Wait();
      }
      catch (AggregateException ex)
      {
        foreach (var e in ex.InnerExceptions)
        {
          Console.WriteLine("Error: " + e.Message);
        }
      }

      Console.WriteLine("Press any key to continue...");
      Console.ReadKey();
    }

    private async Task Run()
    {
      UserCredential credential;
      using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
      {
        credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
            GoogleClientSecrets.Load(stream).Secrets,
            // This OAuth 2.0 access scope allows an application to upload files to the
            // authenticated user's YouTube channel, but doesn't allow other types of access.
            new[] { YouTubeService.Scope.YoutubeUpload },
            "user",
            CancellationToken.None
        );
      }

      var youtubeService = new YouTubeService(new BaseClientService.Initializer()
      {
        HttpClientInitializer = credential,
        ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
      });

      var video = new Video();
      video.Snippet = new VideoSnippet();
      video.Snippet.Title = "Default Video Title";
      video.Snippet.Description = "Default Video Description";
      video.Snippet.Tags = new string[] { "tag1", "tag2" };
      video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
      video.Status = new VideoStatus();
      video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
      var filePath = @"REPLACE_ME.mp4"; // Replace with path to actual movie file.

      using (var fileStream = new FileStream(filePath, FileMode.Open))
      {
        var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
        videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
        videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;

        await videosInsertRequest.UploadAsync();
      }
    }

    void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
    {
      switch (progress.Status)
      {
        case UploadStatus.Uploading:
          Console.WriteLine("{0} bytes sent.", progress.BytesSent);
          break;

        case UploadStatus.Failed:
          Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
          break;
      }
    }

    void videosInsertRequest_ResponseReceived(Video video)
    {
      Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
    }
  }
}
```

### Ruby

This sample calls the API's `videos.insert` method to upload a video to the channel associated with the request.

This example uses the [Ruby client library](https://developers.google.com/api-client-library/ruby).

```ruby
#!/usr/bin/ruby

require 'rubygems'
gem 'google-api-client', '>0.7'
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/file_storage'
require 'google/api_client/auth/installed_app'
require 'trollop'

# A limited OAuth 2 access scope that allows for uploading files, but not other
# types of account access.
YOUTUBE_UPLOAD_SCOPE = 'https://www.googleapis.com/auth/youtube.upload'
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'

def get_authenticated_service
  client = Google::APIClient.new(
    :application_name => $PROGRAM_NAME,
    :application_version => '1.0.0'
  )
  youtube = client.discovered_api(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION)

  file_storage = Google::APIClient::FileStorage.new("#{$PROGRAM_NAME}-oauth2.json")
  if file_storage.authorization.nil?
    client_secrets = Google::APIClient::ClientSecrets.load
    flow = Google::APIClient::InstalledAppFlow.new(
      :client_id => client_secrets.client_id,
      :client_secret => client_secrets.client_secret,
      :scope => [YOUTUBE_UPLOAD_SCOPE]
    )
    client.authorization = flow.authorize(file_storage)
  else
    client.authorization = file_storage.authorization
  end

  return client, youtube
end

def main
  opts = Trollop::options do
    opt :file, 'Video file to upload', :type => String
    opt :title, 'Video title', :default => 'Test Title', :type => String
    opt :description, 'Video description',
          :default => 'Test Description', :type => String
    opt :category_id, 'Numeric video category. See https://developers.google.com/youtube/v3/docs/videoCategories/list',
          :default => 22, :type => :int
    opt :keywords, 'Video keywords, comma-separated',
          :default => '', :type => String
    opt :privacy_status, 'Video privacy status: public, private, or unlisted',
          :default => 'public', :type => String
  end

  if opts[:file].nil? or not File.file?(opts[:file])
    Trollop::die :file, 'does not exist'
  end

  client, youtube = get_authenticated_service

  begin
    body = {
      :snippet => {
        :title => opts[:title],
        :description => opts[:description],
        :tags => opts[:keywords].split(','),
        :categoryId => opts[:category_id],
      },
      :status => {
        :privacyStatus => opts[:privacy_status]
      }
    }

    videos_insert_response = client.execute!(
      :api_method => youtube.videos.insert,
      :body_object => body,
      :media => Google::APIClient::UploadIO.new(opts[:file], 'video/*'),
      :parameters => {
        :uploadType => 'resumable',
        :part => body.keys.join(',')
      }
    )

    videos_insert_response.resumable_upload.send_all(client)

    puts "Video id '#{videos_insert_response.data.id}' was successfully uploaded."
  rescue Google::APIClient::TransmissionError => e
    puts e.result.body
  end
end

main
```

## Errors

The following table identifies error messages that the API could return in response to a call to this method. Please see the [error message](https://developers.google.com/youtube/data/reference/rest/v3/errors.md.txt) documentation for more detail.

| Error type | Error detail | Description |
|---|---|---|
| `badRequest (400)` | `defaultLanguageNotSet` | The request is trying to add localized video details without specifying the default language of the video details. |
| `badRequest (400)` | `invalidCategoryId` | The `snippet.categoryId` property specifies an invalid category ID. Use the `videoCategories.list` method to retrieve supported categories. |
| `badRequest (400)` | `invalidDescription` | The request metadata specifies an invalid video description. |
| `badRequest (400)` | `invalidFilename` | The video filename specified in the `Slug` header is invalid. |
| `badRequest (400)` | `invalidPublishAt` | The request metadata specifies an invalid scheduled publishing time. |
| `badRequest (400)` | `invalidRecordingDetails` | The `recordingDetails` object in the request metadata specifies invalid recording details. |
| `badRequest (400)` | `invalidTags` | The request metadata specifies invalid video keywords. |
| `badRequest (400)` | `invalidTitle` | The request metadata specifies an invalid or empty video title. |
| `badRequest (400)` | `invalidVideoGameRating` | The request metadata specifies an invalid video game rating. |
| `badRequest (400)` | `invalidVideoMetadata` | The request metadata is invalid. |
| `badRequest (400)` | `mediaBodyRequired` | The request does not include the video content. |
| `badRequest (400)` | `uploadLimitExceeded` | The user has exceeded the number of videos they may upload. |
| `forbidden (403)` | `forbidden` |   |
| `forbidden (403)` | `forbiddenLicenseSetting` | The request attempts to set an invalid license for the video. |
| `forbidden (403)` | `forbiddenPrivacySetting` | The request attempts to set an invalid privacy setting for the video. |

## Try it!

Use the APIs Explorer to call this API and see the API request and response.