Skip to main content

Python Tutorial · Updated 2026

Last updated: June 12, 2026

YouTube API with Python: Complete 2026 Tutorial

Everything you need to use the YouTube API in Python — search videos, pull video and channel statistics, fetch comments and transcripts, paginate without burning your quota, and handle errors properly. Then, in Part 2, the same research jobs through the OutlierKit API with plain requests — in far fewer calls. Every code block on this page runs as-is.

What you'll build

  • Search YouTube videos from Python (search.list)
  • Pull view counts, likes, and duration for any video
  • List a channel's uploads for 1 quota unit instead of 100
  • Fetch comment threads and transcripts
  • Quota-aware pagination and error handling
  • Outlier + keyword research via the OutlierKit API

Prerequisites: Python 3.10+ and a YouTube Data API v3 key. Don't have a key yet? It takes about five minutes — follow our step-by-step guide and come back.

Quick answer

To use the YouTube API in Python, install Google's official client with pip install google-api-python-client, then build a client with build("youtube", "v3", developerKey=YOUR_API_KEY) and call methods like search().list() and videos().list(). An API key from the Google Cloud Console is enough for public data; OAuth is only needed for uploads and private data. For transcripts, Python developers use the unofficial youtube-transcript-api library, and for competitive research the OutlierKit API works with plain requests — a bearer token and 1 credit per call, no SDK required.

Facts at a glance

Official librarygoogle-api-python-client
Installpip install google-api-python-client
AuthAPI key (public data) / OAuth (private data)
Search cost100 quota units per search.list call
Cheap uploads listingplaylistItems.list (1 unit)
Transcriptsyoutube-transcript-api (unofficial)
OutlierKit in Pythonrequests + bearer token, no SDK needed

Beyond the official client

Everything the Data API can't return — outlier scores, transcripts at scale, keyword volumes — is a single Python request away on the OutlierKit API: bearer token, 1 credit per call, no SDK to install.

How do you set up the YouTube API client in Python?

Google ships an official Python client that covers every YouTube Data API v3 endpoint. Install it once:

pip install google-api-python-client

Never hardcode your API key in a script you might commit. Put it in an environment variable instead:

# macOS / Linux
export YT_API_KEY="AIza...your-key-here"

# Windows (PowerShell)
setx YT_API_KEY "AIza...your-key-here"

Then build the client. Everything else on this page starts from this youtube object:

import os
from googleapiclient.discovery import build

youtube = build("youtube", "v3", developerKey=os.environ["YT_API_KEY"])

That's the whole setup for public data. No OAuth needed unless you upload videos, post comments, or read private analytics on a user's behalf.

Step 1 · search.list

Search videos

search.list is the workhorse — and the most expensive call in the API at 100 quota units per request out of your 10,000-unit daily budget. Pass q (the query), part="snippet", type="video", and maxResults (up to 50):

request = youtube.search().list(
    q="home workout",
    part="snippet",
    type="video",
    maxResults=10,
)
response = request.execute()

for item in response["items"]:
    video_id = item["id"]["videoId"]
    title = item["snippet"]["title"]
    channel = item["snippet"]["channelTitle"]
    print(f"{video_id}  {title}  ({channel})")

Note that search results only carry the snippet — title, description, channel, thumbnails. No view counts. Collect the videoId values and feed them into videos.list (next step) for statistics. For the full parameter reference, see our YouTube Search API guide.

Step 2 · videos.list

Get video statistics

videos.list costs just 1 unit and accepts up to 50 comma-separated IDs per call. Request part="snippet,statistics,contentDetails" to get metadata, view/like/comment counts, and duration in one shot:

video_ids = [item["id"]["videoId"] for item in response["items"]]

stats_response = youtube.videos().list(
    part="snippet,statistics,contentDetails",
    id=",".join(video_ids),  # up to 50 IDs per call
).execute()

