<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nuco Z</title>
    <description>The latest articles on DEV Community by Nuco Z (@nuco_z_270906fb0e460592db).</description>
    <link>https://dev.to/nuco_z_270906fb0e460592db</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3629891%2Ff0b9fcae-351e-4316-b5cf-1fdb20a0c398.png</url>
      <title>DEV Community: Nuco Z</title>
      <link>https://dev.to/nuco_z_270906fb0e460592db</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nuco_z_270906fb0e460592db"/>
    <language>en</language>
    <item>
      <title>From Side Project to $500/Month: Building a YouTube Tool as a Solo Developer</title>
      <dc:creator>Nuco Z</dc:creator>
      <pubDate>Mon, 09 Mar 2026 03:52:06 +0000</pubDate>
      <link>https://dev.to/nuco_z_270906fb0e460592db/from-side-project-to-500month-building-a-youtube-tool-as-a-solo-developer-8ok</link>
      <guid>https://dev.to/nuco_z_270906fb0e460592db/from-side-project-to-500month-building-a-youtube-tool-as-a-solo-developer-8ok</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40u7vk2s714corl59x64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40u7vk2s714corl59x64.png" alt=" " width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built a YouTube subtitle downloader as a side project. After 6 months and a lot of trial and error, I'm finally hitting $500/month. Here is the raw truth about what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎬 The Origin Story
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;March 2025:&lt;/strong&gt; I was working on a video project and needed to extract subtitles from 50+ YouTube videos. Manual copying? No way.&lt;/p&gt;

&lt;p&gt;I spent 2 hours building a quick Python script to automate it. It worked perfectly. Then I thought: &lt;em&gt;"Others probably need this too."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spoiler:&lt;/strong&gt; That assumption was both right and wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 The Idea Validation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What I did right:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Searched "youtube subtitle download" - 10K+ monthly searches. ✅&lt;/li&gt;
&lt;li&gt;Checked competitors - they existed (market validation!). ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I did wrong:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Didn't talk to potential users before writing code. ❌&lt;/li&gt;
&lt;li&gt;Assumed people would pay immediately. ❌&lt;/li&gt;
&lt;li&gt;Ignored the "Red Ocean" warning signs. ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Lesson 1:&lt;/strong&gt; Competition = validation, but you need a unique angle to stand out.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Building the MVP
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Week 1-2:&lt;/strong&gt; Basic functionality&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input URL → Download subtitle&lt;/li&gt;
&lt;li&gt;Tech: Next.js + Python + Vercel&lt;/li&gt;
&lt;li&gt;Cost: $0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Week 3-4:&lt;/strong&gt; The "Feature Creep" mistake&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added 5 different export formats&lt;/li&gt;
&lt;li&gt;Fancy animations&lt;/li&gt;
&lt;li&gt;Dark mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Users:&lt;/strong&gt; 0&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson 2:&lt;/strong&gt; Ship fast, iterate faster. Nobody cares about dark mode if your core value isn't hitting the mark.&lt;/p&gt;

&lt;h2&gt;
  
  
  📉 The Reality Check
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Month 1 Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traffic: 50 visitors/day (mostly bots)&lt;/li&gt;
&lt;li&gt;Revenue: $0&lt;/li&gt;
&lt;li&gt;Time spent: 80 hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; I built a "better mousetrap" in a market full of them. I needed to pivot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Turning Point:&lt;/strong&gt; I added &lt;strong&gt;AI-powered summarization&lt;/strong&gt;. Suddenly, I wasn't just a "downloader"—I was an AI productivity tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Monetization Strategy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What didn't work:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Paywalls:&lt;/strong&gt; 95% bounce rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$9.99/mo subscription:&lt;/strong&gt; Too expensive for casual users.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What worked:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Freemium:&lt;/strong&gt; First download is free (no signup). This reduced friction by 80%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credit system:&lt;/strong&gt; Pay-as-you-go model. Users love flexibility.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Conversion rate:&lt;/strong&gt; 2.5% (Industry average is 2-5%)&lt;/p&gt;

&lt;h2&gt;
  
  
  📊 Growth Tactics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ What Failed:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Paid ads (burned $200, got 2 signups).&lt;/li&gt;
