Blog

YouTube Comments API: Guía Completa de Gestión

Domina la YouTube Comments API con ejemplos en TypeScript para obtener, publicar y moderar comentarios. Incluye consejos de optimización de cuota.

Por

+8

Publica en todo. Una API.

Try Free

La YouTube Comments API permite a los desarrolladores crear potentes herramientas de engagement que obtienen, publican y moderan comentarios de forma programática. Ya sea que estés construyendo un panel de redes sociales, una herramienta de gestión de comunidad o un sistema de respuesta automatizado, comprender los recursos de comentarios de la YouTube Data API es esencial para crear integraciones efectivas.

Esta guía cubre todo lo que necesitas para implementar una gestión robusta de comentarios de YouTube en tus aplicaciones, desde la configuración de autenticación hasta estrategias de optimización de cuota que mantienen tu integración funcionando sin problemas.

Arquitectura de YouTube Comments API

Introducción a YouTube Data API v3

La YouTube Data API v3 proporciona acceso programático a las funciones principales de YouTube, incluyendo subida de videos, gestión de listas de reproducción y operaciones de comentarios. Para la gestión de comentarios específicamente, la API expone dos recursos principales:

  • CommentThreads: Comentarios de nivel superior en videos, incluyendo metadatos sobre el conteo de respuestas
  • Comments: Comentarios individuales, incluyendo respuestas a comentarios de nivel superior

La API sigue convenciones RESTful y devuelve respuestas JSON. Todas las operaciones de comentarios requieren autenticación OAuth 2.0 ya que involucran datos y acciones específicas del usuario.

// Base configuration for YouTube API requests
const YOUTUBE_API_BASE = 'https://www.googleapis.com/youtube/v3';

interface YouTubeApiConfig {
  accessToken: string;
  baseUrl: string;
}

const config: YouTubeApiConfig = {
  accessToken: process.env.YOUTUBE_ACCESS_TOKEN || '',
  baseUrl: YOUTUBE_API_BASE,
};

API Key vs Autenticación OAuth 2.0

Las operaciones de comentarios de YouTube requieren diferentes niveles de autenticación dependiendo de la acción:

OperaciónAPI KeyOAuth 2.0
Leer comentarios públicos
Leer comentarios en videos propios
Publicar comentarios
Responder a comentarios
Eliminar comentarios
Moderar comentarios

Para cualquier operación de escritura o acceso a datos privados, debes usar OAuth 2.0. Aquí te mostramos cómo configurar el flujo OAuth:

interface OAuthConfig {
  clientId: string;
  clientSecret: string;
  redirectUri: string;
  scopes: string[];
}

const oauthConfig: OAuthConfig = {
  clientId: process.env.YOUTUBE_CLIENT_ID || '',
  clientSecret: process.env.YOUTUBE_CLIENT_SECRET || '',
  redirectUri: 'https://yourapp.com/auth/youtube/callback',
  scopes: [
    'https://www.googleapis.com/auth/youtube.force-ssl',
    'https://www.googleapis.com/auth/youtube',
  ],
};

function getAuthUrl(state?: string): string {
  const params = new URLSearchParams({
    client_id: oauthConfig.clientId,
    redirect_uri: oauthConfig.redirectUri,
    scope: oauthConfig.scopes.join(' '),
    response_type: 'code',
    access_type: 'offline',
    prompt: 'consent',
  });

  if (state) {
    params.append('state', state);
  }

  return `https://accounts.google.com/o/oauth2/auth?${params.toString()}`;
}

