sociallinks.dev

ब्लॉग / डेवलपर गाइड

हैंडल से YouTube चैनल ID कैसे पाएँ (और उल्टा भी)

क्रिएटर अपना YouTube चैनल @handle के रूप में शेयर करते हैं, पर YouTube Data API में लगभग सब कुछ चैनल ID से चलता है। अगर आप सिर्फ़ हैंडल सेव करते हैं, तो नाम बदलने पर आपके रिकॉर्ड टूट जाते हैं; अगर सिर्फ़ ID सेव करते हैं, तो यूज़र को वह नाम नहीं दिखा सकते जिसे वे पहचानते हैं। यह गाइड दिखाती है कि एक API कॉल से हैंडल को चैनल ID में कैसे बदलें, एक बार में 50 ID से वापस हैंडल कैसे पाएँ, पुराने /user/ और /c/ URL का क्या करें, और डिफ़ॉल्ट कोटा के अंदर कैसे रहें।

हैंडल, चैनल ID और पुराने नाम

किसी YouTube चैनल तक चार तरह के पहचानकर्ता (identifier) से पहुँचा जा सकता है। उनमें से सिर्फ़ एक के स्थिर रहने की गारंटी है।

पहचानकर्ताउदाहरणURL का रूपनोट
चैनल IDUC_x5XG1OV2P6uZZ5FSM9Ttwyoutube.com/channel/UC...वह ID जिससे YouTube चैनल की पहचान करता है। बड़े-छोटे अक्षर मायने रखते हैं। इसे अपनी primary key बनाएँ।
हैंडल@GoogleDevelopersyoutube.com/@GoogleDevelopersयूनिक, बड़े-छोटे अक्षर से फ़र्क नहीं पड़ता, और मालिक इसे YouTube Studio में बदल सकता है।
पुराना usernameGoogleDevelopersyoutube.com/user/GoogleDevelopersपुराने username सिस्टम से। API के forUsername फ़िल्टर से हल हो सकता है।
पुराना कस्टम URLGoogleDevelopersyoutube.com/c/GoogleDevelopersपुराने पर्सनलाइज़्ड URL, जो YouTube के अनुसार काम करते रहते हैं। कोई API फ़िल्टर इन्हें हल नहीं करता।

चैनल ID को key के रूप में सेव करें और हैंडल को डिस्प्ले मान की तरह रखें जिसे आप रिफ़्रेश करते हैं। बाकी गाइड इस बारे में है कि यूज़र ने जो भी दिया, उससे उस ID तक कैसे पहुँचें।

API key बनाएँ और कोटा समझें

  1. Step 1Google Cloud प्रोजेक्ट बनाएँ

    Google Cloud console में एक प्रोजेक्ट बनाएँ या मौजूदा प्रोजेक्ट चुनें।

  2. Step 2YouTube Data API v3 चालू करें

    APIs and services खोलें, लाइब्रेरी में YouTube Data API v3 खोजें और प्रोजेक्ट के लिए उसे चालू करें।

  3. Step 3API key बनाएँ

    Credentials में जाकर एक API key बनाएँ। हैंडल या ID से channels.list जैसे पब्लिक चैनल डेटा के लिए सिर्फ़ key चाहिए, OAuth नहीं। key को YouTube Data API तक सीमित करें।

  4. Step 4key को अपने कोड से बाहर रखें

    इसे किसी environment variable में रखें, जैसे YOUTUBE_API_KEY, और रनटाइम पर पढ़ें। नीचे के उदाहरण यही करते हैं।

  5. Step 5अपना कोटा जाँचें

    console में API का Quotas पेज आपका रोज़ का आवंटन और इस्तेमाल दिखाता है।

Google के दस्तावेज़ों से अहम संख्याएँ:

तो डिफ़ॉल्ट कोटा रोज़ लगभग 10,000 हैंडल खोज के लिए काफ़ी है, और ID खोज उससे 50 गुना तक, क्योंकि एक कॉल उसी एक यूनिट में ID से 50 चैनल ला सकती है।

forHandle से हैंडल को चैनल ID में बदलें

channels.list ठीक एक फ़िल्टर लेता है। forHandle फ़िल्टर एक YouTube हैंडल लेता है, और रेफ़रेंस कहता है कि मान के आगे @ चिह्न लगाया जा सकता है, इसलिए GoogleDevelopers और @GoogleDevelopers दोनों काम करते हैं। जब सिर्फ़ ID चाहिए तो part=id माँगें; जवाब छोटा होता है और कीमत वही।

cURL

curl -s "https://www.googleapis.com/youtube/v3/channels?part=id&forHandle=@GoogleDevelopers&key=$YOUTUBE_API_KEY"

