sociallinks.dev

Blog / Guide développeur

Comment obtenir l’ID d’une chaîne YouTube à partir d’un pseudo (et inversement)

Les créateurs partagent leur chaîne YouTube sous la forme @handle, mais presque tout dans l’API YouTube Data repose sur l’ID de chaîne. Si vous ne stockez que les pseudos, un changement de nom casse vos données ; si vous ne stockez que les ID, vous ne pouvez pas montrer aux utilisateurs le nom qu’ils reconnaissent. Ce guide montre comment convertir un pseudo en ID de chaîne en un seul appel d’API, comment repasser des ID aux pseudos par lots de 50, que faire des anciennes URL /user/ et /c/, et comment rester dans le quota par défaut.

Pseudos, ID de chaîne et anciens noms

Une chaîne YouTube peut être atteinte par jusqu’à quatre types d’identifiants. Un seul est garanti stable.

IdentifiantExempleForme d’URLRemarques
ID de chaîneUC_x5XG1OV2P6uZZ5FSM9Ttwyoutube.com/channel/UC...L’ID que YouTube utilise pour identifier la chaîne. Sensible à la casse. Utilisez-le comme clé primaire.
Pseudo (handle)@GoogleDevelopersyoutube.com/@GoogleDevelopersUnique, insensible à la casse, et le titulaire peut le changer dans YouTube Studio.
Ancien nom d’utilisateurGoogleDevelopersyoutube.com/user/GoogleDevelopersIssu de l’ancien système de noms d’utilisateur. Se résout avec le filtre forUsername de l’API.
Ancienne URL personnaliséeGoogleDevelopersyoutube.com/c/GoogleDevelopersAnciennes URL personnalisées, qui selon YouTube continuent de fonctionner. Aucun filtre de l’API ne les résout.

Stockez l’ID de chaîne comme clé et gardez le pseudo comme valeur d’affichage que vous mettez à jour. La suite de ce guide explique comment passer de ce que l’utilisateur vous a donné à cet ID.

Créer une clé API et comprendre le quota

  1. Step 1Créez un projet Google Cloud

    Dans la console Google Cloud, créez un projet ou choisissez-en un existant.

  2. Step 2Activez l’API YouTube Data v3

    Ouvrez API et services, trouvez YouTube Data API v3 dans la bibliothèque et activez-la pour le projet.

  3. Step 3Créez une clé API

    Dans Identifiants, créez une clé API. Les données publiques de chaîne, comme channels.list par pseudo ou par ID, ne demandent qu’une clé, pas OAuth. Limitez la clé à l’API YouTube Data.

  4. Step 4Gardez la clé hors de votre code

    Placez-la dans une variable d’environnement, par exemple YOUTUBE_API_KEY, et lisez-la à l’exécution. C’est exactement ce que font les exemples ci-dessous.

  5. Step 5Vérifiez votre quota

    Dans la console, la page Quotas de l’API affiche votre allocation quotidienne et votre consommation.

Les chiffres qui comptent, d’après la documentation de Google :

Le quota par défaut couvre donc environ 10 000 recherches de pseudos par jour, et jusqu’à 50 fois plus de recherches par ID, car un appel peut récupérer 50 chaînes par ID pour la même unité.

Du pseudo à l’ID de chaîne avec forHandle

channels.list accepte exactement un filtre. Le filtre forHandle prend un pseudo YouTube, et la référence indique que la valeur peut être précédée d’un symbole @ : GoogleDevelopers et @GoogleDevelopers fonctionnent tous les deux. Demandez part=id si vous n’avez besoin que de l’ID ; la réponse est plus petite pour le même coût.

cURL

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

L’exemple Node ci-dessous encapsule la requête dans une fonction utilitaire, listChannels, que le reste du guide réutilise. Elle renvoie une liste vide quand rien ne correspond et lève une exception sur les vraies erreurs, comme une clé invalide ou un quota épuisé.

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

Transmettez les pseudos tels que l’utilisateur les a écrits, après avoir décodé l’éventuel encodage en pourcentage de l’URL. Les pseudos peuvent utiliser des lettres de nombreux alphabets, pas seulement l’ASCII, et URLSearchParams comme requests encodent le paramètre pour vous.

Anciens noms d’utilisateur et URL personnalisées /c/

Les anciens liens dans les bios des créateurs et dans les tableurs utilisent souvent l’une des formes antérieures aux pseudos.

De l’ID de chaîne au pseudo, 50 à la fois

Dans l’autre sens, on utilise le filtre id, qui prend une liste d’ID de chaîne séparés par des virgules. Demandez part=snippet et lisez snippet.customUrl. channelsById ci-dessus dédoublonne les ID, les envoie par lots de 50 et renvoie une table indexée par ID. Les ID absents de la table n’ont pas été trouvés, ont été supprimés ou ne sont pas visibles par l’API.

Introuvables, erreurs et cache

Une recherche sans correspondance n’est pas une erreur. Pour forHandle et forUsername, la réponse ne contient simplement aucun élément : vérifiez donc si le tableau items est vide ou absent. Voici les erreurs documentées pour channels.list :