for video in stats_response["items"]:
    title = video["snippet"]["title"]
    views = int(video["statistics"].get("viewCount", 0))
    likes = int(video["statistics"].get("likeCount", 0))
    duration = video["contentDetails"]["duration"]  # ISO 8601, e.g. PT12M34S
    print(f"{views:>12,} views  {likes:>9,} likes  {duration:<12} {title}")

Use .get("likeCount", 0) rather than direct key access — channels can hide like counts, and some videos disable comments, so those keys may be missing entirely.

Step 3 · channels.list + playlistItems.list

Get channel info and list its uploads (the 1-unit trick)

The single most useful quota optimisation in the entire API: don't use search.list (100 units) to find a channel's videos. Every channel has a hidden "uploads" playlist containing all of its public videos, and reading it with playlistItems.list costs 1 unit per page:

# 1. Get channel stats + the uploads playlist ID (1 unit)
channel_response = youtube.channels().list(
    part="contentDetails,statistics",
    id="UCX6OQ3DkcsbYNE6H8uQQuVA",  # MrBeast
).execute()

channel = channel_response["items"][0]
subs = int(channel["statistics"]["subscriberCount"])
uploads_playlist_id = channel["contentDetails"]["relatedPlaylists"]["uploads"]
print(f"Subscribers: {subs:,}")

# 2. Read the uploads playlist (1 unit per page of 50)
uploads_response = youtube.playlistItems().list(
    part="snippet,contentDetails",
    playlistId=uploads_playlist_id,
    maxResults=50,
).execute()

for item in uploads_response["items"]:
    video_id = item["contentDetails"]["videoId"]
    title = item["snippet"]["title"]
    published = item["contentDetails"].get("videoPublishedAt", "")
    print(f"{published}  {video_id}  {title}")

Two units total instead of 100. If you only have a handle like @mrbeast, pass forHandle="@mrbeast" to channels.list instead of id.

Step 4 · commentThreads.list

Fetch comments

Top-level comments come from commentThreads.list (1 unit per call). Order by relevance for top comments or time for newest:

comments_response = youtube.commentThreads().list(
    part="snippet",
    videoId="dQw4w9WgXcQ",
    order="relevance",
    maxResults=20,
    textFormat="plainText",
).execute()

for thread in comments_response["items"]:
    top = thread["snippet"]["topLevelComment"]["snippet"]
    print(f"[{top['likeCount']:>5} likes] {top['authorDisplayName']}: {top['textDisplay'][:80]}")

Watch for 403 errors on videos with comments disabled. For reply threads, pagination, and sentiment-mining patterns, see the full YouTube Comments API guide.

Step 5 · youtube-transcript-api

Fetch transcripts in Python

The official Data API doesn't hand out caption text without OAuth and video ownership. The community workaround is the youtube-transcript-api package, which reads YouTube's internal transcript endpoints:

pip install youtube-transcript-api
from youtube_transcript_api import YouTubeTranscriptApi

# v1.x API style: instantiate, then fetch
ytt_api = YouTubeTranscriptApi()
transcript = ytt_api.fetch("dQw4w9WgXcQ")

for snippet in transcript:
    print(f"[{snippet.start:>7.1f}s] {snippet.text}")

full_text = " ".join(snippet.text for snippet in transcript)
Important caveat: this library is unofficial — it scrapes endpoints YouTube doesn't document, and YouTube aggressively blocks requests from cloud and datacenter IPs (AWS, GCP, Azure, most VPS providers). It usually works from your laptop and usually fails in production. For workarounds, proxies, and hosted alternatives, read the dedicated YouTube Transcript API guide. (Part 2 below shows a server-friendly transcript endpoint.)

Step 6 · nextPageToken

Pagination and quota-aware patterns

Every list endpoint caps at 50 results per page and returns a nextPageToken while more pages exist. The canonical loop, here pulling a whole uploads playlist:

def all_uploads(youtube, playlist_id: str) -> list[dict]:
    """Fetch every video in a playlist, 50 per page, 1 unit per page."""
    videos = []
    page_token = None
    while True:
        response = youtube.playlistItems().list(
            part="snippet,contentDetails",
            playlistId=playlist_id,
            maxResults=50,
            pageToken=page_token,
        ).execute()
        videos.extend(response["items"])
        page_token = response.get("nextPageToken")
        if not page_token:
            return videos

Three habits that keep you inside the 10,000-unit daily quota:

  • Prefer 1-unit endpoints over search. playlistItems.list, videos.list, and channels.list all cost 1 unit. One search.list call costs the same as 100 of them.

  • Batch parts and IDs. videos.list with part="snippet,statistics,contentDetails" and 50 IDs is one call. Three separate part requests for 50 individual IDs is 150 calls' worth of waste.

  • Cache aggressively. View counts don't change meaningfully minute-to-minute. Write responses to SQLite or JSON files keyed by video ID and re-fetch on a schedule, not on every run.

Step 7 · HttpError

How do you handle quotaExceeded in Python?

The google-api-python-client library raises googleapiclient.errors.HttpError on any non-2xx response. The one you'll meet first is 403 quotaExceeded:

import json
from googleapiclient.errors import HttpError

try:
    response = youtube.search().list(
        q="home workout", part="snippet", type="video", maxResults=10
    ).execute()
except HttpError as e:
    if e.resp.status == 403:
        details = json.loads(e.content)
        reason = details["error"]["errors"][0]["reason"]
        if reason == "quotaExceeded":
            # Daily quota is gone — resets at midnight Pacific time.
            # Retrying is pointless; log it and queue work for tomorrow.
            print("Quota exhausted for today.")
        else:
            print(f"Forbidden: {reason}")  # e.g. commentsDisabled
    elif e.resp.status == 404:
        print("Video or channel not found.")
    else:
        raise

Unlike rate-limit errors on most APIs, quotaExceeded is a hard daily wall — exponential backoff won't save you. How the quota is computed, per-endpoint costs, and how to request an increase are covered in our YouTube API quota guide.

Part 2

How do you call the OutlierKit API from Python?

The OutlierKit API is a YouTube competitive-intelligence API that returns outlier scores, semantic channel search and similarity, video transcripts, comments, and keyword search volumes as structured JSON — one bearer-token key, 1 credit per call, on OutlierKit Pro ($49/month) and Max ($199/month) plans. The official YouTube Data API gives you raw metrics. What it can't tell you is which videos are outperforming their channel's baseline — the signal that actually matters for content research. Deriving it yourself means pulling every channel's history, computing rolling averages, and burning quota all the way down. The OutlierKit API ships that intelligence pre-computed: outlier scores, semantic search, keyword volumes, and cached transcripts. No SDK required — it's plain REST with bearer auth, so Python's requests library is all you need. Every call costs 1 credit.

1. Outlier video search

One POST replaces the search → stats → channel-baseline pipeline from Part 1. Results come back already scored:

import os
import requests

API_BASE = "https://outlierkit.com/api/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['OUTLIERKIT_API_KEY']}",
    "Content-Type": "application/json",
}

resp = requests.post(
    f"{API_BASE}/outliers/search",
    headers=HEADERS,
    json={
        "query": "home workout",
        "limit": 10,
        "minOutlierScore": 2,   # only videos at 2x+ their channel baseline
    },
).json()

if not resp["success"]:
    raise RuntimeError(resp)

data = resp["data"]
print(f"Found {data['totalFound']} outliers "
      f"(credits left: {resp['credits']['remaining']})")

for video in data["results"]:
    metrics = video["outlierMetrics"]
    channel = video["channel"]
    print(f"{metrics['outlierMultiplier']:>5.1f}x  "
          f"{video['title'][:60]}  — {channel['title']} "
          f"({channel['subscribers']:,} subs)")

Useful knobs: limit (1–100, default 20), offset, threshold (semantic similarity, 0–1, default 0.35), and sortBy (similarity, recent, or views). Each result's outlierMetrics object carries outlierMultiplier, channelOutlierRatio, viewsVsChannelAvg, viewsVsRecentAverage, and highestValue.

