Blog

YouTube Comments API: Guia Completo de Gerenciamento

Domine a YouTube Comments API com exemplos em TypeScript para buscar, publicar e moderar comentários. Inclui dicas de otimização de quota.

Por

+8

Poste em tudo. Uma API.

Try Free

A YouTube Comments API permite que desenvolvedores construam ferramentas poderosas de engajamento que buscam, publicam e moderam comentários programaticamente. Seja construindo um painel de mídia social, uma ferramenta de gerenciamento de comunidade ou um sistema de resposta automatizado, entender os recursos de comentários da YouTube Data API é essencial para criar integrações eficazes.

Este guia cobre tudo o que você precisa para implementar um gerenciamento robusto de comentários do YouTube em suas aplicações, desde a configuração de autenticação até estratégias de otimização de quota que mantêm sua integração funcionando sem problemas.

Arquitetura da YouTube Comments API

Introdução à YouTube Data API v3

A YouTube Data API v3 fornece acesso programático aos recursos principais do YouTube, incluindo uploads de vídeos, gerenciamento de playlists e operações de comentários. Para gerenciamento de comentários especificamente, a API expõe dois recursos principais:

  • CommentThreads: Comentários de nível superior em vídeos, incluindo metadados sobre contagem de respostas
  • Comments: Comentários individuais, incluindo respostas a comentários de nível superior

A API segue convenções RESTful e retorna respostas JSON. Todas as operações de comentários requerem autenticação OAuth 2.0, pois envolvem dados e ações específicas do usuário.

// 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 Autenticação OAuth 2.0

As operações de comentários do YouTube requerem diferentes níveis de autenticação dependendo da ação:

OperaçãoAPI KeyOAuth 2.0
Ler comentários públicos
Ler comentários em vídeos próprios
Publicar comentários
Responder a comentários
Excluir comentários
Moderar comentários

Para qualquer operação de escrita ou acesso a dados privados, você deve usar OAuth 2.0. Veja como configurar o fluxo 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: O escopo youtube.force-ssl é necessário para operações de comentários. Sem ele, requisições de escrita falharão com um erro 403.

Entendendo a Quota da YouTube API (10.000 Unidades/Dia)

O sistema de quota da YouTube API é um dos conceitos mais importantes para entender antes de construir qualquer integração. Cada requisição à API consome unidades de quota, e você está limitado a 10.000 unidades por dia por padrão.

Aqui está o detalhamento do custo de quota para operações relacionadas a comentários:

OperaçãoCusto de Quota
commentThreads.list1 unidade
comments.list1 unidade
commentThreads.insert50 unidades
comments.insert (resposta)50 unidades
comments.delete50 unidades
comments.update50 unidades
comments.setModerationStatus50 unidades

Com 10.000 unidades por dia, você pode realizar aproximadamente:

  • 10.000 operações de leitura, OU
  • 200 operações de escrita, OU
  • Uma combinação 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: A quota é resetada à meia-noite no horário do Pacífico (PT). Se você atingir os limites de quota, suas requisições falharão até o reset. Planeje suas operações adequadamente.

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 a diferença entre esses dois recursos é crucial para um gerenciamento eficiente de comentários do YouTube.

CommentThreads representam comentários de nível superior em um vídeo. Cada thread contém:

  • O comentário original (topLevelComment)
  • Contagem de respostas
  • Opcionalmente, um subconjunto de respostas

Comments representam comentários individuais, que podem ser:

  • Comentários de nível superior (acessados via CommentThreads)
  • Respostas a comentários de nível superior

Use CommentThreads quando você quiser:

  • Buscar todos os comentários de nível superior em um vídeo
  • Obter contagens de comentários e informações básicas de respostas
  • Publicar um novo comentário de nível superior

Use Comments quando você quiser:

  • Buscar todas as respostas a um comentário específico
  • Publicar uma resposta a um comentário existente
  • Atualizar ou excluir um comentário 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[];
  };
}

Buscando Comentários de Vídeos com a YouTube Comments API

Recuperar comentários é a operação mais comum. Aqui está uma implementação completa que lida com paginação e decodificação 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 Respostas a Comentários

Responder a comentários requer o endpoint comments.insert com o ID do comentário pai:

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

Operações de Moderação de Comentários

O YouTube fornece capacidades de moderação para proprietários de canais gerenciarem comentários em seus vídeos:

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

interface ModerationResult {
  succes

Uma API. 13+ plataformas.

Integre redes sociais em minutos, não semanas.

Criado para desenvolvedores. Adorado por agências. Confiado por 6.325 usuários.