ब्लॉग / डेवलपर गाइड
हैंडल से YouTube चैनल ID कैसे पाएँ (और उल्टा भी)
क्रिएटर अपना YouTube चैनल @handle के रूप में शेयर करते हैं, पर YouTube Data API में लगभग सब कुछ चैनल ID से चलता है। अगर आप सिर्फ़ हैंडल सेव करते हैं, तो नाम बदलने पर आपके रिकॉर्ड टूट जाते हैं; अगर सिर्फ़ ID सेव करते हैं, तो यूज़र को वह नाम नहीं दिखा सकते जिसे वे पहचानते हैं। यह गाइड दिखाती है कि एक API कॉल से हैंडल को चैनल ID में कैसे बदलें, एक बार में 50 ID से वापस हैंडल कैसे पाएँ, पुराने /user/ और /c/ URL का क्या करें, और डिफ़ॉल्ट कोटा के अंदर कैसे रहें।
हैंडल, चैनल ID और पुराने नाम
किसी YouTube चैनल तक चार तरह के पहचानकर्ता (identifier) से पहुँचा जा सकता है। उनमें से सिर्फ़ एक के स्थिर रहने की गारंटी है।
| पहचानकर्ता | उदाहरण | URL का रूप | नोट |
|---|---|---|---|
| चैनल ID | UC_x5XG1OV2P6uZZ5FSM9Ttw | youtube.com/channel/UC... | वह ID जिससे YouTube चैनल की पहचान करता है। बड़े-छोटे अक्षर मायने रखते हैं। इसे अपनी primary key बनाएँ। |
| हैंडल | @GoogleDevelopers | youtube.com/@GoogleDevelopers | यूनिक, बड़े-छोटे अक्षर से फ़र्क नहीं पड़ता, और मालिक इसे YouTube Studio में बदल सकता है। |
| पुराना username | GoogleDevelopers | youtube.com/user/GoogleDevelopers | पुराने username सिस्टम से। API के forUsername फ़िल्टर से हल हो सकता है। |
| पुराना कस्टम URL | GoogleDevelopers | youtube.com/c/GoogleDevelopers | पुराने पर्सनलाइज़्ड URL, जो YouTube के अनुसार काम करते रहते हैं। कोई API फ़िल्टर इन्हें हल नहीं करता। |
चैनल ID को key के रूप में सेव करें और हैंडल को डिस्प्ले मान की तरह रखें जिसे आप रिफ़्रेश करते हैं। बाकी गाइड इस बारे में है कि यूज़र ने जो भी दिया, उससे उस ID तक कैसे पहुँचें।
API key बनाएँ और कोटा समझें
- Step 1Google Cloud प्रोजेक्ट बनाएँ
Google Cloud console में एक प्रोजेक्ट बनाएँ या मौजूदा प्रोजेक्ट चुनें।
- Step 2YouTube Data API v3 चालू करें
APIs and services खोलें, लाइब्रेरी में YouTube Data API v3 खोजें और प्रोजेक्ट के लिए उसे चालू करें।
- Step 3API key बनाएँ
Credentials में जाकर एक API key बनाएँ। हैंडल या ID से
channels.listजैसे पब्लिक चैनल डेटा के लिए सिर्फ़ key चाहिए, OAuth नहीं। key को YouTube Data API तक सीमित करें। - Step 4key को अपने कोड से बाहर रखें
इसे किसी environment variable में रखें, जैसे
YOUTUBE_API_KEY, और रनटाइम पर पढ़ें। नीचे के उदाहरण यही करते हैं। - Step 5अपना कोटा जाँचें
console में API का Quotas पेज आपका रोज़ का आवंटन और इस्तेमाल दिखाता है।
Google के दस्तावेज़ों से अहम संख्याएँ:
- एक
channels.listकॉल की कीमत 1 यूनिट है, फ़िल्टर चाहे जो हो। - प्रोजेक्ट को
search.listऔरvideos.insertके अलावा सभी endpoint के लिए रोज़ 10,000 यूनिट का डिफ़ॉल्ट आवंटन मिलता है; इन दोनों की अब अपनी अलग सीमा है, हर एक की रोज़ 100 कॉल। - हर अनुरोध की कीमत कम से कम 1 यूनिट है, अमान्य अनुरोधों समेत।
- रोज़ का कोटा Pacific Time की आधी रात को रीसेट होता है।
तो डिफ़ॉल्ट कोटा रोज़ लगभग 10,000 हैंडल खोज के लिए काफ़ी है, और ID खोज उससे 50 गुना तक, क्योंकि एक कॉल उसी एक यूनिट में ID से 50 चैनल ला सकती है।
forHandle से हैंडल को चैनल ID में बदलें
channels.list ठीक एक फ़िल्टर लेता है। forHandle फ़िल्टर एक YouTube हैंडल लेता है, और रेफ़रेंस कहता है कि मान के आगे @ चिह्न लगाया जा सकता है, इसलिए GoogleDevelopers और @GoogleDevelopers दोनों काम करते हैं। जब सिर्फ़ ID चाहिए तो part=id माँगें; जवाब छोटा होता है और कीमत वही।
curl -s "https://www.googleapis.com/youtube/v3/channels?part=id&forHandle=@GoogleDevelopers&key=$YOUTUBE_API_KEY"नीचे का Node उदाहरण अनुरोध को एक हेल्पर, listChannels, में लपेटता है, जिसे बाकी गाइड दोबारा इस्तेमाल करती है। कुछ मेल न खाए तो यह ख़ाली सूची लौटाता है और ग़लत key या ख़त्म कोटा जैसी असली गड़बड़ियों पर error फेंकता है।
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
}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 foundURL की किसी भी percent-encoding को डिकोड करने के बाद, हैंडल वैसे ही भेजें जैसे यूज़र ने लिखे। हैंडल में कई लिपियों के अक्षर हो सकते हैं, सिर्फ़ ASCII नहीं, और URLSearchParams व requests दोनों आपके लिए पैरामीटर एन्कोड कर देते हैं।
पुराने username और /c/ कस्टम URL
क्रिएटर के बायो और स्प्रेडशीट में पुराने लिंक अक्सर हैंडल से पहले वाले किसी रूप में होते हैं।
- `youtube.com/user/<name>`: ऊपर के
channelIdFromUsernameसे नाम कोforUsernameमें भेजें। किसी भी दूसरीchannels.listकॉल की तरह इसकी कीमत 1 यूनिट है। - `youtube.com/c/<name>`: कस्टम URL के लिए कोई
channels.listफ़िल्टर नहीं है। कई चैनल ने अपने पुराने कस्टम नाम से मेल खाता हैंडल चुना, इसलिए उसी स्ट्रिंग सेforHandleआज़माना काम कर सकता है, पर मैच मिलना इस बात का सबूत नहीं कि चैनल वही है। उस पर भरोसा करने से पहले किसी और तरीके से पक्का करें, जैसे टाइटल जाँचकर या पेज का canonical लिंक पढ़कर (नीचे बताया गया है)। - `search.list` किसी क्वेरी के लिए रैंक किए नतीजे देता है, सटीक खोज नहीं, और इसकी डिफ़ॉल्ट सीमा रोज़ 100 कॉल है। पहचानकर्ता हल करने के लिए इससे बचें।
चैनल ID से हैंडल, एक बार में 50
उल्टी दिशा में id फ़िल्टर इस्तेमाल होता है, जो चैनल ID की कॉमा से अलग सूची लेता है। part=snippet माँगें और snippet.customUrl पढ़ें। ऊपर का channelsById ID से डुप्लिकेट हटाता है, उन्हें 50 के हिस्सों में भेजता है, और ID को key बनाकर एक map लौटाता है। जो ID map में नहीं दिखतीं, वे मिलीं नहीं, मिटा दी गईं या API को नहीं दिखतीं।
- बैच का आकार। रेफ़रेंस
idको कॉमा से अलग सूची बताता है औरmaxResultsकी सीमा 50 रखता है। ID की संख्या के लिए अलग सीमा नहीं बताता, इसलिए कोड हर कॉल में 50 भेजता है औरmaxResults=50सेट करता है ताकि डिफ़ॉल्ट 5 कभी किसी बैच को न काटे। - क्रम। यह न मानें कि नतीजे उसी क्रम में आएँगे जिसमें आपने भेजे। जवाब को
idसे इंडेक्स करें। - `customUrl` वैकल्पिक है। रेफ़रेंस इसे सिर्फ़ चैनल का कस्टम URL बताता है, और 2016 का एक revision नोट कहता है कि हर चैनल के पास यह नहीं होता। इसे nullable मानें, आगे
@हो तो हटा दें, और इस पर कुछ बनाने से पहले कुछ जाने-पहचाने चैनल पर इसका फ़ॉर्मैट जाँच लें। - टाइटल हैंडल नहीं हैं।
snippet.titleडिस्प्ले नाम है। यह यूनिक नहीं है और इसमें स्पेस और इमोजी हो सकते हैं।
न मिलना, errors और कैशिंग
कुछ भी मेल न खाने वाली खोज error नहीं है। forHandle और forUsername के लिए जवाब में बस कोई item नहीं होता, इसलिए ख़ाली या ग़ायब items array की जाँच करें। channels.list के लिए दस्तावेज़ में दिए errors ये हैं:
| Error का कारण | HTTP | कब होता है | क्या करें |
|---|---|---|---|
200 जवाब में कोई items नहीं | 200 | हैंडल या username मौजूद नहीं | null लौटाएँ; न मिलने को थोड़े समय के लिए कैश करें |
channelNotFound | 404 | id पैरामीटर का कोई चैनल नहीं मिला | न मिला मानें, जैसा ऊपर के हेल्पर करते हैं |
invalidCriteria | 400 | अनुरोध में एक से ज़्यादा फ़िल्टर | forHandle, forUsername या id में से ठीक एक भेजें |
channelForbidden | 403 | चैनल इस अनुरोध को सपोर्ट नहीं करता या अनुरोध ठीक से अधिकृत नहीं | लॉग करें और चैनल छोड़ दें |
quotaExceeded | 403 | आपके प्रोजेक्ट ने रोज़ का कोटा इस्तेमाल कर लिया | रुकें और Pacific Time की आधी रात के बाद दोबारा कोशिश करें, या ज़्यादा कोटा माँगें |
badRequest (API key not valid) | 400 | key ग़ायब या ग़लत | कॉन्फ़िगरेशन ठीक करें; दोबारा कोशिश न करें |
कोटा और देरी बचाने के लिए नतीजे कैश करें, पर डेटा सेव करने के YouTube के नियमों का सम्मान करें। YouTube API Services Developer Policies (सेक्शन III.E.4) कहती हैं कि आप non-authorized API डेटा 30 कैलेंडर दिनों से ज़्यादा सेव नहीं कर सकते, उसके बाद उसे मिटाना या रिफ़्रेश करना होगा। छोटे अक्षरों वाले हैंडल को key बनाकर, 30 दिन से काफ़ी कम TTL वाला कैश ज़्यादातर फ़ायदा दे देता है:
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/ नाम को हल करने का यही एक तरीका है।
// 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;
}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 मदद न कर सके। यह अनाधिकारिक और नाज़ुक है:
- मार्कअप कोई दस्तावेज़ीकृत इंटरफ़ेस नहीं है और कभी भी बदल सकता है।
- YouTube ऑटोमेटेड अनुरोधों को, ख़ासकर क्लाउड IP रेंज से, consent पेज, rate limit या CAPTCHA दिखा सकता है। हमारी टेस्टिंग में क्लाउड सर्वर से एक
youtu.beलिंक फ़ॉलो करने पर Google के CAPTCHA पेज पर पहुँचे। - साइट तक ऑटोमेटेड पहुँच YouTube की Terms of Service से टकरा सकती है। उन्हें पढ़ें और आधिकारिक API को प्राथमिकता दें।
तरीकों की तुलना
| तरीका | इनपुट | कीमत | फ़ायदे | कमियाँ |
|---|---|---|---|---|
forHandle के साथ channels.list | हैंडल, @ के साथ या बिना | हर हैंडल पर 1 यूनिट | आधिकारिक, सटीक मैच, किसी भी हैंडल पर काम करता है | एक कॉल में एक हैंडल |
forUsername के साथ channels.list | पुराना /user/ नाम | हर नाम पर 1 यूनिट | आधिकारिक, सटीक मैच | सिर्फ़ पुराने username के लिए |
id के साथ channels.list | 50 तक चैनल 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, और न मिलने वालों के लिए उससे भी कम, आपको उस सीमा के काफ़ी अंदर रखता है।