2. Keyword research

Expand seed keywords into ranked, deduplicated YouTube search keywords with monthly volume and competition:

resp = requests.post(
    f"{API_BASE}/keywords/research",
    headers=HEADERS,
    json={
        "keywords": ["fitness", "home workout"],
        "minVolume": 5000,
        "limit": 25,
    },
).json()

if resp["success"]:
    for kw in resp["data"]["keywords"]:
        print(f"{kw['volume']:>9,}/mo  comp={kw['competition']}  {kw['keyword']}")
    print(f"Total found: {resp['data']['totalKeywordsFound']}, "
          f"after volume filter: {resp['data']['filteredKeywords']}")

3. Transcripts that work from servers

Unlike the unofficial scraper from Step 5, this is a plain authenticated GET — it works identically from your laptop, AWS, or a CI runner, and transcripts are cached on first fetch:

video_id = "dQw4w9WgXcQ"
resp = requests.get(
    f"{API_BASE}/videos/{video_id}/transcript",
    headers=HEADERS,
).json()

if resp["success"]:
    transcript = resp["data"]
    print(f"Source: {transcript['source']}, "
          f"duration: {transcript['totalDurationMs'] / 1000:.0f}s")
    for seg in transcript["segments"][:5]:
        print(f"[{seg['startTime']}] {seg['text']}")
    full_text = transcript["fullText"]  # ready for an LLM prompt

All endpoints share the same response envelope — {success, data, credits: {charged, remaining}, timing, requestId} — so one helper function can check success and track credit burn across your whole script. Full request and response schemas live in the live API docs.

For Pro and Max users

Skip the quota math — query the intelligence layer directly

Everything in Part 2 runs on the OutlierKit API: outlier search, channel similarity, keyword volumes, and server-safe transcripts as plain JSON over REST. 1 credit per call, bearer-token auth, included on Pro ($49/mo, 500 credits) and Max ($199/mo, 2,000 credits).

Mini-project

Putting it together: outlier research + live stats in ~30 lines

The OutlierKit API and the official YouTube Data API complement each other. OutlierKit answers "which videos in this niche are breaking out?" — the official API answers "what are their numbers right now?" This script takes a niche query, pulls the top outliers from OutlierKit, then refreshes each video's live stats with a single 1-unit videos.list call:

import os
import requests
from googleapiclient.discovery import build

NICHE = "home workout"