नीचे का Node उदाहरण अनुरोध को एक हेल्पर, listChannels, में लपेटता है, जिसे बाकी गाइड दोबारा इस्तेमाल करती है। कुछ मेल न खाए तो यह ख़ाली सूची लौटाता है और ग़लत key या ख़त्म कोटा जैसी असली गड़बड़ियों पर error फेंकता है।

Node.js

const API = "https://www.googleapis.com/youtube/v3/channels";
async function listChannels(params) {
  const key = process.env.YOUTUBE_API_KEY;
  if (!key) throw new Error("Set YOUTUBE_API_KEY");
  const url = new URL(API);
  url.search = new URLSearchParams({ ...params, key }).toString();
  const res = await fetch(url, { signal: AbortSignal.timeout(10_000) });
  const data = await res.json();
  if (!res.ok) {
    const reason = data.error?.errors?.[0]?.reason;
    if (reason === "channelNotFound") return [];
    throw new Error(`YouTube API ${res.status} ${reason ?? ""}: ${data.error?.message}`);
  }
  return data.items ?? []; // no match: items is empty or missing
}

/** "@GoogleDevelopers" or "GoogleDevelopers" -> "UC_x5XG1OV2P6uZZ5FSM9Ttw" or null */
export async function channelIdFromHandle(handle) {
  const items = await listChannels({ part: "id", forHandle: handle.trim() });
  return items[0]?.id ?? null;
}

/** Legacy username from a youtube.com/user/<name> URL -> channel ID or null */
export async function channelIdFromUsername(username) {
  const items = await listChannels({ part: "id", forUsername: username.trim() });
  return items[0]?.id ?? null;
}

/** Any number of channel IDs -> Map(id -> { title, customUrl }) */
export async function channelsById(ids) {
  const unique = [...new Set(ids)];
  const found = new Map();
  for (let i = 0; i < unique.length; i += 50) {
    const batch = unique.slice(i, i + 50);
    const items = await listChannels({ part: "snippet", id: batch.join(","), maxResults: "50" });
    for (const ch of items) {
      found.set(ch.id, { title: ch.snippet.title, customUrl: ch.snippet.customUrl ?? null });
    }
  }
  return found; // IDs missing from the map were not found
}

Python

import os

import requests

API = "https://www.googleapis.com/youtube/v3/channels"


def list_channels(**params):
    key = os.environ.get("YOUTUBE_API_KEY")
    if not key:
        raise RuntimeError("Set YOUTUBE_API_KEY")
    response = requests.get(API, params={**params, "key": key}, timeout=10)
    data = response.json()
    if not response.ok:
        errors = data.get("error", {}).get("errors") or [{}]
        reason = errors[0].get("reason")
        if reason == "channelNotFound":
            return []
        raise RuntimeError(f"YouTube API {response.status_code} {reason}: {data.get('error', {}).get('message')}")
    return data.get("items", [])  # no match: items is empty or missing


def channel_id_from_handle(handle):
    """'@GoogleDevelopers' or 'GoogleDevelopers' -> 'UC_x5XG1OV2P6uZZ5FSM9Ttw' or None"""
    items = list_channels(part="id", forHandle=handle.strip())
    return items[0]["id"] if items else None


def channel_id_from_username(username):
    """Legacy username from a youtube.com/user/<name> URL -> channel ID or None"""
    items = list_channels(part="id", forUsername=username.strip())
    return items[0]["id"] if items else None


def channels_by_id(ids):
    """Any number of channel IDs -> {id: {"title", "customUrl"}}; missing IDs were not found"""
    unique = list(dict.fromkeys(ids))
    found = {}
    for i in range(0, len(unique), 50):
        batch = unique[i:i + 50]
        for ch in list_channels(part="snippet", id=",".join(batch), maxResults=50):
            found[ch["id"]] = {
                "title": ch["snippet"]["title"],
                "customUrl": ch["snippet"].get("customUrl"),
            }
    return found

URL की किसी भी percent-encoding को डिकोड करने के बाद, हैंडल वैसे ही भेजें जैसे यूज़र ने लिखे। हैंडल में कई लिपियों के अक्षर हो सकते हैं, सिर्फ़ ASCII नहीं, और URLSearchParams व requests दोनों आपके लिए पैरामीटर एन्कोड कर देते हैं।

पुराने username और /c/ कस्टम URL

क्रिएटर के बायो और स्प्रेडशीट में पुराने लिंक अक्सर हैंडल से पहले वाले किसी रूप में होते हैं।

चैनल ID से हैंडल, एक बार में 50

