Short answers
What does HTTP 429 Too Many Requests mean?
The responding service is applying a request-rate limit. The status alone does not tell you whether it counts an account, a key, an address, a resource or another scope. Read the response details and that service’s documentation before deciding what to change.
Does a 429 mean the proxy password is wrong?
No. Proxy authentication and rate limiting are different checks. Identify whether the response came during the proxy connection or after the destination request, and use any reported error code. A bare 429 does not identify an authentication failure.
How long should I wait after a 429?
Follow the responding service’s retry guidance. Retry-After can express a delay in seconds or an HTTP date. If the required wait exceeds the job’s remaining time, stop or reschedule; do not shorten the server’s requested delay.
Will changing an IP solve a rate limit?
Do not treat an address change as permission to continue a refused workload. Limits can apply to other scopes, including an account or API key. Reduce demand and use the service’s permitted recovery process.
Identify which request received the response
Record the failing operation before changing settings. A shop API call, a proxy tunnel and a website request are different operations. In an HTTPS request through an HTTP proxy, a successful CONNECT only establishes the tunnel; the destination can still return its own response. Use the curl status check to keep the tunnel result, destination result and client error separate.
| Evidence | Next check |
|---|---|
| A 429 while establishing CONNECT | Read the proxy response and documented gateway code. Do not interpret this as a completed destination request. |
| CONNECT succeeded, then the HTTPS request received 429 | Inspect the destination response and its service rules. A successful tunnel does not grant a higher request allowance. |
| A Portproof API response with rate_limited | Read the API retry response and current limits. Keep this separate from traffic sent through a proxy. |
| A bare 429 with no reliable stage or code | Keep the cause unresolved. Collect a minimal redacted report instead of guessing the limiting scope. |
RFC 6585 defines 429 and allows a Retry-After field, but leaves the way a service identifies and counts requests to that service. Do not turn the status into a claim about an IP address, your subscription, or a particular limit without further evidence.
Read Retry-After before scheduling another attempt
HTTP semantics permits a non-negative whole number of seconds or an HTTP date. In the fictional examples below, 120 means a wait of at least two minutes; a date must be compared with a reliable clock. A missing field is not permission for an immediate retry.
| Input | Decision |
|---|---|
| Retry-After: 120; 300 seconds left in the job | The wait fits the budget. Check permission and remaining attempts before scheduling a retry. |
| Retry-After: 120; 30 seconds left | Stop or reschedule. Waiting only 30 seconds would shorten the required delay. |
| A valid date in the future | Wait until that time, allowing for clock accuracy and the remaining job budget. |
| Missing, malformed or conflicting values | Read the service-specific policy. Keep an automatic retry disabled until the decision is clear. |
| Zero or a date already passed | The header adds no future delay, but an attempt limit and any other service restriction still apply. |
Test the wait decision without making a request
This Python helper returns a wait or None. It does not send, repeat or authorise a request. Pass one unambiguous Retry-After field from a response you have already received, an aware current clock and the job’s remaining wait budget. The example accepts digits and the preferred GMT HTTP-date format; other date formats are left for review.
Fictional 120-second wait and 300-second job budget. The function never turns an excessive wait into a shorter one. Keep automatic retries off when it returns None.
import math
import re
from datetime import datetime, timezone
from email.utils import format_datetime, parsedate_to_datetime
def retry_delay(value, *, now, remaining_seconds):
"""Return a permitted wait, or None to stop and review."""
if now.tzinfo is None or not math.isfinite(remaining_seconds):
raise ValueError("Use an aware clock and a finite time budget")
if remaining_seconds < 0 or value is None:
return None
value = value.strip()
if re.fullmatch(r"[0-9]+", value):
# An enormous delay cannot fit this small-job example.
if len(value) > 18:
return None
delay = int(value)
else:
try:
date = parsedate_to_datetime(value)
if date.tzinfo is None:
return None
date = date.astimezone(timezone.utc)
# Accept the preferred HTTP date form; review other formats.
if format_datetime(date, usegmt=True) != value:
return None
delay = max(0, math.ceil((date - now).total_seconds()))
except (TypeError, ValueError, OverflowError):
return None
# Never shorten a server's wait to fit our job deadline.
return delay if delay <= remaining_seconds else None
# Fictional response value and job budget; no network request is made.
wait = retry_delay("120", now=datetime.now(timezone.utc),
remaining_seconds=300)
print("Stop and review" if wait is None else f"Wait at least {wait}s")The date conversion uses Python’s email utilities, followed by an explicit format check. This is a small decision helper, not a complete HTTP retry client. Production scheduling also needs an accurate wall clock, an elapsed-time deadline, an attempt budget and coordination between workers. Close or release the response before waiting for another request.
The published helper was tested on 23 October 2026 with fixed clock inputs and a local HTTP fixture. Cases include seconds, future and past dates, absent and malformed fields, conflicting values and waits exceeding the budget. The fixture sends synthetic 429 responses; it does not establish a Portproof gateway limit, an external service’s policy or success after a retry.
Reduce the work that causes repeated requests
- Pause the affected queue and check its configured worker count. Several processes sharing the same allowance need a shared schedule, not independent retry loops.
- Remove duplicate fetches and keep only the data the permitted task needs. Use the service’s documented caching, conditional requests or event delivery where available.
- Set a finite attempt budget and a job deadline. Count automatic library retries as well as retries in your own code so they do not multiply unnoticed.
- Resume only under the responding service’s policy. If limits remain unclear, keep the job stopped and ask that service for the supported approach.
For example, GitHub’s REST API guidance supplies its own waiting and bounded retry rules. Those details are specific to GitHub, not a universal timer for every site. Apply the documentation for your actual endpoint.
Use a detailed Portproof code only when it is reported
| Reported code | What to review |
|---|---|
| E_RATE_LIMITED_CONN | The gateway reports too many parallel connections. Reduce concurrent work and follow the response’s retry guidance. |
| E_SESSION_LIMIT | The gateway reports too many open sticky sessions. Check how your job creates and reuses named sessions. |
| rate_limited from the API | Check the API operation and returned retry guidance. A new proxy session is not a new API allowance. |
Keep session reuse tied to the intended task. The sticky-session guide explains the username controls and their limits. Do not create a new session for every retry simply to work around an unexplained response. The error directory routes authentication, connectivity and certificate problems to their own checks.
Keep a repeated read separate from an uncertain write
This guide does not implement automatic replay of purchases, payments, uploads or other state-changing operations. If an operation’s result is unclear, check its existing status and documented recovery mechanism before sending another one. A retry timer says when a request may be reconsidered; it does not make repeating an operation safe. HTTP method semantics explains why replay decisions depend on the operation.
Keep a small, useful diagnostic record
Record the UTC time, operation, client version, request stage, status, reported code, Retry-After value and whether other workers shared the same workload. Keep credentials, cookies, tokens, received codes and complete sensitive URLs out of the report. For a Portproof gateway response, add the scheme, host and port without credentials and use the support route in setup documentation.
Sources and scope
Primary references checked 23 October 2026: RFC 6585, RFC 9110, Python’s email utilities and GitHub’s REST API best practices, linked beside the relevant claims. Portproof code descriptions follow the shipped setup reference. The local fixture verifies this example’s decisions; no live rate limit was deliberately triggered.
What is not allowed
Run only tasks you are authorised to perform, respect the destination’s request limits and follow the acceptable-use policy.
Read next
How to test a proxy with curl
Prove the connection, the exit address and the rotation mode before you point a job at it.
Read the guide
Proxy errors: find the cause and next check
Match the exact error to its failing stage and follow one targeted check before changing credentials or retrying a workload.
Read the guide