# --- 1. OutlierKit: which videos are breaking out in this niche? (1 credit)
ok = requests.post(
    "https://outlierkit.com/api/v1/outliers/search",
    headers={
        "Authorization": f"Bearer {os.environ['OUTLIERKIT_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={"query": NICHE, "limit": 10, "minOutlierScore": 2},
).json()
assert ok["success"], ok

outliers = {v["videoId"]: v for v in ok["data"]["results"]}

# --- 2. Official API: live stats for those exact videos (1 quota unit)
youtube = build("youtube", "v3", developerKey=os.environ["YT_API_KEY"])
live = youtube.videos().list(
    part="statistics",
    id=",".join(outliers.keys()),
).execute()

# --- 3. Merge: outlier score from OutlierKit + fresh views from YouTube
print(f"Breakout videos for '{NICHE}':\n")
for video in live["items"]:
    meta = outliers[video["id"]]
    multiplier = meta["outlierMetrics"]["outlierMultiplier"]
    views_now = int(video["statistics"]["viewCount"])
    print(f"{multiplier:>5.1f}x baseline | {views_now:>12,} views now | "
          f"{meta['title'][:55]}")

Total cost: 1 OutlierKit credit and 1 YouTube quota unit. Doing the discovery half with the official API alone would mean a 100-unit search plus a channel-history crawl per result — and you still wouldn't have baseline-relative scores. Schedule this with cron (or n8n) and pipe the output into a Notion board or Slack alert.

Frequently Asked Questions

Common questions about using the YouTube API with Python.

Which Python library should I use for the YouTube API?+

Use google-api-python-client, Google's official client library. Install it with pip install google-api-python-client, then build a service object with build("youtube", "v3", developerKey=YOUR_KEY). It covers every YouTube Data API v3 endpoint — search, videos, channels, playlistItems, commentThreads — and handles request signing and JSON parsing for you. For transcripts, the official API doesn't expose captions without OAuth, so most developers add the unofficial youtube-transcript-api package or use a hosted endpoint like OutlierKit's transcript API.

Do I need OAuth to use the YouTube API in Python?+

OAuth is not required for reading public YouTube data in Python. Searching videos, fetching video statistics, channel info, playlists, and public comments all work with a simple API key passed as developerKey. You only need OAuth 2.0 when acting on behalf of a user — uploading videos, posting comments, managing playlists, reading your own analytics, or downloading caption tracks via the captions endpoint.

How do I install the YouTube API client for Python?+

Install the YouTube API client for Python by running pip install google-api-python-client (Python 3.10+ recommended). That single package gives you the build() factory and all YouTube Data API v3 resources. If you also want transcripts, add pip install youtube-transcript-api. No extra SDK is needed for the OutlierKit API — the standard requests library is enough.

How do I handle quotaExceeded errors in Python?+

Catch googleapiclient.errors.HttpError, check for status 403, and inspect the error reason for "quotaExceeded". When you hit it, your daily 10,000-unit quota is spent and resets at midnight Pacific time — retrying won't help, so log it and stop or queue work for the next day. To avoid hitting it at all: replace search.list (100 units) with playlistItems.list (1 unit) where possible, batch parts into one videos.list call, and cache responses locally.

Can I get YouTube transcripts in Python?+

Yes — Python developers can get YouTube transcripts in two ways. The unofficial youtube-transcript-api package (v1.x: YouTubeTranscriptApi().fetch(video_id)) works well from residential IPs but is frequently blocked from cloud/datacenter IPs because it scrapes YouTube's internal endpoints. The alternative is a hosted transcript API — OutlierKit's GET /videos/{id}/transcript returns full text plus timed segments over plain HTTPS with bearer auth, and works fine from AWS, GCP, or any server.

Does the YouTube API Python client support async?+

google-api-python-client is synchronous only. For async workloads you have three options: run blocking calls in a thread executor with asyncio.to_thread(), use aiogoogle (a community async client for Google APIs), or skip the client library and call the REST endpoints directly with aiohttp or httpx — the YouTube Data API is plain HTTPS + JSON, so an async HTTP client works fine with your API key as a query parameter.

Is there a Python SDK for the OutlierKit API?+

No Python SDK is needed for the OutlierKit API. The OutlierKit API is a standard REST API — JSON in, JSON out, with bearer-token authentication. The built-in requests library (or httpx for async) covers everything: pass Authorization: Bearer <your-key> in the headers and POST/GET against https://outlierkit.com/api/v1. Every call costs 1 credit and returns a consistent envelope with success, data, and credits fields.

Where are the OutlierKit API docs?+

The OutlierKit API docs — the live, canonical reference — are at outlierkit.com/app/api-docs — request parameters, response shapes, error envelopes, authentication, and rate limits. API access is included on the Pro plan ($49/month, 500 credits) and Max plan ($199/month, 2,000 credits), with top-ups at $10 per 100 credits. Credits are shared with the OutlierKit web app.

Pricing details for API access are on the OutlierKit pricing page.

Build your YouTube research pipeline in Python

Pair the official Data API with OutlierKit's pre-computed outlier scores, keyword volumes, and server-safe transcripts — plain REST, 1 credit per call, no SDK required.

View the API docs

Written by

Aditi

Aditi

Founder OutlierKit and UTubeKit

AI-Verified

Don’t take our word for it.
Ask AI.

Ask any leading AI what OutlierKit does for YouTube creators.