Auto-rotating Gemini credentials to circumvent 429 rate limits
Problem / Context
Gemini's rate limits don't care about your task queue. When you hit 429 or RESOURCE_EXHAUSTED, you're locked out until the window resets. For an agent running 24/7 with cron jobs, queued tasks, and time-sensitive work, sitting idle for minutes or hours is unacceptable. The rate limit applies per credential, not per agent, which means the same agent with a different key gets a fresh quota.
Solution
Built a credential rotation system with four auth sources: three Google OAuth accounts (personal pro, work pro, free tier) and one API key. A watcher daemon (watcher.sh) tails the Docker container logs in real time. When it detects 429 or RESOURCE_EXHAUSTED, it triggers rotate.sh which: (1) increments to the next credential slot, (2) updates the provider config since OAuth and API key credentials require different LiteLLM provider formats, (3) restarts the container, (4) enforces a 30-second cooldown to prevent thrashing if multiple 429s arrive in quick succession. The watcher auto-starts on Windows boot via a startup hook. The Docker container runs with restart: unless-stopped so it comes back after each credential swap. The rotation is circular; after exhausting all four slots, it cycles back to the first, which by then has usually had its rate limit window reset.
Implementation
bash# watcher.sh - tail container logs, trigger rotation on 429
tail -F /var/log/ronin/container.log | while read line; do
if echo "$line" | grep -qE '429|RESOURCE_EXHAUSTED'; then
echo "[$(date)] Rate limit hit, rotating..."
bash ~/.gemini/rotation/rotate.sh
sleep 30 # cooldown to prevent thrashing
fi
done
# rotate.sh - swap credential slot
# [0] personal_pro (OAuth) <- ACTIVE
# [1] work_pro (OAuth)
# [2] api_key (API key)
# [3] free (OAuth)
SLOT=$(( (CURRENT_SLOT + 1) % 4 ))
update_provider_config $SLOT
docker restart roninResult
Zero manual intervention needed for rate limits since deployment. Agent burns through all four credential slots on heavy days, but the circular rotation means by the time it cycles back, the first slot has reset. Effective uptime went from roughly 70% (frequent idle gaps) to 99%+.