{"slug": "beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-boot", "title": "Beyond Heavy API Keys: How I Built a Zero-Cost Asynchronous AI Summarizer in Spring Boot 3.4", "summary": "A developer built EchoEngine, a Spring Boot 3.4 application that summarizes YouTube videos using Google's Gemini 2.5 Flash model without API keys for metadata extraction. The app leverages Java 21 virtual threads for high concurrency and uses public endpoints like oEmbed and HTML regex to fetch video metadata. The developer also documented a GCP quota trap where corporate-managed projects enforce zero-quota defaults, requiring migration to an unmanaged sandbox.", "body_md": "When building cloud services around Generative AI, developers often face two major challenges:\n\nTo 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.\n\nIn 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.\n\n`spring-ai-starter-model-google-genai`\n\n)\n\n```\n<dependency>\n    <groupId>org.springframework.ai</groupId>\n    <artifactId>spring-ai-starter-model-google-genai</artifactId>\n</dependency>\n<dependency>\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-web</artifactId>\n</dependency>\n🏗️ Architectural Overview & Core Workflow\nEchoEngine isolates data fetching, prompt construction, and LLM communication into clear service boundaries.\n\n[ HTTP Client ]\n       │\n       ▼ (POST /api/summarize)\n[ EchoController ] ──(Extracts Video ID)──► [ YouTubeSummaryService ]\n       │\n       ┌─────────────────┴─────────────────┐\n       ▼                                   ▼\n[ YouTubeClient ]                 [ Spring AI ChatClient ]\n(oEmbed + HTML Regex)                (Google Gemini 2.5)\n1. Key-Less YouTube Metadata Extraction\nInstead of registering for developer API keys, YouTubeClient uses public endpoints:\n\nTitle: Fetched via YouTube’s public oEmbed endpoint (https://www.youtube.com/oembed?url=...&format=json).\nDescription: Extracted via Spring’s RestClient by parsing raw HTML meta tags using regular expressions.\n2. High-Concurrency via Java 21 Virtual Threads\nBecause 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:\n\n# Enable Project Loom Virtual Threads\nspring.threads.virtual.enabled=true\nWhen 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.\n\n3. Spring AI Model Orchestration\nSpring AI auto-configures a ChatClient bean using settings declared in application.properties:\n\nspring.ai.google.genai.api-key=${GEMINI_API_KEY}\nspring.ai.google.genai.chat.options.model=gemini-2.5-flash\nThe service constructs a targeted prompt and hands it off cleanly to the LLM:\n\n@Service\npublic class YouTubeSummaryService {\n\n    private final ChatClient chatClient;\n\n    public YouTubeSummaryService(ChatClient.Builder chatClientBuilder) {\n        this.chatClient = chatClientBuilder\n            .defaultSystem(\"You are a technical assistant. Given a video's title and description, \" +\n                           \"provide a 3-bullet-point technical summary.\")\n            .build();\n    }\n\n    public String summarizeVideo(String videoUrl) {\n        // Fetch metadata via YouTubeClient & pass to Gemini\n        return chatClient.prompt().user(promptText).call().content();\n    }\n}\n```\n\n🛑 Real-World Debugging & Battle Scars\n\nThe \"Corporate Sandbox\" 429 API Quota Trap\n\nDuring early testing, the initial API key continuously threw a 429 Quota Exceeded (limit: 0) error despite zero previous requests.\n\nThe 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.\n\nThe Fix: Migrating to an unmanaged developer sandbox project in Google AI Studio unlocked standard developer free-tier allowances.\n\nModel Override Issues\n\nSpring 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:\n\n./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\"\n\n⚡ How to Run Locally\n\nSet the API Key:\n\n$env:GEMINI_API_KEY=\"YOUR_GEMINI_API_KEY\"\n\nStart the Service:\n\n./mvnw spring-boot:run\n\nTrigger the POST Endpoint:\n\n$body = @{ url = \"[https://www.youtube.com/watch?v=dQw4w9WgXcQ](https://www.youtube.com/watch?v=dQw4w9WgXcQ)\" } | ConvertTo-Json\n\nInvoke-RestMethod -Uri \"[http://localhost:8080/api/summarize](http://localhost:8080/api/summarize)\" -Method Post -Body $body -ContentType \"application/json\"\n\n🎯 Key Takeaways\n\nDecoupled Architecture: Separating scraping, prompt logic, and controllers makes the codebase maintainable and testable.\n\nVirtual Threads simplify high-I/O applications: Project Loom delivers high throughput without rewriting code to be reactive.", "url": "https://wpnews.pro/news/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-boot", "canonical_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_at": "2026-08-31 18:28:38+00:00", "updated_at": "2026-08-31 18:53:08.521965+00:00", "lang": "en", "topics": ["developer-tools", "generative-ai", "large-language-models", "ai-infrastructure"], "entities": ["EchoEngine", "Spring Boot", "Google Gemini", "YouTube", "Java 21", "Project Loom", "Google Cloud Platform", "Spring AI"], "alternates": {"html": "https://wpnews.pro/news/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-boot", "markdown": "https://wpnews.pro/news/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-boot.md", "text": "https://wpnews.pro/news/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-boot.txt", "jsonld": "https://wpnews.pro/news/beyond-heavy-api-keys-how-i-built-a-zero-cost-asynchronous-ai-summarizer-in-boot.jsonld"}}