&lt;li&gt;Cold emails (0% response).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ What Worked:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. SEO Long-tail Keywords&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"how to download youtube subtitles for free"&lt;/li&gt;
&lt;li&gt;Traffic: 60% of my total visitors come from organic search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Reddit (The "Careful" approach)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answering genuine questions in &lt;code&gt;r/SaaS&lt;/code&gt; and &lt;code&gt;r/Productivity&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warning:&lt;/strong&gt; Don't be spammy. You will get banned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Content Marketing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrote a 5,000-word "Ultimate Guide to YouTube Subtitles".&lt;/li&gt;
&lt;li&gt;It ranks #3 on Google for specific queries. Traffic: 25%.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💸 Revenue Breakdown (Month 6)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Revenue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Individual users&lt;/td&gt;
&lt;td&gt;$320&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API subscriptions&lt;/td&gt;
&lt;td&gt;$100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ads (AdSense)&lt;/td&gt;
&lt;td&gt;$80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$500/mo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Net profit: ~$435/mo.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 What I'd Do Differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Talk to users FIRST:&lt;/strong&gt; Would've discovered the AI angle in Week 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with paid from day 1:&lt;/strong&gt; Free users give feedback, paid users give validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on ONE channel:&lt;/strong&gt; Don't spread yourself thin across Twitter, LinkedIn, and Reddit. Master one first.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📈 Key Metrics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MRR:&lt;/strong&gt; $500&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Churn:&lt;/strong&gt; 15% (Working to improve this)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAC:&lt;/strong&gt; $12 (mostly organic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LTV:&lt;/strong&gt; $35 (Average user stays 5 months)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤝 Want to Follow Along?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Check out the tool:&lt;/strong&gt; &lt;a href="https://ytvidhub.com" rel="noopener noreferrer"&gt;ytvidhub.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Questions for you:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What's your biggest challenge with side projects right now?&lt;/li&gt;
&lt;li&gt;Would you pay for a YouTube AI tool?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Drop your thoughts below! 👇&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S. If you're building something, ship it. Imperfect action &amp;gt; perfect inaction.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>indiehacke</category>
      <category>showdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Built a Bulk YouTube Subtitle Extractor as a Solo Dev — Here's My Tech Stack and What I Learned</title>
      <dc:creator>Nuco Z</dc:creator>
      <pubDate>Fri, 27 Feb 2026 03:11:58 +0000</pubDate>
      <link>https://dev.to/nuco_z_270906fb0e460592db/i-built-a-bulk-youtube-subtitle-extractor-as-a-solo-dev-heres-my-tech-stack-and-what-i-learned-21m6</link>
      <guid>https://dev.to/nuco_z_270906fb0e460592db/i-built-a-bulk-youtube-subtitle-extractor-as-a-solo-dev-heres-my-tech-stack-and-what-i-learned-21m6</guid>
      <description>&lt;p&gt;Every developer has that one side project that starts as a weekend hack and turns into something real. For me, it was a tool to bulk-extract YouTube subtitles.&lt;/p&gt;

&lt;p&gt;It started with a simple frustration: I needed transcripts from an entire YouTube playlist for a research project. YouTube lets you copy transcripts one video at a time. I had 200 videos. That wasn't going to work.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://ytvidhub.com/" rel="noopener noreferrer"&gt;YTVidHub&lt;/a&gt; — a web app that extracts subtitles from YouTube videos, playlists, and entire channels in bulk. Paste a URL, pick your format (SRT, VTT, or TXT), and download everything as a ZIP.&lt;/p&gt;

&lt;p&gt;Here's what the journey looked like from a technical perspective.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;p&gt;I wanted something that could ship fast, handle server-side rendering for SEO, and scale without DevOps headaches. Here's what I landed on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; Next.js 16 (App Router)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL + Prisma ORM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; JWT-based (custom, no third-party provider)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Features:&lt;/strong&gt; DeepSeek API for video summarization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; Stripe&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;i18n:&lt;/strong&gt; next-intl (4 languages)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Next.js?
&lt;/h3&gt;

&lt;p&gt;Two reasons: SEO and developer experience.&lt;/p&gt;

&lt;p&gt;A subtitle downloader tool lives and dies by organic search traffic. Server-side rendering was non-negotiable. Next.js App Router gave me that plus React Server Components, which meant I could fetch data on the server without shipping unnecessary JavaScript to the client.&lt;/p&gt;

&lt;p&gt;The file-based routing also made it easy to create dozens of SEO landing pages without a complex routing setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use a third-party auth provider?
&lt;/h3&gt;

