Sign in to Channel ID Finder

← All posts

Extract YouTube Channel IDs with Python

channelid.app blog

Need Channel IDs for a data pipeline, research project, or automation? Here is how to extract them with Python — from a single URL to bulk CSV processing.


What You Will Build

By the end of this guide, you will have a Python function (or script) that:


Prerequisites

Optionally: pandas for CSV processing (pip install pandas)


Single URL Lookup

Using the YouTube Data API

import re
import requests

API_KEY = "your_api_key_here"
BASE_URL = "https://www.googleapis.com/youtube/v3/channels"

def get_channel_id(url: str) -> str | None:
    """
    Resolve a YouTube channel URL to its Channel ID.

    Supports:
    - https://youtube.com/@handle
    - https://youtube.com/channel/UCxxxxxxxx
    - https://youtube.com/c/customname
    - https://youtube.com/user/username
    """
    url = url.strip()

    # Case 1: /channel/ format — ID is already in the URL
    match = re.search(r"youtube\.com/channel/(UC[a-zA-Z0-9_-]{22})", url)
    if match:
        return match.group(1)

    # Case 2: @handle format (post-2022)
    match = re.search(r"youtube\.com/(@[\w.-]+)", url)
    if match:
        resp = requests.get(BASE_URL, params={
            "part": "id",
            "forHandle": match.group(1),
            "key": API_KEY
        })
        resp.raise_for_status()
        items = resp.json().get("items", [])
        return items[0]["id"] if items else None

    # Case 3: Legacy /user/ format
    match = re.search(r"youtube\.com/user/([\w-]+)", url)
    if match:
        resp = requests.get(BASE_URL, params={
            "part": "id",
            "forUsername": match.group(1),
            "key": API_KEY
        })
        resp.raise_for_status()
        items = resp.json().get("items", [])
        return items[0]["id"] if items else None

    # Case 4: /c/ custom URL — API does not support this directly
    match = re.search(r"youtube\.com/c/([\w-]+)", url)
    if match:
        return _get_id_from_page(url)

    return None


def _get_id_from_page(url: str) -> str | None:
    """Extract Channel ID from YouTube page source."""
    headers = {"User-Agent": "Mozilla/5.0"}
    try:
        resp = requests.get(url, headers=headers, timeout=10)
        match = re.search(r'"channelId":"(UC[a-zA-Z0-9_-]{22})"', resp.text)
        return match.group(1) if match else None
    except Exception:
        return None


# Quick test
if __name__ == "__main__":
    test_urls = [
        "https://youtube.com/@mkbhd",
        "https://youtube.com/channel/UCBcRF18a7Qf58cCRy5xuWwQ",
        "https://youtube.com/user/marquesbrownlee",
    ]
    for url in test_urls:
        channel_id = get_channel_id(url)
        print(f"{url}{channel_id}")

Bulk Processing from a CSV

If you have a list of channels in a spreadsheet, process them all at once:

channels.csv:

name,url
MKBHD,https://youtube.com/@mkbhd
Linus Tech Tips,https://youtube.com/@linustechtips
Veritasium,https://youtube.com/@veritasium

bulk_lookup.py:

import csv
import time

def process_csv(input_path: str, output_path: str, delay: float = 0.5):
    """
    Read channel URLs from a CSV, look up Channel IDs, write results.
    delay: seconds to wait between API calls (rate limiting)
    """
    rows = []
    with open(input_path, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            url = row.get("url", "").strip()
            channel_id = get_channel_id(url) if url else None
            rows.append({
                **row,
                "channel_id": channel_id or "NOT_FOUND",
            })
            print(f"Processed: {row.get('name', url)}{channel_id}")
            time.sleep(delay)  # Stay within quota

    if rows:
        fieldnames = list(rows[0].keys())
        with open(output_path, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerows(rows)
        print(f"\nSaved {len(rows)} results to {output_path}")


process_csv("channels.csv", "channels_with_ids.csv")

Caching Channel IDs

Channel IDs are permanent — once you have one, you never need to look it up again. Cache aggressively:

import json
from pathlib import Path

CACHE_FILE = Path("channel_id_cache.json")

def load_cache() -> dict:
    if CACHE_FILE.exists():
        return json.loads(CACHE_FILE.read_text())
    return {}

def save_cache(cache: dict):
    CACHE_FILE.write_text(json.dumps(cache, indent=2))

def get_channel_id_cached(url: str) -> str | None:
    cache = load_cache()
    if url in cache:
        return cache[url]

    channel_id = get_channel_id(url)
    if channel_id:
        cache[url] = channel_id
        save_cache(cache)

    return channel_id

For production workloads, replace the JSON file cache with Redis or a database.


Quota Management

The YouTube Data API gives you 10,000 units/day for free. Each channels.list call with part=id costs 1 unit.

For larger volumes:

  1. Cache everything — re-use IDs, they do not expire
  2. Batch by ID — verify multiple channels in one request (up to 50): python ids = ["UC1...", "UC2...", "UC3..."] resp = requests.get(BASE_URL, params={ "part": "snippet", "id": ",".join(ids), "key": API_KEY })
  3. channelid.app — for occasional one-off lookups outside your quota

Error Handling

import time

def get_channel_id_safe(url: str, retries: int = 3) -> str | None:
    """get_channel_id with retry logic for transient errors."""
    for attempt in range(retries):
        try:
            return get_channel_id(url)
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 403:
                print("Quota exceeded. Wait until midnight Pacific or use a new key.")
                return None
            if attempt < retries - 1:
                time.sleep(2 ** attempt)
        except requests.exceptions.ConnectionError:
            if attempt < retries - 1:
                time.sleep(2 ** attempt)
    return None

Common Issues and Fixes

Problem Cause Fix
items: [] returned Handle/username not found Check URL is correct; verify on channelid.app
403 quotaExceeded Daily quota hit Cache IDs; use new API key; wait until midnight PT
/c/ URL returns None API cannot resolve /c/ format Use _get_id_from_page() fallback
Rate limit on page scraping Too many requests to YouTube Add time.sleep(1) between scrape calls

Summary

URL Format Resolution Method API Cost
/channel/UCxxxxxx Extract from URL (regex) 0 units
@handle channels.list?forHandle= 1 unit
/user/username channels.list?forUsername= 1 unit
/c/customname Page scrape + regex 0 API units

For a quick one-off lookup without writing code, use channelid.app — paste any URL, get the ID instantly.

Find any YouTube Channel ID instantly

Paste any channel URL — @handle, /c/, /channel/, or /user/ format — and get the Channel ID in one click.

Try channelid.app — Free