From 451ebb3e221b09fecb72461da14a288986a13788 Mon Sep 17 00:00:00 2001 From: Tjarko Leifer Date: Fri, 20 Feb 2026 11:40:02 +0100 Subject: [PATCH] Fix OpenAI 429 rate limiting with exponential backoff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Reddit search uses OpenAI's Responses API with web_search, which frequently returns 429 rate limit errors. The previous retry logic used linear backoff (1s, 2s, 3s) which is too aggressive for OpenAI's rate limiter (often needs 10-60s waits). Changes: - Increase max retries from 3 to 5 - Switch from linear to exponential backoff (2s, 5s, 9s, 17s, 33s) - Parse and respect Retry-After header from OpenAI 429 responses - Fall back to cheaper models (gpt-4.1 → gpt-4o) on 429s, not just on 400/403 access errors - Remove gpt-4o-mini from fallback chain — it doesn't support web_search with the filters parameter Co-Authored-By: Claude Opus 4.6 --- scripts/lib/http.py | 19 ++++++++++++++++--- scripts/lib/openai_reddit.py | 6 +++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/scripts/lib/http.py b/scripts/lib/http.py index 17d612a..9894c6e 100644 --- a/scripts/lib/http.py +++ b/scripts/lib/http.py @@ -18,8 +18,8 @@ def log(msg: str): if DEBUG: sys.stderr.write(f"[DEBUG] {msg}\n") sys.stderr.flush() -MAX_RETRIES = 3 -RETRY_DELAY = 1.0 +MAX_RETRIES = 5 +RETRY_DELAY = 2.0 USER_AGENT = "last30days-skill/2.1 (Assistant Skill)" @@ -92,7 +92,20 @@ def request( raise last_error if attempt < retries - 1: - time.sleep(RETRY_DELAY * (attempt + 1)) + if e.code == 429: + # Respect Retry-After header, fall back to exponential backoff + retry_after = e.headers.get("Retry-After") if hasattr(e, 'headers') else None + if retry_after: + try: + delay = float(retry_after) + except ValueError: + delay = RETRY_DELAY * (2 ** attempt) + 1 + else: + delay = RETRY_DELAY * (2 ** attempt) + 1 # 2s, 5s, 9s... + log(f"Rate limited (429). Waiting {delay:.1f}s before retry {attempt + 2}/{retries}") + else: + delay = RETRY_DELAY * (2 ** attempt) + time.sleep(delay) except urllib.error.URLError as e: log(f"URL Error: {e.reason}") last_error = HTTPError(f"URL Error: {e.reason}") diff --git a/scripts/lib/openai_reddit.py b/scripts/lib/openai_reddit.py index 51e2eed..2c6b838 100644 --- a/scripts/lib/openai_reddit.py +++ b/scripts/lib/openai_reddit.py @@ -8,7 +8,8 @@ from typing import Any, Dict, List, Optional from . import http # Fallback models when the selected model isn't accessible (e.g., org not verified for GPT-5) -MODEL_FALLBACK_ORDER = ["gpt-4.1", "gpt-4o", "gpt-4o-mini"] +# Note: gpt-4o-mini does NOT support web_search with filters param, so exclude it +MODEL_FALLBACK_ORDER = ["gpt-4.1", "gpt-4o"] def _log_error(msg: str): @@ -188,6 +189,9 @@ def search_reddit( if _is_model_access_error(e): _log_info(f"Model {current_model} not accessible, trying fallback...") continue + if e.status_code == 429: + _log_info(f"Rate limited on {current_model}, trying fallback model...") + continue # Non-access error, don't retry with different model raise