Upload videos

The YouTubeVideoUploadService lets you upload videos directly to YouTube through the Google Ads API. These videos can then be used as assets in various ad types, such as Performance Max campaigns or Demand Gen campaigns.

This service streamlines the workflow of creating video ads by handling the YouTube upload process, ensuring that the videos are correctly associated with your account.

Key concepts

Before you begin, it's important to understand how video uploads are managed and the different states they can go through.

Channel ownership

When you upload a video, you can specify the destination YouTube channel using the channel_id field in the YouTubeVideoUpload resource:

  • Advertiser-owned (brand) channel: Provide the channel_id of a YouTube channel owned by the advertiser. Uploading to a brand channel allows for more control over the video's privacy and visibility.
  • Google-managed channel: If channel_id is omitted, the video is uploaded to a Google-managed YouTube channel associated with the Google Ads account.

Upload states

The lifecycle of a YouTube video upload is tracked by the state field. The YouTubeVideoUploadState enum defines the following states:

State Description
PENDING The video is being uploaded.
UPLOADED The video has been successfully uploaded and is being processed by YouTube.
PROCESSED The video has been successfully processed and is ready for use.
FAILED The upload or processing failed and cannot be completed.
REJECTED The video was rejected due to validation or policy reasons.
UNAVAILABLE The video state is unavailable; it may have been removed from YouTube.

Privacy settings

The video_privacy field controls who can see the uploaded video. The YouTubeVideoPrivacy enum supports:

  • PUBLIC: The video is available to anyone on YouTube. (Only allowed for brand channels).
  • UNLISTED: The video is not searchable but can be viewed by anyone with the link. This is the default and the only option for Google-managed channels.

Upload a video

To upload a video, you must use a multi-part request to the CreateYouTubeVideoUpload method. The request contains both the metadata for the upload and the video file itself.

1. Initiate the upload

Construct a CreateYouTubeVideoUploadRequest specifying the:

  • customer_id: Your Google Ads customer ID.
  • you_tube_video_upload: A YouTubeVideoUpload object with the video_title, video_description, and optionally the channel_id and video_privacy.

If you are using a client library, call the CreateYouTubeVideoUpload method, passing your video file, and the video upload will be handled internally.

Java

This example is not yet available in Java; you can take a look at the other languages.
    

C#

This example is not yet available in C#; you can take a look at the other languages.
    

PHP

This example is not yet available in PHP; you can take a look at the other languages.
    

Python

yt_service: YouTubeVideoUploadServiceClient = client.get_service(
    "YouTubeVideoUploadService"
)

create_upload_request: CreateYouTubeVideoUploadRequest = (
    youtube_video_upload_service.CreateYouTubeVideoUploadRequest()
)
create_upload_request.customer_id = customer_id
create_upload_request.you_tube_video_upload.video_title = "Test Video"
create_upload_request.you_tube_video_upload.video_description = (
    "Test Video Description"
)
create_upload_request.you_tube_video_upload.video_privacy = (
    client.enums.YouTubeVideoPrivacyEnum.UNLISTED
)

video_upload_resource_name: str
with open(video_file_path, "rb") as stream:
    response: CreateYouTubeVideoUploadResponse = (
        yt_service.create_you_tube_video_upload(
            stream=stream,
            request=create_upload_request,
            retry=None,
        )
    )
    print(f"Created YouTube video upload: {response.resource_name}")
      

Ruby

This example is not yet available in Ruby; you can take a look at the other languages.
    

Perl

This example is not yet available in Perl; you can take a look at the other languages.
    

curl

# 
# Use the --i curl parameter to capture response headers in the $RESPONSE
# variable.
FILE_SIZE=$(wc -c < "${VIDEO_FILE_NAME}" | tr -d '\r')
RESPONSE=$(curl -i -f -v -s --request POST \
"https://googleads.googleapis.com/resumable/upload/v${API_VERSION}/customers/${CUSTOMER_ID}:youTubeVideoUploads:create" \
--header "Content-Type: application/json" \
--header "developer-token: ${DEVELOPER_TOKEN}" \
--header "login-customer-id: ${MANAGER_CUSTOMER_ID}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
--header "X-Goog-Upload-Protocol: resumable" \
--header "X-Goog-Upload-Command: start" \
--header "X-Goog-Upload-Header-Content-Length: ${FILE_SIZE}" \
--data @- <<EOF
{
  "customer_id": "${CUSTOMER_ID}",
  "you_tube_video_upload": {
    "video_title": "${VIDEO_TITLE}",
    "video_description": "${VIDEO_DESCRIPTION}",
    "video_privacy": "UNLISTED"
  }
}
EOF
)

# Extract the value of the "x-goog-upload-url" header from the HTTP response.
UPLOAD_URL=$(echo "${RESPONSE}" \
  | grep -i '^x-goog-upload-url' \
  | awk '{print $2}' \
  | tr -d '\r')