उल्टी दिशा में id फ़िल्टर इस्तेमाल होता है, जो चैनल ID की कॉमा से अलग सूची लेता है। part=snippet माँगें और snippet.customUrl पढ़ें। ऊपर का channelsById ID से डुप्लिकेट हटाता है, उन्हें 50 के हिस्सों में भेजता है, और ID को key बनाकर एक map लौटाता है। जो ID map में नहीं दिखतीं, वे मिलीं नहीं, मिटा दी गईं या API को नहीं दिखतीं।

न मिलना, errors और कैशिंग

कुछ भी मेल न खाने वाली खोज error नहीं है। forHandle और forUsername के लिए जवाब में बस कोई item नहीं होता, इसलिए ख़ाली या ग़ायब items array की जाँच करें। channels.list के लिए दस्तावेज़ में दिए errors ये हैं:

Error का कारणHTTPकब होता हैक्या करें
200 जवाब में कोई items नहीं200हैंडल या username मौजूद नहींnull लौटाएँ; न मिलने को थोड़े समय के लिए कैश करें
channelNotFound404id पैरामीटर का कोई चैनल नहीं मिलान मिला मानें, जैसा ऊपर के हेल्पर करते हैं
invalidCriteria400अनुरोध में एक से ज़्यादा फ़िल्टरforHandle, forUsername या id में से ठीक एक भेजें
channelForbidden403चैनल इस अनुरोध को सपोर्ट नहीं करता या अनुरोध ठीक से अधिकृत नहींलॉग करें और चैनल छोड़ दें
quotaExceeded403आपके प्रोजेक्ट ने रोज़ का कोटा इस्तेमाल कर लियारुकें और Pacific Time की आधी रात के बाद दोबारा कोशिश करें, या ज़्यादा कोटा माँगें
badRequest (API key not valid)400key ग़ायब या ग़लतकॉन्फ़िगरेशन ठीक करें; दोबारा कोशिश न करें

कोटा और देरी बचाने के लिए नतीजे कैश करें, पर डेटा सेव करने के YouTube के नियमों का सम्मान करें। YouTube API Services Developer Policies (सेक्शन III.E.4) कहती हैं कि आप non-authorized API डेटा 30 कैलेंडर दिनों से ज़्यादा सेव नहीं कर सकते, उसके बाद उसे मिटाना या रिफ़्रेश करना होगा। छोटे अक्षरों वाले हैंडल को key बनाकर, 30 दिन से काफ़ी कम TTL वाला कैश ज़्यादातर फ़ायदा दे देता है:

Node.js कैश

import { channelIdFromHandle } from "./youtube.mjs";

const TTL_MS = 7 * 24 * 60 * 60 * 1000; // refresh well inside YouTube's 30-day limit
const cache = new Map(); // lowercase handle -> { id, at }

export async function cachedChannelId(handle) {
  const key = handle.trim().replace(/^@/, "").toLowerCase();
  const hit = cache.get(key);
  if (hit && Date.now() - hit.at < TTL_MS) return hit.id;
  const id = await channelIdFromHandle(handle);
  cache.set(key, { id, at: Date.now() }); // caches misses (null) too
  return id;
}

प्रोडक्शन में यही लॉजिक Redis में या expires_at कॉलम वाली डेटाबेस टेबल में रखें, और न मिलने वाले नतीजों को मिलने वालों से छोटा TTL दें ताकि नया बना हैंडल जल्दी पकड़ में आए।

API key के बिना विकल्प: चैनल पेज पढ़ना

पब्लिक चैनल पेज के HTML में चैनल ID होती है। https://www.youtube.com/@GoogleDevelopers का अनुरोध अभी ऐसा पेज लौटाता है जिसका <link rel="canonical"> https://www.youtube.com/channel/UC_x5XG1OV2P6uZZ5FSM9Ttw की ओर इशारा करता है, और यही /c/ और /user/ पेज पर भी होता है, इसलिए बिना अंदाज़े के किसी /c/ नाम को हल करने का यही एक तरीका है।

Node.js

// Unofficial fallback: reads the public channel page. Fragile, prefer the API.
export async function channelIdFromPage(handle) {
  const url = `https://www.youtube.com/@${encodeURIComponent(handle.replace(/^@/, ""))}`;
  const res = await fetch(url, {
    headers: { "Accept-Language": "en-US" },
    signal: AbortSignal.timeout(10_000),
  });
  if (!res.ok) return null;
  const html = await res.text();
  const match = html.match(/<link rel="canonical" href="https:\/\/www\.youtube\.com\/channel\/(UC[A-Za-z0-9_-]{22})"/);
  return match?.[1] ?? null;
}

Python

import re
from urllib.parse import quote

import requests

CANONICAL = re.compile(r'<link rel="canonical" href="https://www\.youtube\.com/channel/(UC[A-Za-z0-9_-]{22})"')