&lt;p&gt;Cost. As a solo developer bootstrapping with zero funding, every dollar matters. Auth0 and Clerk are great, but their free tiers have limits that would bite me as I scale. A simple JWT + Google OAuth flow took me a day to implement and costs nothing to run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified auth flow&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is it as feature-rich as Auth0? No. Does it work perfectly for my use case? Yes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hard Part: Subtitle Extraction at Scale
&lt;/h2&gt;

&lt;p&gt;The core technical challenge wasn't building the UI — it was reliably extracting subtitles from YouTube at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 1: YouTube doesn't want you scraping subtitles
&lt;/h3&gt;

&lt;p&gt;YouTube's official Data API doesn't provide subtitle content. You can get video metadata, but not the actual transcript text. The &lt;code&gt;timedtext&lt;/code&gt; endpoint that serves subtitles to the YouTube player is undocumented and changes without notice.&lt;/p&gt;

&lt;p&gt;My approach: I reverse-engineered the subtitle delivery mechanism and built a resilient fetcher that handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple subtitle tracks per video (manual + auto-generated)&lt;/li&gt;
&lt;li&gt;Language detection and selection&lt;/li&gt;
&lt;li&gt;Rate limiting with exponential backoff&lt;/li&gt;
&lt;li&gt;Graceful fallbacks when subtitles aren't available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight was that YouTube serves subtitles as XML, which needs to be parsed and converted to standard formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// YouTube serves subtitles as XML like this:&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;text start="1.23" dur="4.5"&amp;gt;Hello world&amp;lt;/text&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;xmlToSrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseXmlEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;formatTimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;formatTimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; --&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Challenge 2: Playlist expansion
&lt;/h3&gt;

&lt;p&gt;A YouTube playlist URL doesn't give you all the video IDs upfront. You need to paginate through the playlist API, which returns 50 items at a time. For a channel with 2,000+ videos, that's 40+ API calls just to get the video list.&lt;/p&gt;

&lt;p&gt;I built a streaming pipeline that starts checking subtitles as soon as the first batch of video IDs arrives, rather than waiting for the full list. This makes the UX feel much faster — users see progress immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 3: Bulk downloads without killing the server
&lt;/h3&gt;

&lt;p&gt;When a user requests subtitles for 500 videos, you can't process them all synchronously. I implemented a queue-based system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User submits playlist URL&lt;/li&gt;
&lt;li&gt;Server expands the playlist and returns video list&lt;/li&gt;
&lt;li&gt;Client requests subtitles in batches of 10&lt;/li&gt;
&lt;li&gt;Each batch is processed in parallel on the server&lt;/li&gt;
&lt;li&gt;Results are streamed back to the client&lt;/li&gt;
&lt;li&gt;Final ZIP is assembled client-side&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This keeps server memory usage flat regardless of playlist size.&lt;/p&gt;




&lt;h2&gt;
  
  
  SEO: The Make-or-Break for Tool Sites
&lt;/h2&gt;

&lt;p&gt;For a free tool targeting organic traffic, SEO isn't a nice-to-have — it's the entire growth strategy. Here's what I did:&lt;/p&gt;

&lt;h3&gt;
  
  
  Programmatic SEO pages
&lt;/h3&gt;

