The YouTube Data API (v3) is an experimental API version, which means it is still in development. We'll be adding many more features to the API while it is still an experimental version. Until the experimental label is removed, however, the Deprecation Policy for YouTube APIs won't apply to this version as discussed in the API Terms of Service.
The following code samples, which use the Python client library, are available for the YouTube Data API.
Search by topic
#!/usr/bin/python from apiclient.discovery import build from optparse import OptionParser import json import urllib # Set DEVELOPER_KEY to the "API key" value from the "Access" tab of the # Google APIs Console http://code.google.com/apis/console#access # Please ensure that you have enabled the YouTube Data API and Freebase API # for your project. DEVELOPER_KEY = "REPLACE_ME" YOUTUBE_API_SERVICE_NAME = "youtube" YOUTUBE_API_VERSION = "v3" FREEBASE_SEARCH_URL = "https://www.googleapis.com/freebase/v1/search?%s" def get_topic_id(options): freebase_params = dict(query=options.query, key=DEVELOPER_KEY) freebase_url = FREEBASE_SEARCH_URL % urllib.urlencode(freebase_params) freebase_response = json.loads(urllib.urlopen(freebase_url).read()) if len(freebase_response["result"]) == 0: exit("No matching terms were found in Freebase.") mids = [] index = 1 print "The following topics were found:" for result in freebase_response["result"]: mids.append(result["mid"]) print " %2d. %s (%s)" % (index, result.get("name", "Unknown"), result.get("notable", {}).get("name", "Unknown")) index += 1 mid = None while mid is None: index = raw_input("Enter a topic number to find related YouTube %ss: " % options.type) try: mid = mids[int(index) - 1] except ValueError: pass return mid def youtube_search(mid, options): youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY) search_response = youtube.search().list( # The q= parameter is currently required, but in the future just a topicId # will be sufficient. q=options.query, topicId=mid, type=options.type, part="id,snippet", maxResults=options.maxResults ).execute() for search_result in search_response.get("items", []): if search_result["id"]["kind"] == "youtube#video": print "%s (%s)" % (search_result["snippet"]["title"], search_result["id"]["videoId"]) elif search_result["id"]["kind"] == "youtube#channel": print "%s (%s)" % (search_result["snippet"]["title"], search_result["id"]["channelId"]) elif search_result["id"]["kind"] == "youtube#playlist": print "%s (%s)" % (search_result["snippet"]["title"], search_result["id"]["playlistId"]) if __name__ == "__main__": parser = OptionParser() parser.add_option("--query", dest="query", help="Freebase search term", default="Google") parser.add_option("--max-results", dest="maxResults", help="Max YouTube results", default=25) parser.add_option("--type", dest="type", help="YouTube result type: video, playlist, or channel", default="channel") (options, args) = parser.parse_args() mid = get_topic_id(options) youtube_search(mid, options)
Search by keyword
#!/usr/bin/python from apiclient.discovery import build from optparse import OptionParser # Set DEVELOPER_KEY to the "API key" value from the "Access" tab of the # Google APIs Console http://code.google.com/apis/console#access # Please ensure that you have enabled the YouTube Data API for your project. DEVELOPER_KEY = "REPLACE_ME" YOUTUBE_API_SERVICE_NAME = "youtube" YOUTUBE_API_VERSION = "v3" def youtube_search(options): youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY) search_response = youtube.search().list( q=options.q, part="id,snippet", maxResults=options.maxResults ).execute() videos = [] channels = [] playlists = [] for search_result in search_response.get("items", []): if search_result["id"]["kind"] == "youtube#video": videos.append("%s (%s)" % (search_result["snippet"]["title"], search_result["id"]["videoId"])) elif search_result["id"]["kind"] == "youtube#channel": channels.append("%s (%s)" % (search_result["snippet"]["title"], search_result["id"]["channelId"])) elif search_result["id"]["kind"] == "youtube#playlist": playlists.append("%s (%s)" % (search_result["snippet"]["title"], search_result["id"]["playlistId"])) print "Videos:\n", "\n".join(videos), "\n" print "Channels:\n", "\n".join(channels), "\n" print "Playlists:\n", "\n".join(playlists), "\n" if __name__ == "__main__": parser = OptionParser() parser.add_option("--q", dest="q", help="Search term", default="Google") parser.add_option("--max-results", dest="maxResults", help="Max results", default=25) (options, args) = parser.parse_args() youtube_search(options)
Retrieve my uploads
#!/usr/bin/python import httplib2 import os import sys from apiclient.discovery import build from oauth2client.file import Storage from oauth2client.client import flow_from_clientsecrets from oauth2client.tools import run # CLIENT_SECRETS_FILE, name of a file containing the OAuth 2.0 information for # this application, including client_id and client_secret. You can acquire an # ID/secret pair from the API Access tab on the Google APIs Console # http://code.google.com/apis/console#access # For more information about using OAuth2 to access Google APIs, please visit: # https://developers.google.com/accounts/docs/OAuth2 # For more information about the client_secrets.json file format, please visit: # https://developers.google.com/api-client-library/python/guide/aaa_client_secrets # Please ensure that you have enabled the YouTube Data API for your project. CLIENT_SECRETS_FILE = "client_secrets.json" # Helpful message to display if the CLIENT_SECRETS_FILE is missing. MISSING_CLIENT_SECRETS_MESSAGE = """ WARNING: Please configure OAuth 2.0 To make this sample run you will need to populate the client_secrets.json file found at: %s with information from the APIs Console https://code.google.com/apis/console#access For more information about the client_secrets.json file format, please visit: https://developers.google.com/api-client-library/python/guide/aaa_client_secrets """ % os.path.abspath(os.path.join(os.path.dirname(__file__), CLIENT_SECRETS_FILE)) # A limited OAuth 2 access scope that allows for uploading files, but not other # types of account access. YOUTUBE_READONLY_SCOPE = "https://www.googleapis.com/auth/youtube.readonly" YOUTUBE_API_SERVICE_NAME = "youtube" YOUTUBE_API_VERSION = "v3" flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, message=MISSING_CLIENT_SECRETS_MESSAGE, scope=YOUTUBE_READONLY_SCOPE) storage = Storage("%s-oauth2.json" % sys.argv[0]) credentials = storage.get() if credentials is None or credentials.invalid: credentials = run(flow, storage) youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, http=credentials.authorize(httplib2.Http())) channels_response = youtube.channels().list( mine=True, part="contentDetails" ).execute() for channel in channels_response["items"]: uploads_list_id = channel["contentDetails"]["relatedPlaylists"]["uploads"] print "Videos in list %s" % uploads_list_id next_page_token = "" while next_page_token is not None: playlistitems_response = youtube.playlistItems().list( playlistId=uploads_list_id, part="snippet", maxResults=50, pageToken=next_page_token ).execute() for playlist_item in playlistitems_response["items"]: title = playlist_item["snippet"]["title"] video_id = playlist_item["snippet"]["resourceId"]["videoId"] print "%s (%s)" % (title, video_id) next_page_token = playlistitems_response.get("tokenPagination", {}).get( "nextPageToken") print
Upload a video
#!/usr/bin/python import httplib import httplib2 import os import random import sys import time from apiclient.discovery import build from apiclient.errors import HttpError from apiclient.http import MediaFileUpload from oauth2client.file import Storage from oauth2client.client import flow_from_clientsecrets from oauth2client.tools import run from optparse import OptionParser # Explicitly tell the underlying HTTP transport library not to retry, since # we are handling retry logic ourselves. httplib2.RETRIES = 1 # Maximum number of times to retry before giving up. MAX_RETRIES = 10 # Always retry when these exceptions are raised. RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, httplib.NotConnected, httplib.IncompleteRead, httplib.ImproperConnectionState, httplib.CannotSendRequest, httplib.CannotSendHeader, httplib.ResponseNotReady, httplib.BadStatusLine) # Always retry when an apiclient.errors.HttpError with one of these status # codes is raised. RETRIABLE_STATUS_CODES = [500, 502, 503, 504] # CLIENT_SECRETS_FILE, name of a file containing the OAuth 2.0 information for # this application, including client_id and client_secret. You can acquire an # ID/secret pair from the API Access tab on the Google APIs Console # http://code.google.com/apis/console#access # For more information about using OAuth2 to access Google APIs, please visit: # https://developers.google.com/accounts/docs/OAuth2 # For more information about the client_secrets.json file format, please visit: # https://developers.google.com/api-client-library/python/guide/aaa_client_secrets # Please ensure that you have enabled the YouTube Data API for your project. CLIENT_SECRETS_FILE = "client_secrets.json" # 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" # Helpful message to display if the CLIENT_SECRETS_FILE is missing. MISSING_CLIENT_SECRETS_MESSAGE = """ WARNING: Please configure OAuth 2.0 To make this sample run you will need to populate the client_secrets.json file found at: %s with information from the APIs Console https://code.google.com/apis/console#access For more information about the client_secrets.json file format, please visit: https://developers.google.com/api-client-library/python/guide/aaa_client_secrets """ % os.path.abspath(os.path.join(os.path.dirname(__file__), CLIENT_SECRETS_FILE)) def get_authenticated_service(): flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, scope=YOUTUBE_UPLOAD_SCOPE, message=MISSING_CLIENT_SECRETS_MESSAGE) storage = Storage("%s-oauth2.json" % sys.argv[0]) credentials = storage.get() if credentials is None or credentials.invalid: credentials = run(flow, storage) return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, http=credentials.authorize(httplib2.Http())) def initialize_upload(options): youtube = get_authenticated_service() tags = None if options.keywords: tags = options.keywords.split(",") insert_request = youtube.videos().insert( part="snippet,status", body=dict( snippet=dict( title=options.title, description=options.description, tags=tags, categoryId=options.category ), status = dict( privacyStatus=options.privacyStatus ) ), media_body=MediaFileUpload(options.file, chunksize=-1, resumable=True) ) resumable_upload(insert_request) def resumable_upload(insert_request): response = None error = None retry = 0 while response is None: try: print "Uploading file..." status, response = insert_request.next_chunk() if 'id' in response: print "'%s' (video id: %s) was successfully uploaded." % ( options.title, response['id']) else: exit("The upload failed with an unexpected response: %s" % response) except HttpError, e: if e.resp.status in RETRIABLE_STATUS_CODES: error = "A retriable HTTP error %d occurred:\n%s" % (e.resp.status, e.content) else: raise except RETRIABLE_EXCEPTIONS, e: error = "A retriable error occurred: %s" % e if error is not None: print error retry += 1 if retry > MAX_RETRIES: exit("No longer attempting to retry.") max_sleep = 2 ** retry sleep_seconds = random.random() * max_sleep print "Sleeping %f seconds and then retrying..." % sleep_seconds time.sleep(sleep_seconds) if __name__ == '__main__': parser = OptionParser() parser.add_option("--file", dest="file", help="Video file to upload") parser.add_option("--title", dest="title", help="Video title", default="Test Title") parser.add_option("--description", dest="description", help="Video description", default="Test Description") parser.add_option("--category", dest="category", help="Video category", default="22") parser.add_option("--keywords", dest="keywords", help="Video keywords, comma separated", default="") parser.add_option("--privacyStatus", dest="privacyStatus", help="Video privacy status", default="unlisted") (options, args) = parser.parse_args() if options.file is None or not os.path.exists(options.file): exit("Please specify a valid file using the --file= parameter.") else: initialize_upload(options)
Post a channel bulletin
#!/usr/bin/python import httplib2 import os import sys from apiclient.discovery import build from oauth2client.file import Storage from oauth2client.client import flow_from_clientsecrets from oauth2client.tools import run from optparse import OptionParser # CLIENT_SECRETS_FILE, name of a file containing the OAuth 2.0 information for # this application, including client_id and client_secret. You can acquire an # ID/secret pair from the API Access tab on the Google APIs Console # http://code.google.com/apis/console#access # For more information about using OAuth2 to access Google APIs, please visit: # https://developers.google.com/accounts/docs/OAuth2 # For more information about the client_secrets.json file format, please visit: # https://developers.google.com/api-client-library/python/guide/aaa_client_secrets # Please ensure that you have enabled the YouTube Data API for your project. CLIENT_SECRETS_FILE = "client_secrets.json" # An OAuth 2 access scope that allows for full read/write access. YOUTUBE_READ_WRITE_SCOPE = "https://www.googleapis.com/auth/youtube" YOUTUBE_API_SERVICE_NAME = "youtube" YOUTUBE_API_VERSION = "v3" # Helpful message to display if the CLIENT_SECRETS_FILE is missing. MISSING_CLIENT_SECRETS_MESSAGE = """ WARNING: Please configure OAuth 2.0 To make this sample run you will need to populate the client_secrets.json file found at: %s with information from the APIs Console https://code.google.com/apis/console#access For more information about the client_secrets.json file format, please visit: https://developers.google.com/api-client-library/python/guide/aaa_client_secrets """ % os.path.abspath(os.path.join(os.path.dirname(__file__), CLIENT_SECRETS_FILE)) def get_authenticated_service(): flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, scope=YOUTUBE_READ_WRITE_SCOPE, message=MISSING_CLIENT_SECRETS_MESSAGE) storage = Storage("%s-oauth2.json" % sys.argv[0]) credentials = storage.get() if credentials is None or credentials.invalid: credentials = run(flow, storage) return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, http=credentials.authorize(httplib2.Http())) def post_bulletin(youtube, options): body = {} if options.message: body["snippet"] = dict(description=options.message) if options.videoid: body["contentDetails"] = dict( bulletin=dict( resourceId=dict( kind="youtube#video", videoId=options.videoid ) ) ) if options.playlistid: body["contentDetails"] = dict( bulletin=dict( resourceId=dict( kind="youtube#playlist", playlistId=options.playlistid ) ) ) youtube.activities().insert( part=",".join(body.keys()), body=body ).execute() print "The bulletin was posted to your channel." if __name__ == "__main__": parser = OptionParser() parser.add_option("--message", dest="message", help="Text message to post.") parser.add_option("--videoid", dest="videoid", help="ID of video to post.") parser.add_option("--playlistid", dest="playlistid", help="ID of playlist to post.") (options, args) = parser.parse_args() # A bulletin needs to contain at least one of: message, video, playlist. # You can post a message with or without an accompanying video or playlist. # You can't post both a video and playlist at the same time. if options.videoid and options.playlistid: exit("You cannot post a video and a playlist at the same time.") if not (options.message or options.videoid or options.playlistid): exit("Please provide a message, video, or playlist.") youtube = get_authenticated_service() post_bulletin(youtube, options)