def channel_id_from_page(handle):
    """Unofficial fallback: reads the public channel page. Fragile, prefer the API."""
    url = "https://www.youtube.com/@" + quote(handle.lstrip("@"), safe="._-")
    response = requests.get(url, headers={"Accept-Language": "en-US"}, timeout=10)
    if not response.ok:
        return None
    match = CANONICAL.search(response.text)
    return match.group(1) if match else None

इसे कम से कम और सिर्फ़ वहीं इस्तेमाल करें जहाँ API मदद न कर सके। यह अनाधिकारिक और नाज़ुक है:

तरीकों की तुलना

तरीकाइनपुटकीमतफ़ायदेकमियाँ
forHandle के साथ channels.listहैंडल, @ के साथ या बिनाहर हैंडल पर 1 यूनिटआधिकारिक, सटीक मैच, किसी भी हैंडल पर काम करता हैएक कॉल में एक हैंडल
forUsername के साथ channels.listपुराना /user/ नामहर नाम पर 1 यूनिटआधिकारिक, सटीक मैचसिर्फ़ पुराने username के लिए
id के साथ channels.list50 तक चैनल IDहर 50 ID पर 1 यूनिटआधिकारिक, बैच में, टाइटल और customUrl लौटाता हैcustomUrl ग़ायब हो सकता है
search.listखुला टेक्स्टडिफ़ॉल्ट रूप से रोज़ 100 कॉल की अलग सीमानाम से चैनल खोजता हैरैंक किए अंदाज़े, सटीक नहीं; कम छूट
चैनल पेज का canonical लिंककोई भी चैनल URL, /c/ समेतकोई API कोटा नहींबिना key के काम करता है; /c/ नाम हल करता हैअनाधिकारिक, नाज़ुक, ब्लॉक हो सकता है, शर्तों से टकरा सकता है

ज़्यादातर प्रोडक्ट के लिए सही सेटअप आसान है: forHandle से हैंडल हल करें, चैनल ID सेव करें, id फ़िल्टर से 50 के बैच में हैंडल रिफ़्रेश करें, और कभी-कभार आने वाले /c/ लिंक के लिए पेज वाला विकल्प एक फ़्लैग के पीछे रखें। अगर आपको दूसरे प्लेटफ़ॉर्म पर क्रिएटर के अकाउंट भी चाहिए, तो sociallinks.dev एक API है जो एक Instagram, TikTok, YouTube या X प्रोफ़ाइल URL लेकर दूसरे प्लेटफ़ॉर्म पर क्रिएटर के अकाउंट लौटाता है, हर मैच के लिए भरोसे के स्कोर और सबूत के साथ।

अक्सर पूछे जाने वाले सवाल

क्या forHandle में हैंडल भेजते समय @ ज़रूरी है?

नहीं। channels.list का रेफ़रेंस कहता है कि forHandle के मान के आगे @ चिह्न लगाया जा सकता है, और उदाहरण के तौर पर GoogleDevelopers और @GoogleDevelopers दोनों देता है।

एक हैंडल खोज में कितना कोटा लगता है?

एक यूनिट। हर channels.list कॉल की कीमत 1 यूनिट है, और डिफ़ॉल्ट आवंटन रोज़ 10,000 यूनिट है, जो Pacific Time की आधी रात को रीसेट होता है। अमान्य अनुरोधों की कीमत भी कम से कम 1 यूनिट है।

क्या एक कॉल में कई हैंडल खोज सकते हैं?

नहीं। forHandle एक ही हैंडल लेता है। सिर्फ़ id फ़िल्टर कॉमा से अलग सूची लेता है, इसलिए उल्टी दिशा (ID से हैंडल) को बैच करें और सीधी दिशा को कैश करें।

क्या चैनल ID कभी बदलती है?

चैनल ID वह पहचानकर्ता है जिससे YouTube चैनल की यूनिक पहचान करता है। हैंडल या टाइटल बदलने से यह नहीं बदलती। इसीलिए यह सही primary key है। हैंडल, टाइटल और कस्टम URL सब बदल सकते हैं।

snippet.customUrl ख़ाली क्यों आता है?

हर चैनल के पास कस्टम URL नहीं होता, जैसा YouTube का अपना revision इतिहास बताता है। फ़ील्ड को वैकल्पिक मानें और उसकी जगह /channel/UC... URL दिखाएँ।

नतीजे कितने समय तक कैश कर सकते हैं?

YouTube की Developer Policies सेव किए API डेटा को मिटाने या रिफ़्रेश करने से पहले 30 कैलेंडर दिन तक सीमित करती हैं। मिलने वाले नतीजों के लिए एक हफ़्ता या उससे कम का TTL, और न मिलने वालों के लिए उससे भी कम, आपको उस सीमा के काफ़ी अंदर रखता है।

किसी क्रिएटर के अकाउंट खोजें