How to Build a YouTube Agent with LangChain in JavaScript
Agents need data to be useful. If you're building one that works with YouTube content, you need a way to give it access to transcripts, comments, and search results without writing a scraper or managing quota limits.
This post shows how to add YouTube tools to a LangChain agent using Stophy. The agent gets three tools — fetch a transcript, pull comments, search YouTube — and can call them as needed based on the user's question.
Setup
1npm install @langchain/core @langchain/openai langchainYou need a Stophy API key from stophy.dev/dashboard and an OpenAI API key.
Build the tools
Start with a shared fetch helper so you're not repeating headers:
1async function stophy(body) {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(body),9 });10 if (!res.ok) throw new Error(`Stophy request failed: ${res.status}`);11 const { data } = await res.json();12 return data;13}Now the three tools:
1import { DynamicTool } from '@langchain/core/tools';23const transcriptTool = new DynamicTool({4 name: 'get_transcript',5 description: 'Get the full transcript of a YouTube video. Input is a YouTube video URL.',6 func: async (videoUrl) => {7 const data = await stophy({ videoUrl, type: 'transcript' });8 if ('empty' in data) return 'No transcript available for this video.';9 return data.text;10 },11});1213const commentsTool = new DynamicTool({14 name: 'get_comments',15 description: 'Get the top comments from a YouTube video. Input is a YouTube video URL.',16 func: async (videoUrl) => {17 const data = await stophy({ videoUrl, type: 'comments', sortBy: 'top' });18 return data.items19 .slice(0, 20)20 .map((c) => `${c.author}: ${c.text}`)21 .join('\n');22 },23});2425const searchTool = new DynamicTool({26 name: 'search_youtube',27 description: 'Search YouTube for videos. Input is a search query string.',28 func: async (q) => {29 const res = await fetch('https://api.stophy.dev/v1/search', {30 method: 'POST',31 headers: {32 'Authorization': `Bearer ${process.env.STOPHY_API_KEY}`,33 'Content-Type': 'application/json',34 },35 body: JSON.stringify({ q }),36 });37 if (!res.ok) throw new Error(`Search failed: ${res.status}`);38 const { data } = await res.json();39 return data.items40 .slice(0, 5)41 .map((v) => `${v.title} — ${v.videoUrl}`)42 .join('\n');43 },44});Wire up the agent
1import { ChatOpenAI } from '@langchain/openai';2import { AgentExecutor, createOpenAIFunctionsAgent } from 'langchain/agents';3import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';45const tools = [transcriptTool, commentsTool, searchTool];67const prompt = ChatPromptTemplate.fromMessages([8 ['system', 'You are a research assistant with access to YouTube data. Use the tools to answer questions about videos, channels, and topics.'],9 ['human', '{input}'],10 new MessagesPlaceholder('agent_scratchpad'),11]);1213const llm = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });14const agent = await createOpenAIFunctionsAgent({ llm, tools, prompt });15const executor = new AgentExecutor({ agent, tools });Run it
1const result = await executor.invoke({2 input: 'Summarize this video and tell me what the top comments say: https://www.youtube.com/watch?v=YOUR_VIDEO_ID',3});45console.log(result.output);The agent calls get_transcript, reads the content, then calls get_comments for audience reaction, and combines them into a summary. You don't have to orchestrate any of that — it figures out what to call based on the question.
Full script
1import { DynamicTool } from '@langchain/core/tools';2import { ChatOpenAI } from '@langchain/openai';3import { AgentExecutor, createOpenAIFunctionsAgent } from 'langchain/agents';4import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';56async function stophy(body) {7 const res = await fetch('https://api.stophy.dev/v1/video', {8 method: 'POST',9 headers: {10 'Authorization': `Bearer ${process.env.STOPHY_API_KEY}`,11 'Content-Type': 'application/json',12 },13 body: JSON.stringify(body),14 });15 if (!res.ok) throw new Error(`Stophy request failed: ${res.status}`);16 const { data } = await res.json();17 return data;18}1920const tools = [21 new DynamicTool({22 name: 'get_transcript',23 description: 'Get the full transcript of a YouTube video. Input is a YouTube video URL.',24 func: async (videoUrl) => {25 const data = await stophy({ videoUrl, type: 'transcript' });26 if ('empty' in data) return 'No transcript available for this video.';27 return data.text;28 },29 }),30 new DynamicTool({31 name: 'get_comments',32 description: 'Get the top comments from a YouTube video. Input is a YouTube video URL.',33 func: async (videoUrl) => {34 const data = await stophy({ videoUrl, type: 'comments', sortBy: 'top' });35 return data.items.slice(0, 20).map((c) => `${c.author}: ${c.text}`).join('\n');36 },37 }),38 new DynamicTool({39 name: 'search_youtube',40 description: 'Search YouTube for videos. Input is a search query string.',41 func: async (q) => {42 const res = await fetch('https://api.stophy.dev/v1/search', {43 method: 'POST',44 headers: {45 'Authorization': `Bearer ${process.env.STOPHY_API_KEY}`,46 'Content-Type': 'application/json',47 },48 body: JSON.stringify({ q }),49 });50 if (!res.ok) throw new Error(`Search failed: ${res.status}`);51 const { data } = await res.json();52 return data.items.slice(0, 5).map((v) => `${v.title} — ${v.videoUrl}`).join('\n');53 },54 }),55];5657const prompt = ChatPromptTemplate.fromMessages([58 ['system', 'You are a research assistant with access to YouTube data. Use the tools to answer questions about videos, channels, and topics.'],59 ['human', '{input}'],60 new MessagesPlaceholder('agent_scratchpad'),61]);6263const llm = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });64const agent = await createOpenAIFunctionsAgent({ llm, tools, prompt });65const executor = new AgentExecutor({ agent, tools });6667const result = await executor.invoke({68 input: 'Summarize this video and tell me what the top comments say: https://www.youtube.com/watch?v=YOUR_VIDEO_ID',69});7071console.log(result.output);1STOPHY_API_KEY=your_key OPENAI_API_KEY=your_key node --input-type=module agent.jsThe docs have the rest of the endpoints — channels, playlists, search filters, and more.