Raison de l’erreurHTTPQuand elle survientQue faire
Pas de items dans une réponse 200200Le pseudo ou le nom d’utilisateur n’existe pasRenvoyer null ; mettre l’échec en cache pour une courte durée
channelNotFound404Une chaîne du paramètre id est introuvableLa traiter comme introuvable, comme le font les fonctions ci-dessus
invalidCriteria400Plus d’un filtre dans la requêteEnvoyer un seul filtre parmi forHandle, forUsername ou id
channelForbidden403La chaîne ne prend pas en charge la requête, ou celle-ci n’est pas correctement autoriséeLa journaliser et ignorer la chaîne
quotaExceeded403Votre projet a épuisé son quota quotidienArrêter et réessayer après minuit, heure du Pacifique, ou demander plus de quota
badRequest (clé API invalide)400Clé manquante ou erronéeCorriger la configuration ; ne pas réessayer

Mettez les résultats en cache pour économiser du quota et de la latence, mais respectez les règles de YouTube sur le stockage des données. Les YouTube API Services Developer Policies (section III.E.4) indiquent que vous pouvez stocker les données d’API non autorisées pendant 30 jours calendaires au maximum, après quoi vous devez les supprimer ou les mettre à jour. Un cache avec une durée de vie bien inférieure à 30 jours, indexé par le pseudo en minuscules, apporte l’essentiel du gain :

Cache 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;
}

En production, placez la même logique dans Redis ou dans une table de base de données avec une colonne expires_at, et donnez aux échecs une durée de vie plus courte qu’aux réussites pour qu’un pseudo nouvellement créé soit vite pris en compte.

La solution sans clé API : lire la page de la chaîne

Les pages publiques des chaînes contiennent l’ID de chaîne dans leur HTML. Une requête vers https://www.youtube.com/@GoogleDevelopers renvoie actuellement une page dont le <link rel="canonical"> pointe vers https://www.youtube.com/channel/UC_x5XG1OV2P6uZZ5FSM9Ttw, et c’est aussi le cas des pages /c/ et /user/, ce qui en fait le seul moyen de résoudre un nom /c/ sans deviner.

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

Utilisez-la avec parcimonie, et seulement là où l’API ne peut pas aider. Elle est non officielle et fragile :

Comparaison des approches

ApprocheEntréeCoûtAvantagesInconvénients
channels.list avec forHandlePseudo, avec ou sans @1 unité par pseudoOfficielle, correspondance exacte, fonctionne pour tout pseudoUn pseudo par appel
channels.list avec forUsernameAncien nom /user/1 unité par nomOfficielle, correspondance exacteUniquement pour les anciens noms d’utilisateur
channels.list avec idJusqu’à 50 ID de chaîne1 unité pour 50 IDOfficielle, par lots, renvoie le titre et customUrlcustomUrl peut manquer
search.listTexte libreLimite distincte de 100 appels par jour par défautTrouve les chaînes par nomSuppositions classées, pas exactes ; faible allocation
Lien canonique de la page de la chaîneToute URL de chaîne, y compris /c/Aucun quota d’APIFonctionne sans clé ; résout les noms /c/Non officielle, fragile, peut être bloquée, peut contrevenir aux conditions

Pour la plupart des produits, la bonne configuration est simple : résolvez les pseudos avec forHandle, stockez l’ID de chaîne, mettez à jour les pseudos par lots de 50 avec le filtre id, et gardez la lecture de page derrière une option pour les rares liens /c/. S’il vous faut aussi les comptes du créateur sur d’autres plateformes, sociallinks.dev est une API qui prend une URL de profil Instagram, TikTok, YouTube ou X et renvoie les comptes du créateur sur les autres plateformes, avec un score de confiance et la preuve de chaque correspondance.

FAQ

Faut-il le @ quand je passe un pseudo à forHandle ?

Non. La référence de channels.list indique que la valeur de forHandle peut être précédée d’un symbole @, et donne en exemple à la fois GoogleDevelopers et @GoogleDevelopers.

Combien de quota consomme une recherche de pseudo ?

Une unité. Chaque appel channels.list coûte 1 unité, et l’allocation par défaut est de 10 000 unités par jour, réinitialisée à minuit, heure du Pacifique. Les requêtes invalides coûtent aussi au moins 1 unité.

Puis-je rechercher plusieurs pseudos en un seul appel ?

Non. forHandle prend un seul pseudo. Seul le filtre id accepte une liste séparée par des virgules : regroupez donc le sens inverse (des ID aux pseudos) en lots et mettez en cache le sens direct.

Un ID de chaîne change-t-il un jour ?

L’ID de chaîne est l’identifiant que YouTube utilise pour identifier la chaîne de façon unique. Changer le pseudo ou le titre ne le modifie pas. C’est pourquoi c’est la bonne clé primaire. Les pseudos, les titres et les URL personnalisées peuvent tous changer.

Pourquoi snippet.customUrl revient-il vide ?

Toutes les chaînes n’ont pas d’URL personnalisée, comme le note l’historique des révisions de YouTube. Traitez le champ comme facultatif et affichez à défaut l’URL /channel/UC....

Combien de temps puis-je garder les résultats en cache ?

Les Developer Policies de YouTube limitent le stockage des données d’API à 30 jours calendaires avant suppression ou mise à jour. Une durée de vie d’une semaine ou moins pour les réussites, et plus courte pour les échecs, vous garde largement dans cette limite.

Trouver les comptes d’un créateur