&lt;p&gt;I created dedicated landing pages for different search intents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/youtube-subtitle-downloader&lt;/code&gt; — targets the generic search&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/bulk-youtube-subtitle-downloader&lt;/code&gt; — targets the bulk/power user search&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/what-is-an-srt-file&lt;/code&gt; — targets informational queries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/guide/*&lt;/code&gt; — long-form content for long-tail keywords&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each page has unique content, structured data (JSON-LD), and proper meta tags. Next.js makes this straightforward with &lt;code&gt;generateMetadata&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateMetadata&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTranslations&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;alternates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buildCanonicalUrl&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buildCanonicalUrl&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buildCanonicalUrl&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Internationalization for traffic
&lt;/h3&gt;

&lt;p&gt;Adding support for Spanish, German, and Korean multiplied my addressable search volume by ~3x with relatively little effort. &lt;code&gt;next-intl&lt;/code&gt; handles the routing and message loading, and I used AI to help with translations (then had native speakers review them).&lt;/p&gt;

&lt;h3&gt;
  
  
  What I got wrong
&lt;/h3&gt;

&lt;p&gt;I initially created too many pages targeting similar keywords. Google got confused about which page to rank, and none of them ranked well. This is called "keyword cannibalization" — a lesson I learned the hard way. I'm now consolidating pages and using 301 redirects to focus authority.&lt;/p&gt;




&lt;h2&gt;
  
  
  Monetization: The Credits Model
&lt;/h2&gt;

&lt;p&gt;I went with a credits-based system instead of a traditional subscription:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free tier:&lt;/strong&gt; 5 credits per day (1 credit = 1 video subtitle download)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily reward:&lt;/strong&gt; +3 bonus credits for returning daily&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paid plans:&lt;/strong&gt; Credit packs via Stripe for heavy users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why credits instead of subscriptions? Two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lower barrier to entry.&lt;/strong&gt; Users can try the tool without committing to a monthly fee. Most people only need subtitles occasionally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Natural upsell.&lt;/strong&gt; When someone hits their limit mid-workflow, the friction of buying credits is much lower than subscribing. They're already invested in the task.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stripe integration with Next.js was surprisingly smooth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;priceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;success_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/pricing?success=true`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/pricing?canceled=true`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;creditAmount&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The webhook handles credit allocation after payment confirmation. Simple, reliable, no subscription management complexity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding AI: The Unexpected Differentiator
&lt;/h2&gt;

&lt;p&gt;The original product was just a subtitle downloader. But I noticed users were downloading transcripts to feed them into ChatGPT for summaries. So I built it in.&lt;/p&gt;

&lt;p&gt;Now users can paste a YouTube URL and get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AI-generated summary of the video&lt;/li&gt;
&lt;li&gt;Key points extraction&lt;/li&gt;
&lt;li&gt;A mind map of the content structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used DeepSeek's API for this — it's significantly cheaper than OpenAI for long-context tasks, and the quality is excellent for summarization. The cost per summary is roughly $0.002, which makes it viable even on the free tier.&lt;/p&gt;

&lt;p&gt;This feature turned out to be the biggest differentiator. There are dozens of YouTube subtitle downloaders, but very few that combine bulk extraction with AI analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned After 3 Months
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What worked
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solving my own problem.&lt;/strong&gt; I built this because I needed it. That meant I understood the user deeply from day one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js + Vercel.&lt;/strong&gt; Zero DevOps overhead. I deploy with &lt;code&gt;git push&lt;/code&gt; and never think about servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credits model.&lt;/strong&gt; Lower friction than subscriptions for a tool that people use sporadically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internationalization early.&lt;/strong&gt; Adding i18n from the start was much easier than retrofitting it later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What I'd do differently
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with fewer pages.&lt;/strong&gt; I created 30+ SEO pages before validating which keywords actually had traffic. Should have started with 3-5 and expanded based on data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build in public sooner.&lt;/strong&gt; I spent 2 months building in silence. Sharing progress on Twitter and dev communities from week 1 would have built an audience before launch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on backlinks from day one.&lt;/strong&gt; On-page SEO is necessary but not sufficient. Without quality backlinks, Google won't rank you no matter how good your content is.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Numbers (honest)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monthly visitors:&lt;/strong&gt; ~600 (growing slowly)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paying users:&lt;/strong&gt; A handful&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revenue:&lt;/strong&gt; Enough for coffee, not rent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time invested:&lt;/strong&gt; ~400 hours over 3 months&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is it a success? Not yet. But it's a real product that real people use, and the foundation is solid. The growth curve for SEO-driven tools is slow at first and compounds over time. I'm playing the long game.&lt;/p&gt;




&lt;h2&gt;
  
  
  If You're Building a Similar Tool
&lt;/h2&gt;

&lt;p&gt;A few tactical tips:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Next.js App Router&lt;/strong&gt; if SEO matters. The metadata API and server components are a huge advantage over client-side frameworks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't over-engineer auth.&lt;/strong&gt; JWT + OAuth is enough for most indie projects. You can always migrate to a provider later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ship the MVP in 2 weeks, then iterate.&lt;/strong&gt; My biggest regret is spending too long on features nobody asked for.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Track everything from day one.&lt;/strong&gt; Google Analytics + Search Console + Microsoft Clarity. You can't improve what you can't measure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write content that helps people.&lt;/strong&gt; The best SEO strategy is genuinely useful content. Google is surprisingly good at detecting fluff.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;If you're working on a side project or building in public, I'd love to connect. Drop a comment about what you're building — I read every one.&lt;/p&gt;

&lt;p&gt;You can check out the tool at &lt;a href="https://ytvidhub.com/" rel="noopener noreferrer"&gt;ytvidhub.com&lt;/a&gt; if you ever need YouTube subtitles in bulk.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
