Terminal 429s: Exponential Backoff with curl
Terminal 429s: Exponential Backoff with curl
A script works once, then a batch of requests starts returning HTTP 429. Your first instinct might be to rerun it. That usually adds traffic to the same overloaded window. Give requests time to recover instead: wait, retry, and increase the delay after each failure.
I’ll use Fluxo, an AI API relay with one key and one base URL, https://api.fluxorelay.com. The commands below use Bash, curl 7.76 or newer, and jq. Run them in the same terminal session.
1. Load your key and select a returned model ID
If you want to follow along, create a Fluxo account and generate an API key. Read it without putting the secret directly into your shell history:
read -rsp 'Fluxo API key: ' FLUXO_API_KEY; echo; export FLUXO_API_KEY
export FLUXO_BASE_URL='https://api.fluxorelay.com'
Fetch the available model IDs before building a request:
curl --silent --show-error --fail-with-body --connect-timeout 10 --max-time 30 "$FLUXO_BASE_URL/v1/models" -H "Authorization: Bearer $FLUXO_API_KEY" | jq -r '.data[].id'
Choose the returned ID for a model such as Claude Sonnet 5.5 or GPT-6 Astra. Treat those as display names; copy the actual API identifier from the response:
read -rp 'Model ID from the response: ' FLUXO_MODEL; export FLUXO_MODEL
Fluxo also works with Claude Code, Codex, Gemini CLI, and any OpenAI SDK. Using curl here makes the HTTP behavior visible without an SDK’s automatic retries obscuring it.
2. Capture the 429 response before changing retries
Create a small request body, then save both the response headers and body:
jq -n --arg model "$FLUXO_MODEL" '{model: $model, messages: [{role: "user", content: "Reply with one short sentence about exponential backoff."}], max_tokens: 128}' > request.json
curl --silent --show-error --fail-with-body --connect-timeout 10 --max-time 30 -D response.headers -o response.json -w 'HTTP %{http_code}\n' "$FLUXO_BASE_URL/v1/chat/completions" -H "Authorization: Bearer $FLUXO_API_KEY" -H 'Content-Type: application/json' --data-binary @request.json
cat response.headers response.json — A 429 means a limit was reached, but the response body matters. Temporary request or token limits can recover with waiting. An exhausted allowance or account restriction may need intervention. Repeating the same request cannot fix every cause.
3. Enable bounded exponential backoff in curl
For transient throttling, run the same request with curl’s retry controls:
curl --silent --show-error --fail-with-body --connect-timeout 10 --max-time 30 --retry 5 --retry-delay 0 --retry-max-time 90 -D response.headers -o response.json -w 'HTTP %{http_code}\n' "$FLUXO_BASE_URL/v1/chat/completions" -H "Authorization: Bearer $FLUXO_API_KEY" -H 'Content-Type: application/json' --data-binary @request.json
- The retry count permits five retries after the initial attempt.
- A zero retry delay selects curl’s built-in exponential waiting schedule.
- Without server guidance, those waits are approximately one, two, four, eight, and sixteen seconds.
- A valid Retry-After header takes precedence over that default schedule.
- The retry budget stops further retries after ninety elapsed seconds.
That budget is not a strict ninety-second wall-clock deadline: an attempt started within it can finish afterward. The thirty-second maximum applies separately to each attempt. Curl also retries certain other transient failures; this policy is broader than 429 alone.
4. Keep parallel workers from retrying together
Backoff helps individual requests, but twenty workers can still wake together. First reduce concurrency. For custom retry loops, add full jitter: randomly choose a delay between zero and the current exponential cap, while honoring any server-directed minimum wait. Curl’s built-in schedule does not provide full jitter.
Keep retries in one layer. If an SDK already retries, wrapping it in another retry loop multiplies attempts. Avoid blindly retrying ambiguous POST failures: a timed-out generation might already have run. Fluxo bills real tokens, including cache reads and writes, with no monthly fee, so duplicate work can affect cost.
5. Verify recovery and preserve failure evidence
Inspect response.json after the command finishes. A successful response confirms this request recovered; repeated 429s call for lower concurrency or investigation of the reported limit. Preserve timestamps, status codes, and request identifiers, keeping authorization headers private.
If you need help, leave a note on the contact page after signing in. Fluxo also offers a referral share when people you invite pay; keep that separate from diagnosing request failures.