La YouTube Comments API consente agli sviluppatori di creare potenti strumenti di engagement che recuperano, pubblicano e moderano commenti in modo programmatico. Che tu stia costruendo una dashboard per social media, uno strumento di gestione della community o un sistema di risposta automatizzato, comprendere le risorse per i commenti della YouTube Data API è essenziale per creare integrazioni efficaci.
Questa guida copre tutto ciò di cui hai bisogno per implementare una robusta gestione dei commenti YouTube nelle tue applicazioni, dalla configurazione dell'autenticazione alle strategie di ottimizzazione della quota che mantengono la tua integrazione funzionante senza problemi.

Introduzione alla YouTube Data API v3
La YouTube Data API v3 fornisce accesso programmatico alle funzionalità principali di YouTube, inclusi caricamenti video, gestione delle playlist e operazioni sui commenti. Per la gestione dei commenti in particolare, l'API espone due risorse principali:
- CommentThreads: Commenti di primo livello sui video, inclusi metadati sul conteggio delle risposte
- Comments: Commenti individuali, incluse le risposte ai commenti di primo livello
L'API segue le convenzioni RESTful e restituisce risposte JSON. Tutte le operazioni sui commenti richiedono l'autenticazione OAuth 2.0 poiché coinvolgono dati e azioni specifici dell'utente.
// 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 Autenticazione OAuth 2.0
Le operazioni sui commenti di YouTube richiedono diversi livelli di autenticazione a seconda dell'azione:
| Operazione | API Key | OAuth 2.0 |
|---|---|---|
| Leggere commenti pubblici | ✅ | ✅ |
| Leggere commenti sui propri video | ❌ | ✅ |
| Pubblicare commenti | ❌ | ✅ |
| Rispondere ai commenti | ❌ | ✅ |
| Eliminare commenti | ❌ | ✅ |
| Moderare commenti | ❌ | ✅ |
Per qualsiasi operazione di scrittura o accesso a dati privati, devi utilizzare OAuth 2.0. Ecco come configurare il flusso 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: Lo scope
youtube.force-sslè richiesto per le operazioni sui commenti. Senza di esso, le richieste di scrittura falliranno con un errore 403.
Comprendere la Quota dell'API YouTube (10.000 Unità/Giorno)
Il sistema di quota dell'API YouTube è uno dei concetti più importanti da comprendere prima di costruire qualsiasi integrazione. Ogni richiesta API consuma unità di quota, e sei limitato a 10.000 unità al giorno per impostazione predefinita.
Ecco la ripartizione dei costi di quota per le operazioni relative ai commenti:
| Operazione | Costo Quota |
|---|---|
| commentThreads.list | 1 unità |
| comments.list | 1 unità |
| commentThreads.insert | 50 unità |
| comments.insert (risposta) | 50 unità |
| comments.delete | 50 unità |
| comments.update | 50 unità |
| comments.setModerationStatus | 50 unità |
Con 10.000 unità al giorno, puoi eseguire approssimativamente:
- 10.000 operazioni di lettura, OPPURE
- 200 operazioni di scrittura, OPPURE
- Una combinazione di entrambe
// 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 quota si resetta a mezzanotte Pacific Time (PT). Se raggiungi i limiti di quota, le tue richieste falliranno fino al reset. Pianifica le tue operazioni di conseguenza.
Risorse CommentThreads vs Comments
Comprendere la differenza tra queste due risorse è cruciale per una gestione efficiente dei commenti YouTube.
CommentThreads rappresentano i commenti di primo livello su un video. Ogni thread contiene:
- Il commento originale (topLevelComment)
- Conteggio delle risposte
- Opzionalmente, un sottoinsieme di risposte
Comments rappresentano commenti individuali, che possono essere:
- Commenti di primo livello (accessibili tramite CommentThreads)
- Risposte ai commenti di primo livello
Usa CommentThreads quando vuoi:
- Recuperare tutti i commenti di primo livello su un video
- Ottenere conteggi dei commenti e informazioni di base sulle risposte
- Pubblicare un nuovo commento di primo livello
Usa Comments quando vuoi:
- Recuperare tutte le risposte a un commento specifico
- Pubblicare una risposta a un commento esistente
- Aggiornare o eliminare un commento specifico
// 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[];
};
}
Recuperare i Commenti dei Video con la YouTube Comments API
Recuperare i commenti è l'operazione più comune. Ecco un'implementazione completa che gestisce la paginazione e la decodifica delle entità HTML:
// Helper to decode HTML entities from YouTube API responses
function decodeHtmlEntities(text: string): string {
if (!text) return text;
return text
.replace(/'/g, "'")
.replace(/"/g, '"')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/'/g, "'")
.replace(///g, '/')
.replace(/ /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,
},
};
}
Pubblicare Risposte ai Commenti
Rispondere ai commenti richiede l'endpoint comments.insert con l'ID del commento 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('I commenti sono disabilitati su questo video');
}
if (errorBody.includes('forbidden')) {
throw new Error('Non hai il permesso di commentare su questo video');
}
}
if (response.status === 400) {
if (errorBody.includes('processingFailure')) {
throw new Error('Elaborazione del commento fallita. Il commento potrebbe contenere contenuti proibiti.');
}
}
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 };
}
Operazioni di Moderazione dei Commenti
YouTube fornisce funzionalità di moderazione per i proprietari dei canali per gestire i commenti sui loro video:
type ModerationStatus = 'heldForReview' | 'published' | 'rejected';
interface ModerationResult {
succes