async function exchangeCodeForToken(code: string): Promise<{
  access_token: string;
  refresh_token: string;
  expires_in: number;
}> {
  const response = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      code,
      client_id: oauthConfig.clientId,
      client_secret: oauthConfig.clientSecret,
      redirect_uri: oauthConfig.redirectUri,
      grant_type: 'authorization_code',
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Token exchange failed: ${response.status} ${errorText}`);
  }

  return response.json();
}

Nota: El scope youtube.force-ssl es requerido para operaciones de comentarios. Sin él, las solicitudes de escritura fallarán con un error 403.

Entendiendo la Cuota de YouTube API (10,000 Unidades/Día)

El sistema de cuota de YouTube API es uno de los conceptos más importantes a entender antes de construir cualquier integración. Cada solicitud a la API consume unidades de cuota, y estás limitado a 10,000 unidades por día por defecto.

Aquí está el desglose del costo de cuota para operaciones relacionadas con comentarios:

OperaciónCosto de Cuota
commentThreads.list1 unidad
comments.list1 unidad
commentThreads.insert50 unidades
comments.insert (respuesta)50 unidades
comments.delete50 unidades
comments.update50 unidades
comments.setModerationStatus50 unidades

Con 10,000 unidades por día, puedes realizar aproximadamente:

  • 10,000 operaciones de lectura, O
  • 200 operaciones de escritura, O
  • Una combinación de ambas
// Quota tracking utility
interface QuotaTracker {
  used: number;
  limit: number;
  operations: Map;
}

const quotaCosts: Record = {
  'commentThreads.list': 1,
  'comments.list': 1,
  'commentThreads.insert': 50,
  'comments.insert': 50,
  'comments.delete': 50,
  'comments.update': 50,
  'comments.setModerationStatus': 50,
};

function createQuotaTracker(dailyLimit: number = 10000): QuotaTracker {
  return {
    used: 0,
    limit: dailyLimit,
    operations: new Map(),
  };
}

function trackQuotaUsage(
  tracker: QuotaTracker,
  operation: string
): { allowed: boolean; remaining: number } {
  const cost = quotaCosts[operation] || 1;
  
  if (tracker.used + cost > tracker.limit) {
    return { allowed: false, remaining: tracker.limit - tracker.used };
  }
  
  tracker.used += cost;
  const currentCount = tracker.operations.get(operation) || 0;
  tracker.operations.set(operation, currentCount + 1);
  
  return { allowed: true, remaining: tracker.limit - tracker.used };
}

Nota: La cuota se reinicia a medianoche hora del Pacífico (PT). Si alcanzas los límites de cuota, tus solicitudes fallarán hasta el reinicio. Planifica tus operaciones en consecuencia.

Build faster with Late

One API call to post everywhere. No OAuth headaches. No platform-specific code.

Free tier • No credit card • 99.97% uptime

Recursos CommentThreads vs Comments

Entender la diferencia entre estos dos recursos es crucial para una gestión eficiente de comentarios de YouTube.

CommentThreads representan comentarios de nivel superior en un video. Cada hilo contiene:

  • El comentario original (topLevelComment)
  • Conteo de respuestas
  • Opcionalmente, un subconjunto de respuestas

Comments representan comentarios individuales, que pueden ser:

  • Comentarios de nivel superior (accedidos vía CommentThreads)
  • Respuestas a comentarios de nivel superior

Usa CommentThreads cuando quieras:

  • Obtener todos los comentarios de nivel superior en un video
  • Obtener conteos de comentarios e información básica de respuestas
  • Publicar un nuevo comentario de nivel superior

Usa Comments cuando quieras:

  • Obtener todas las respuestas a un comentario específico
  • Publicar una respuesta a un comentario existente
  • Actualizar o eliminar un comentario específico
// Type definitions for YouTube comment structures
interface YouTubeCommentSnippet {
  authorDisplayName: string;
  authorProfileImageUrl: string;
  authorChannelId: { value: string };
  textDisplay: string;
  textOriginal: string;
  likeCount: number;
  publishedAt: string;
  updatedAt: string;
}

interface YouTubeComment {
  id: string;
  snippet: YouTubeCommentSnippet;
}

interface YouTubeCommentThread {
  id: string;
  snippet: {
    videoId: string;
    topLevelComment: YouTubeComment;
    totalReplyCount: number;
    isPublic: boolean;
  };
  replies?: {
    comments: YouTubeComment[];
  };
}

Obteniendo Comentarios de Video con YouTube Comments API

Recuperar comentarios es la operación más común. Aquí hay una implementación completa que maneja paginación y decodificación de entidades HTML:

// Helper to decode HTML entities from YouTube API responses
function decodeHtmlEntities(text: string): string {
  if (!text) return text;
  return text
    .replace(/&#39;/g, "'")
    .replace(/&quot;/g, '"')
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&#x27;/g, "'")
    .replace(/&#x2F;/g, '/')
    .replace(/&nbsp;/g, ' ');
}

interface CommentData {
  commentId: string;
  comment: string;
  created: string;
  from: {
    id: string;
    name: string;
    picture: string;
  };
  likeCount: number;
  replyCount: number;
  platform: 'youtube';
  platformPostId: string;
  replies: CommentData[];
}

interface GetCommentsOptions {
  limit?: number;
  pageToken?: string;
}

interface GetCommentsResult {
  comments: CommentData[];
  pagination: {
    hasMore: boolean;
    pageToken?: string;
  };
}

async function getVideoComments(
  accessToken: string,
  videoId: string,
  options: GetCommentsOptions = {}
): Promise {
  const maxResults = Math.min(options.limit || 25, 100);
  
  const params = new URLSearchParams({
    part: 'snippet,replies',
    videoId: videoId,
    maxResults: maxResults.toString(),
  });

  if (options.pageToken) {
    params.append('pageToken', options.pageToken);
  }

  const response = await fetch(
    `${YOUTUBE_API_BASE}/commentThreads?${params.toString()}`,
    {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    }
  );

  if (!response.ok) {
    const errorBody = await response.text();
    throw new Error(`Failed to fetch comments: ${response.status} ${errorBody}`);
  }

  const data = await response.json();

  const comments: CommentData[] = (data.items || []).map(
    (thread: YouTubeCommentThread) => {
      const topComment = thread.snippet.topLevelComment.snippet;
      
      const replies: CommentData[] = (thread.replies?.comments || []).map(
        (reply: YouTubeComment) => ({
          commentId: reply.id,
          comment: decodeHtmlEntities(reply.snippet.textDisplay),
          created: reply.snippet.publishedAt,
          from: {
            id: reply.snippet.authorChannelId?.value || '',
            name: reply.snippet.authorDisplayName,
            picture: reply.snippet.authorProfileImageUrl,
          },
          likeCount: reply.snippet.likeCount || 0,
          replyCount: 0,
          platform: 'youtube' as const,
          platformPostId: thread.snippet.videoId,
          replies: [],
        })
      );

      return {
        commentId: thread.snippet.topLevelComment.id,
        comment: decodeHtmlEntities(topComment.textDisplay),
        created: topComment.publishedAt,
        from: {
          id: topComment.authorChannelId?.value || '',
          name: topComment.authorDisplayName,
          picture: topComment.authorProfileImageUrl,
        },
        likeCount: topComment.likeCount || 0,
        replyCount: thread.snippet.totalReplyCount || 0,
        platform: 'youtube' as const,
        platformPostId: thread.snippet.videoId,
        replies,
      };
    }
  );

  return {
    comments,
    pagination: {
      hasMore: !!data.nextPageToken,
      pageToken: data.nextPageToken,
    },
  };
}

Publicando Respuestas a Comentarios

Responder a comentarios requiere el endpoint comments.insert con el ID del comentario padre:

interface ReplyResult {
  replyId: string;
  text: string;
  publishedAt: string;
}

async function replyToComment(
  accessToken: string,
  parentCommentId: string,
  text: string
): Promise {
  const response = await fetch(
    `${YOUTUBE_API_BASE}/comments?part=snippet`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${accessToken}`,
      },
      body: JSON.stringify({
        snippet: {
          parentId: parentCommentId,
          textOriginal: text,
        },
      }),
    }
  );

  if (!response.ok) {
    const errorBody = await response.text();
    
    // Handle specific error cases
    if (response.status === 403) {
      if (errorBody.includes('commentsDisabled')) {
        throw new Error('Comments are disabled on this video');
      }
      if (errorBody.includes('forbidden')) {
        throw new Error('You do not have permission to comment on this video');
      }
    }
    
    if (response.status === 400) {
      if (errorBody.includes('processingFailure')) {
        throw new Error('Comment processing failed. The comment may contain prohibited content.');
      }
    }
    
    throw new Error(`Failed to post reply: ${response.status} ${errorBody}`);
  }

  const data = await response.json();
  
  return {
    replyId: data.id,
    text: data.snippet.textDisplay,
    publishedAt: data.snippet.publishedAt,
  };
}

// Post a new top-level comment on a video
async function postComment(
  accessToken: string,
  videoId: string,
  text: string
): Promise<{ commentId: string }> {
  const response = await fetch(
    `${YOUTUBE_API_BASE}/commentThreads?part=snippet`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${accessToken}`,
      },
      body: JSON.stringify({
        snippet: {
          videoId,
          topLevelComment: {
            snippet: {
              textOriginal: text,
            },
          },
        },
      }),
    }
  );

  if (!response.ok) {
    const errorBody = await response.text();
    throw new Error(`Failed to post comment: ${response.status} ${errorBody}`);
  }

  const data = await response.json();
  return { commentId: data.id };
}

Operaciones de Moderación de Comentarios

YouTube proporciona capacidades de moderación para que los propietarios de canales gestionen los comentarios en sus videos:

type ModerationStatus = 'heldForReview' | 'published' | 'rejected';

interface ModerationResult {
  succes

Una API. 13+ plataformas.

Integra redes sociales en minutos, no semanas.

Diseñada para desarrolladores. Usada por agencias. Más de 6,325 usuarios.