Skip to content

Commit 4fd0ec2

Browse files
committed
api: Add auto_retry_rate_limits option to handle 429 errors
Python's API Library does not handle HTTP 429 (RATE_LIMIT_HIT) errors automatically. If a client exceeds the message limit, Python will throw an error and force users to manually implement a method to pause the thread and try again. To resolve this, the __init__ constructor of the Client class has been given a new addition of an opt-in parameter initialized to False to keep existing behavior. This catches the 429 status code in do_api_query(). It ensures that the client safely parses the retry-after header, pauses the sleep thread using the time.sleep() function, and finally tries the message request again. Fixes: #773
1 parent 9960360 commit 4fd0ec2

File tree

1 file changed

+16
-0
lines changed

1 file changed

+16
-0
lines changed

zulip/zulip/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ def __init__(
393393
config_file: Optional[str] = None,
394394
verbose: bool = False,
395395
retry_on_errors: bool = True,
396+
auto_retry_rate_limits: bool = False,
396397
site: Optional[str] = None,
397398
client: Optional[str] = None,
398399
cert_bundle: Optional[str] = None,
@@ -476,6 +477,7 @@ def __init__(
476477
self.api_key = api_key
477478
self.email = email
478479
self.verbose = verbose
480+
self.auto_retry_rate_limits = auto_retry_rate_limits
479481
if site is not None:
480482
if site.startswith("localhost"):
481483
site = "http://" + site
@@ -655,6 +657,20 @@ def end_error_retry(succeeded: bool) -> None:
655657

656658
self.has_connected = True
657659

660+
# On Rate Limit (429) errors, retry after the delay the server asked us to wait
661+
if res.status_code == 429 and self.auto_retry_rate_limits:
662+
try:
663+
# Zulip will return the wait time in the either the (JSON) body or header
664+
wait_time = float(res.headers.get("Retry-After", 1.0))
665+
except Exception:
666+
wait_time = 1.0
667+
if self.verbose:
668+
print(
669+
f"Rate limit hit! Sleeping for {wait_time} seconds before retrying..."
670+
)
671+
time.sleep(wait_time)
672+
continue
673+
658674
# On 50x errors, try again after a short sleep
659675
if str(res.status_code).startswith("5") and error_retry(
660676
f" (server {res.status_code})"

0 commit comments

Comments
 (0)