<?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: Siddhi Singh</title>
    <description>The latest articles on DEV Community by Siddhi Singh (@siddhi_singh).</description>
    <link>https://dev.to/siddhi_singh</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4103192%2F7634914f-d99d-46af-87b7-1c1e81416e73.png</url>
      <title>DEV Community: Siddhi Singh</title>
      <link>https://dev.to/siddhi_singh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siddhi_singh"/>
    <language>en</language>
    <item>
      <title>Beyond Heavy API Keys: How I Built a Zero-Cost Asynchronous AI Summarizer in Spring Boot 3.4</title>
      <dc:creator>Siddhi Singh</dc:creator>
      <pubDate>Mon, 31 Aug 2026 18:28:38 +0000</pubDate>
      <link>https://dev.to/siddhi_singh/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-spring-boot-34-3nii</link>
      <guid>https://dev.to/siddhi_singh/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-spring-boot-34-3nii</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Introduction
&lt;/h2&gt;

&lt;p&gt;When building cloud services around Generative AI, developers often face two major challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High Latency &amp;amp; Thread Exhaustion:&lt;/strong&gt; Downstream AI model calls take seconds to stream back tokens, pinning standard OS server threads in I/O wait states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-Party API Overhead:&lt;/strong&gt; Relying on official data APIs (like the YouTube Data API v3) introduces unnecessary API key rotation, cost, and rate-limiting friction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address this, I built &lt;strong&gt;EchoEngine&lt;/strong&gt;—a lightweight, high-concurrency Spring Boot 3.4 application designed to ingest YouTube URLs, scrape publicly available metadata cleanly without YouTube API keys, and generate 3-bullet-point technical summaries using Google’s Gemini 2.5 Flash model.&lt;/p&gt;

