# Beyond Heavy API Keys: How I Built a Zero-Cost Asynchronous AI Summarizer in Spring Boot 3.4

> Source: <https://dev.to/siddhi_singh/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-spring-boot-34-3nii>
> Published: 2026-08-31 18:28:38+00:00

When building cloud services around Generative AI, developers often face two major challenges:

To address this, I built **EchoEngine**—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.

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.

`spring-ai-starter-model-google-genai`

)

```
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-google-genai</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
🏗️ Architectural Overview & 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=...&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 & pass to Gemini
        return chatClient.prompt().user(promptText).call().content();
    }
}
```

🛑 Real-World Debugging & Battle Scars

The "Corporate Sandbox" 429 API Quota Trap

During early testing, the initial API key continuously threw a 429 Quota Exceeded (limit: 0) error despite zero previous requests.

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.

The Fix: Migrating to an unmanaged developer sandbox project in Google AI Studio unlocked standard developer free-tier allowances.

Model Override Issues

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:

./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"

⚡ How to Run Locally

Set the API Key:

$env:GEMINI_API_KEY="YOUR_GEMINI_API_KEY"

Start the Service:

./mvnw spring-boot:run

Trigger the POST Endpoint:

$body = @{ url = "[https://www.youtube.com/watch?v=dQw4w9WgXcQ](https://www.youtube.com/watch?v=dQw4w9WgXcQ)" } | ConvertTo-Json

Invoke-RestMethod -Uri "[http://localhost:8080/api/summarize](http://localhost:8080/api/summarize)" -Method Post -Body $body -ContentType "application/json"

🎯 Key Takeaways

Decoupled Architecture: Separating scraping, prompt logic, and controllers makes the codebase maintainable and testable.

Virtual Threads simplify high-I/O applications: Project Loom delivers high throughput without rewriting code to be reactive.
