Skip to content

fix(crawler): treat HTTP 429 as rate-limit condition with cooldown backoff - #3642

Closed
SoloDevAbu wants to merge 3 commits into
apify:masterfrom
SoloDevAbu:fix/rate-limit-429-cooldown-backoff
Closed

fix(crawler): treat HTTP 429 as rate-limit condition with cooldown backoff#3642
SoloDevAbu wants to merge 3 commits into
apify:masterfrom
SoloDevAbu:fix/rate-limit-429-cooldown-backoff

Conversation

@SoloDevAbu

Copy link
Copy Markdown

Fix: HTTP 429 treated as rate-limit condition with cooldown backoff

Closes #3623

Changes

  • Introduced RateLimitError to represent retriable rate-limit failures with optional delay metadata
  • Added rateLimitCooldownSecs (BasicCrawler option, default 60) as fallback wait time for 429 without Retry-After
  • Updated retry handling in BasicCrawler to:
    • sleep for RateLimitError.delayMillis (or fallback cooldown)
    • reclaim the request after the delay
    • avoid session.markBad() for rate-limit retries
  • Updated HTTP and Browser crawler response handling to throw RateLimitError on 429 and parse Retry-After
  • Removed 429 from default blocked status codes so session blocking remains focused on true block/auth statuses (401, 403)
  • Updated related docs/default annotations accordingly

Test Coverage

Test updates/additions validate:

  • 429 with Retry-After delays retry appropriately
  • 429 without Retry-After uses fallback cooldown
  • blocked-status session retirement expectations exclude 429
  • browser crawler coverage includes 429 delayed-retry behavior

@barjin barjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution @SoloDevAbu .

I believe we should match the implementation of this feature with the similar feature in crawlee-python. Please see the PR here.

Below, I added a few comments (those might be irrelevant to the Python-inspired implementation).

Comment on lines +267 to +272
/**
* How long to wait before retrying a request that failed with a rate limit error (HTTP 429).
* This value will only be used if the server does not return a `Retry-After` header.
* @default 60
*/
rateLimitCooldownSecs?: number;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that for backward compatibility, we should retry immediately. This change in default behaviour might make some users' workflows run much longer.


if (error instanceof RateLimitError) {
const delayMillis = error.delayMillis || this.rateLimitCooldownMillis;
await sleep(delayMillis);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if delayMillis is longer than internalTimeoutMillis? Won't the request fail anyway with a timeout?

@SoloDevAbu
SoloDevAbu requested a review from barjin May 11, 2026 17:15

@barjin barjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As described in the previous review, we want to implement something like the new Python ThrottlingRequestManager to keep both versions aligned.

Also, the implementation in this PR would likely not work well with Crawlee's autoscaling. We do not want the waiting requests to block the execution of other requests - these should preferably "skip the line". With the current impl, we might just get stuck waiting for the delay to elapse, with other (valid) requests just waiting idle in the RequestQueue.

For these reasons, I'll be closing this PR now. Feel free to open a new one following apify/crawlee-python#1762 if you feel like it.

Thank you!

@barjin barjin closed this May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP 429 responses retire sessions and immediately retry reclaimed requests instead of applying cooldown

4 participants