◉GEMINI LABJP
●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 6 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — deep-research-pro-preview-12-2025 shuts down 10/23, 6 days left. Move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…
Articles/API / SDK
◈ API / SDK/2026-05-19Advanced

Wiring Circuit Breakers and Graceful Degradation into Gemini API — an Indie App's Stability-First Notes

When you run Gemini API in production for an indie app, something upstream breaks at least a few times a month. Here are the building blocks for circuit breakers and graceful degradation I settled on, with the implementation traps I actually hit.

Gemini API249Circuit BreakerAvailabilityProduction33Architecture10

✦ Premium Article

Around 2 AM on a night in late March 2026, a phone notification told me my crash rate had spiked. I opened Crashlytics and traced it to a chain of 503 responses from the Gemini API, followed by the main thread dutifully waiting out a 30-second timeout. AdMob impressions sagged in real time; that night alone, ad revenue ended up at less than half my usual figure. The app itself was fine, but a few minutes of upstream weather had punched a hole in the daily revenue. The next morning I sat down and rebuilt the circuit breaker and graceful degradation design more seriously. This piece is my notes from that rebuild and the tuning I have done since.

Why an Indie App Should Stop Aiming for "Always Working"

I have been shipping smartphone wallpaper apps and visualization apps as an indie developer since 2014, and the catalog has now passed 50 million cumulative downloads. As an indie developer Hirokawa, operations are basically a team of one. Even with Cloudflare Workers and CDN layers in front, an upstream provider like Gemini API will eventually have a few minutes that ripple through to the user.

So I gave up on a different goal. Instead of "Gemini API always answers", I aim for "the app stays quietly useful even in the seconds Gemini API does not answer". A nominal 99.9% availability sounds great, but it leaves you about 43 minutes of allowed downtime per month, and for an indie developer that 43 minutes is almost guaranteed to land on the weeknight evening when ads are actually clicking. The thing worth defending is not perfect uptime, it is the user experience during those minutes.

Circuit breakers and graceful degradation are the concrete tools for that mindset. Over 14 months since I shipped this design, upstream-triggered fatal crashes across my three main apps fell by 87% in Crashlytics, and the depth of revenue dips during incidents shrank noticeably.

Minimum Viable Circuit Breaker — Skeleton First

Plenty of libraries exist, but for an indie app the best insurance is "code I can fully explain to myself in one sitting." Here is the minimum Python skeleton I run server-side. While request volume is modest this is enough on its own.

import time
from dataclasses import dataclass, field
from enum import Enum
from threading import Lock
 
class State(str, Enum):
    CLOSED = "closed"
    OPEN = "open"
    HALF_OPEN = "half_open"
 
@dataclass
class Breaker:
    failure_threshold: int = 5          # consecutive failures to OPEN
    recovery_seconds: float = 30.0      # OPEN until HALF_OPEN
    half_open_max_calls: int = 3        # trial calls allowed
    state: State = State.CLOSED
    fail_count: int = 0
    opened_at: float = 0.0
    half_open_calls: int = 0
    lock: Lock = field(default_factory=Lock)
 
    def allow_request(self) -> bool:
        with self.lock:
            if self.state == State.OPEN:
                if time.time() - self.opened_at >= self.recovery_seconds:
                    self.state = State.HALF_OPEN
                    self.half_open_calls = 0
                else:
                    return False
            if self.state == State.HALF_OPEN:
                if self.half_open_calls >= self.half_open_max_calls:
                    return False
                self.half_open_calls += 1
            return True
 
    def on_success(self) -> None:
        with self.lock:
            self.fail_count = 0
            self.state = State.CLOSED
            self.half_open_calls = 0
 
    def on_failure(self) -> None:
        with self.lock:
            self.fail_count += 1
            if self.state == State.HALF_OPEN or self.fail_count >= self.failure_threshold:
                self.state = State.OPEN
                self.opened_at = time.time()
                self.fail_count = 0

The skeleton is under 60 lines. What matters in production is choosing failure_threshold and recovery_seconds against your real traffic shape. My wallpaper app backend runs around 4–12 req/s, so 5 consecutive failures (about a second of bad weather) into OPEN, with a 30-second cooldown, is my current resting point.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦How to pick Closed/Open/Half-Open thresholds for an indie app's traffic shape
✦A four-stage fallback (Gemini 3 Pro → 2.5 Flash → cache → default text) with working code
✦A retry budget pattern that protects p95 latency instead of inflating it
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ API / SDK2026-04-26
Architecting a Multi-Tenant SaaS on Gemini API — Tenant Isolation, Usage Metering, and Runaway Cost Defense in Production
A field-tested blueprint for serving Gemini API to multiple tenants on a single backend — covering tenant isolation choices, per-tenant rate limiting in Redis, request-level usage metering for billing, and runaway-cost defenses.
◈ API / SDK2026-07-27
Retiring Prompt Assets That Refuse to Die — A Ledger for Paths Static Analysis Cannot See
A prompt I had supposedly consolidated away was still running daily. Here is the retirement ledger I built to cross-check static references, flag defaults, observation windows, model end-of-life dates, and cache effectiveness.
◈ API / SDK2026-05-21
Designing Event-Driven AI Workflows with Gemini API and Cloud Pub/Sub — Notes from an Indie Developer
An implementation memo on wiring Gemini API into Cloud Pub/Sub event-driven workflows. Using an app-review analysis pipeline as the running example, the article covers retry policy, dead-lettering, idempotency, and cost guardrails — from the perspective of someone running it solo.