CHUNK_SIZE=$(echo "${RESPONSE}" \
  | grep -i '^x-goog-upload-chunk-granularity' \
  | awk '{print $2}' \
  | tr -d '\r')
      

If you are using REST, the following section describes how to manage the video upload.

2. Upload the video

When you send a REST request to the CreateYouTubeVideoUpload method, the response contains the URL to be used for uploading the video bytes in the x-goog-upload-url HTTP response header, along with other metadata such as the expected size of each chunk for chunked uploads.

You can also declare the size of the video you are going to upload initially, when starting the process, with the x-goog-upload-header-content-length HTTP request header.

For a full description of the HTTP headers used in the video upload protocol, refer to the following code example:

# Take the first ${CHUNK_SIZE} bytes of the video file and upload them.
head -c ${CHUNK_SIZE} ${VIDEO_FILE_NAME} | curl -i -v -X PUT "${UPLOAD_URL}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
--header "X-Goog-Upload-Offset: 0" \
--header "X-Goog-Upload-Command: upload" \
--header "Content-Length: ${CHUNK_SIZE}" \
--data-binary @-

# Query the status of the upload.
QUERY_RESPONSE=$(curl -i -s -X POST "${UPLOAD_URL}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
--header "X-Goog-Upload-Command: query")

# Extract the value of the "x-goog-upload-size-received" header from the HTTP
# response.
UPLOADED_BYTES=$(echo "${QUERY_RESPONSE}" \
  | grep -i '^x-goog-upload-size-received' \
  | awk '{print $2}' \
  | tr -d '\r')

echo "Uploaded ${UPLOADED_BYTES} bytes."

REMAINING_BYTES=$((FILE_SIZE - UPLOADED_BYTES))
echo "${REMAINING_BYTES} bytes remaining to upload."

FINALIZE_RESPONSE=$(tail -c ${REMAINING_BYTES} ${VIDEO_FILE_NAME} | curl -v -X PUT "${UPLOAD_URL}" \
--header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
--header "X-Goog-Upload-Offset: ${UPLOADED_BYTES}" \
--header "X-Goog-Upload-Command: upload, finalize" \
--data-binary @-)
UPLOADED_VIDEO_RESOURCE_NAME=$(echo $FINALIZE_RESPONSE | jq -r '.resourceName')
      

3. Retrieve the video upload state

After you started a video upload, you can retrieve its state by querying the you_tube_video_upload resource with GAQL:

Java

This example is not yet available in Java; you can take a look at the other languages.
    

C#

This example is not yet available in C#; you can take a look at the other languages.
    

PHP

This example is not yet available in PHP; you can take a look at the other languages.
    

Python

# Retrieve the metadata of the newly uploaded video.
query: str = f"""
    SELECT
      you_tube_video_upload.resource_name,
      you_tube_video_upload.video_id,
      you_tube_video_upload.state
    FROM you_tube_video_upload
    WHERE you_tube_video_upload.resource_name = '{video_upload_resource_name}'"""

ga_service: GoogleAdsServiceClient = client.get_service("GoogleAdsService")
stream: Iterator[SearchGoogleAdsStreamResponse] = ga_service.search_stream(
    customer_id=customer_id, query=query
)

for row in itertools.chain.from_iterable(batch.results for batch in stream):
    video = row.you_tube_video_upload
    print(
        f"Video with ID {row.you_tube_video_upload.video_id} was found in state {row.you_tube_video_upload.state}."
    )
      

Ruby

This example is not yet available in Ruby; you can take a look at the other languages.
    

Perl

This example is not yet available in Perl; you can take a look at the other languages.
    

curl

curl -i -v -X POST \
"https://qa-prod-googleads.sandbox.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:search" \
--header "Content-Type: application/json" \
  --header "Developer-Token: ${DEVELOPER_TOKEN}" \
  --header "login-customer-id: ${MANAGER_CUSTOMER_ID}" \
  --header "Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}" \
  --data @- <<EOF
{
  "query": "SELECT you_tube_video_upload.resource_name, you_tube_video_upload.video_id, you_tube_video_upload.state FROM you_tube_video_upload WHERE you_tube_video_upload.resource_name = '$UPLOADED_VIDEO_RESOURCE_NAME'"
}
EOF
      

Manage uploads

After a video upload is completed, you can use it as a video asset.

Use the uploaded video

After a video reaches the PROCESSED state, you can find its YouTube video ID in the video_id field of the YouTubeVideoUpload resource.

Use this video_id to create a VideoAsset or link it directly to ad types that support YouTube videos by referencing the video ID.

Update metadata

You can update the metadata of a video uploaded through this API using the UpdateYouTubeVideoUpload method. Only the video_title, video_description, and video_privacy fields can be updated.

Remove uploads

If you need to delete videos uploaded with the Google Ads API, use the RemoveYouTubeVideoUpload method. This will remove the video from both the Google Ads asset library and YouTube.