429, defined in RFC 6585, is the polite way for a server to say "slow down". It may include a Retry-After header, either a number of seconds or an HTTP date, telling you when it will accept requests again.

Who sent it

Through a proxy, a 429 can come from two places:

  • The website. It arrives after the tunnel is established, and means the site's rate limit for your IP, account or session was exceeded.
  • The proxy provider. Some providers return 429 when you exceed a plan limit such as concurrent connections. It arrives on the CONNECT response, before the site is involved.

curl -v shows which: a 429 in reply to the CONNECT line is the proxy's; one after Connection established is the site's.

Handle it in code

Honour Retry-After when it is present; otherwise back off exponentially with jitter:

import random

def backoff_seconds(response, attempt):
    retry_after = response.headers.get("Retry-After", "")
    if retry_after.isdigit():
        return int(retry_after)
    return min(60, 2 ** attempt) + random.random()

Sleep for the returned time, retry, and give up after a few attempts rather than looping forever.

Common confusion

The tempting fix is to rotate to fresh IPs every time a 429 arrives. That spreads a per-IP limit, but it does not help when the limit is on your account or session, and it treats a clear request to slow down as an obstacle. Our allowed-use policy asks you to respect rate limits. Lower your concurrency per host first. The proxy error codes guide covers 429 alongside the other codes you will meet.