&lt;p&gt;In this article, I will break down the system architecture, how Java 21 Virtual Threads (Project Loom) kept the application non-blocking, and the real-world GCP API quota traps I ran into while building it.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Tech Stack &amp;amp; Dependencies
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language &amp;amp; Runtime:&lt;/strong&gt; Java 21 (LTS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; Spring Boot 3.4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Integration:&lt;/strong&gt; Spring AI 1.1.5 (&lt;code&gt;spring-ai-starter-model-google-genai&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency:&lt;/strong&gt; Project Loom (Virtual Threads)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Tool:&lt;/strong&gt; Maven
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.ai&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-ai-starter-model-google-genai&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
🏗️ Architectural Overview &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Core Workflow
EchoEngine isolates data fetching, prompt construction, and LLM communication into clear service boundaries.

[ HTTP Client ]
       │
       ▼ (POST /api/summarize)
[ EchoController ] ──(Extracts Video ID)──► [ YouTubeSummaryService ]
       │
       ┌─────────────────┴─────────────────┐
       ▼                                   ▼
[ YouTubeClient ]                 [ Spring AI ChatClient ]
(oEmbed + HTML Regex)                (Google Gemini 2.5)
1. Key-Less YouTube Metadata Extraction
Instead of registering for developer API keys, YouTubeClient uses public endpoints:

Title: Fetched via YouTube’s public oEmbed endpoint (https://www.youtube.com/oembed?url=...&lt;span class="err"&gt;&amp;amp;&lt;/span&gt;format=json).
Description: Extracted via Spring’s RestClient by parsing raw HTML meta tags using regular expressions.
2. High-Concurrency via Java 21 Virtual Threads
Because calls to YouTube and Gemini involve substantial network latency, standard OS thread pools risk exhaustion under load. By configuring Virtual Threads in application.properties, every incoming request runs on a lightweight virtual thread:

# Enable Project Loom Virtual Threads
spring.threads.virtual.enabled=true
When RestClient blocks waiting for Gemini to finish generating text, the underlying carrier thread is unmounted to perform other tasks, giving the application non-blocking scalability without reactive stream complexity.

3. Spring AI Model Orchestration
Spring AI auto-configures a ChatClient bean using settings declared in application.properties:

spring.ai.google.genai.api-key=${GEMINI_API_KEY}
spring.ai.google.genai.chat.options.model=gemini-2.5-flash
The service constructs a targeted prompt and hands it off cleanly to the LLM:

@Service
public class YouTubeSummaryService {

    private final ChatClient chatClient;

    public YouTubeSummaryService(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder
            .defaultSystem("You are a technical assistant. Given a video's title and description, " +
                           "provide a 3-bullet-point technical summary.")
            .build();
    }

    public String summarizeVideo(String videoUrl) {
        // Fetch metadata via YouTubeClient &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; pass to Gemini
        return chatClient.prompt().user(promptText).call().content();
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;🛑 Real-World Debugging &amp;amp; Battle Scars&lt;br&gt;
The "Corporate Sandbox" 429 API Quota Trap&lt;br&gt;
During early testing, the initial API key continuously threw a 429 Quota Exceeded (limit: 0) error despite zero previous requests.&lt;/p&gt;

&lt;p&gt;The Cause: The key was generated inside a corporate-managed Google Cloud Platform project domain. GCP automatically enforces an explicit zero-quota default (limit: 0) on enterprise free tiers until billing accounts are linked.&lt;br&gt;
The Fix: Migrating to an unmanaged developer sandbox project in Google AI Studio unlocked standard developer free-tier allowances.&lt;br&gt;
Model Override Issues&lt;br&gt;
Spring AI's default configuration targeted gemini-2.0-flash out-of-the-box. Since the AI Studio account allocation was explicitly set for the gemini-2.5-flash model endpoint, runtime parameters were passed to force the override:&lt;/p&gt;

&lt;p&gt;./mvnw spring-boot:run "-Dspring-boot.run.arguments=--spring.ai.google.genai.api-key=YOUR_KEY --spring.ai.google.genai.chat.options.model=gemini-2.5-flash"&lt;br&gt;
⚡ How to Run Locally&lt;br&gt;
Set the API Key:&lt;/p&gt;

&lt;p&gt;$env:GEMINI_API_KEY="YOUR_GEMINI_API_KEY"&lt;br&gt;
Start the Service:&lt;/p&gt;

&lt;p&gt;./mvnw spring-boot:run&lt;br&gt;
Trigger the POST Endpoint:&lt;/p&gt;

&lt;p&gt;$body = @{ url = "&lt;a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=dQw4w9WgXcQ&lt;/a&gt;" } | ConvertTo-Json&lt;br&gt;
Invoke-RestMethod -Uri "&lt;a href="http://localhost:8080/api/summarize" rel="noopener noreferrer"&gt;http://localhost:8080/api/summarize&lt;/a&gt;" -Method Post -Body $body -ContentType "application/json"&lt;br&gt;
🎯 Key Takeaways&lt;br&gt;
Decoupled Architecture: Separating scraping, prompt logic, and controllers makes the codebase maintainable and testable.&lt;br&gt;
Virtual Threads simplify high-I/O applications: Project Loom delivers high throughput without rewriting code to be reactive.&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;
Need more details? Check out the &lt;a href="https://docs.google.com/document/d/1MZcqPWCEvBZ2dT8VypxZ2gkrTDlIFFEv7dUPYz8rxqE/edit?pli=1&amp;amp;tab=t.0" rel="noopener noreferrer"&gt;full documentation and design notes&lt;/a&gt;.&lt;br&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/singhsiddhi301" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F89301365%3Fv%3D4%3Fs%3D400" height="420" class="m-0" width="420"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/singhsiddhi301" rel="noopener noreferrer" class="c-link"&gt;
            singhsiddhi301 (Siddhi Singh) · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            singhsiddhi301 has 9 repositories available. Follow their code on GitHub.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg" width="32" height="32"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>java</category>
      <category>ai</category>
      <category>springboot</category>
      <category>antigravity</category>
    </item>
  </channel>
</rss>
