How to Scrape YouTube Comments in JavaScript
Scraping YouTube comments is annoying. The page loads them dynamically, pagination is tied to scroll events, and replies are a separate request entirely. The official API has a comments endpoint, but it's quota-limited and the data model is clunky.
This post shows how to pull comments — with pagination and replies — using the Stophy API. One POST request gets you a page of comments with full metadata. Continuation tokens handle the rest.
Setup
Node.js 18+ (fetch is built in) and a Stophy API key from stophy.dev/dashboard.
Fetch the first page
1async function getComments(videoUrl, sortBy = 'top') {2 const res = await fetch('https://api.stophy.dev/v1/video', {3 method: 'POST',4 headers: {5 'Authorization': `Bearer ${process.env.STOPHY_API_KEY}`,6 'Content-Type': 'application/json',7 },8 body: JSON.stringify({ videoUrl, type: 'comments', sortBy }),9 });1011 if (!res.ok) throw new Error(`Request failed: ${res.status}`);12 const { data } = await res.json();13 return data;14}1516const data = await getComments('https://www.youtube.com/watch?v=YOUR_VIDEO_ID');1718for (const comment of data.items) {19 console.log(`${comment.author}: ${comment.text}`);20}sortBy takes "top" or "latest". Each comment has author, text, likeCount, replyCount, publishedAt, isPinned, isHearted, isChannelOwner, and a repliesToken for fetching replies.
Paginate through all comments
If data.continuationToken is set, there are more pages. Pass it back in the next request:
1async function getAllComments(videoUrl, sortBy = 'top') {2 const headers = {3 'Authorization': `Bearer ${process.env.STOPHY_API_KEY}`,4 'Content-Type': 'application/json',5 };6 const all = [];7 let token = null;89 while (true) {10 const body = { videoUrl, type: 'comments', sortBy };11 if (token) body.continuationToken = token;1213 const res = await fetch('https://api.stophy.dev/v1/video', {14 method: 'POST',15 headers,16 body: JSON.stringify(body),17 });1819 if (!res.ok) throw new Error(`Request failed: ${res.status}`);20 const { data } = await res.json();2122 all.push(...data.items);23 token = data.continuationToken ?? null;24 if (!token) break;25 }2627 return all;28}Fetch replies
Each top-level comment has a repliesToken. Pass it as continuationToken with type: 'replies':
1async function getReplies(repliesToken) {2 const res = await fetch('https://api.stophy.dev/v1/video', {3 method: 'POST',4 headers: {5 'Authorization': `Bearer ${process.env.STOPHY_API_KEY}`,6 'Content-Type': 'application/json',7 },8 body: JSON.stringify({ type: 'replies', continuationToken: repliesToken }),9 });1011 if (!res.ok) throw new Error(`Request failed: ${res.status}`);12 const { data } = await res.json();13 return data.items;14}Full script: export top 200 comments to CSV
1import { writeFileSync } from 'node:fs';23async function fetchComments(videoUrl, max = 200) {4 const headers = {5 'Authorization': `Bearer ${process.env.STOPHY_API_KEY}`,6 'Content-Type': 'application/json',7 };8 const results = [];9 let token = null;1011 while (results.length < max) {12 const body = { videoUrl, type: 'comments', sortBy: 'top' };13 if (token) body.continuationToken = token;1415 const res = await fetch('https://api.stophy.dev/v1/video', {16 method: 'POST',17 headers,18 body: JSON.stringify(body),19 });2021 if (!res.ok) throw new Error(`Request failed: ${res.status}`);22 const { data } = await res.json();2324 results.push(...data.items);25 token = data.continuationToken ?? null;26 if (!token) break;27 }2829 return results.slice(0, max);30}3132function toCSV(comments) {33 const fields = ['author', 'text', 'likeCount', 'replyCount', 'publishedAt', 'isPinned'];34 const header = fields.join(',');35 const rows = comments.map(c =>36 fields.map(f => JSON.stringify(c[f] ?? '')).join(',')37 );38 return [header, ...rows].join('\n');39}4041const comments = await fetchComments('https://www.youtube.com/watch?v=YOUR_VIDEO_ID', 200);42writeFileSync('comments.csv', toCSV(comments));43console.log(`Saved ${comments.length} comments`);1STOPHY_API_KEY=your_key node --input-type=module script.jsThe docs have the full reference.