Technical SEO · Website troubleshooting
Find the rejected request, identify the limit and verify that the affected action works again.
By Rahul S., founder and SEO consultant at SearchCounselCo. Updated .
HTTP 429 Too Many Requests usually means a service is limiting your requests. Pause and inspect the response before retrying. Website owners should identify which component rejected the request. API callers should also read the provider’s error code: request-rate, concurrency and account-quota conditions can need different fixes.
Pause, follow available retry guidance and report persistent blocked access.
Capture one failure, match its evidence and correct the confirmed cause.
Read the response condition before choosing pacing, concurrency or account changes.
Capture one failed request before changing settings
A page may open while its login, form, AJAX call or inventory sync fails. Capture the request returning 429, rather than only the address in the browser’s address bar.
- Open developer tools and select Network before reproducing the action.
- Clear unrelated entries. Enable Preserve log if navigation would remove the request.
- Repeat the ordinary failing action once. Avoid repeatedly submitting an order, payment or booking.
- Enter
status-code:429in the filter box, then select the failed request. - Inspect Headers and Response. Record its URL, HTTP method, time/timezone, status, request ID and any
Retry-Aftervalue. Read the provider’s error condition when present. - Inspect the Initiator information where available. Match the request with the relevant security event, server log or integration record.
Chrome’s Network reference documents these inspection features. An initiator is the script or action that started a request; it is a clue to investigate, not automatic proof that the script caused the limit.
Keep credentials, cookies and customer data out of shared captures. If a backend makes the external call, the browser may show only your backend’s response; inspect that backend’s redacted provider record too.
If you are visiting a site you do not control
Stop repeated refreshes and follow a valid wait instruction. Retry an ordinary read-only page when appropriate. If access stays blocked, send the owner the time, affected action and non-sensitive request details. Check whether a submission succeeded before sending it again.
A comparison on another network can describe the affected scope. Clearing cookies may change a session identity, but neither action proves the original limit was repaired. A visitor cannot correct the owner’s firewall or provider-account configuration.
Optional: capture the response from one public GET
Replace the reserved example URL with a public page you operate. This performs one GET without automatic retries; it does not reproduce a logged-in submission.
curl --silent --show-error --output /dev/null --dump-header - \
--max-time 20 --write-out '\nhttp_code=%{http_code}\n' \
'https://example.com/'
Read the HTTP status printed at the end. A successful curl process exit does not necessarily mean the HTTP response succeeded. The 20-second client limit is not the server’s reset time.
Find the component that returned the 429
The HTTP 429 specification does not prescribe one way to count requests or identify users. Find the issuer—the component returning the rejection—and its counter key, meaning the identity used to group requests. That identity might be an IP address, account, token or another configured value.
On a small screen, scroll inside the table to see every column.
| Observed evidence | Responsible component to check | Targeted next action |
|---|---|---|
| Matching edge event for the incident | CDN or web application firewall | Inspect the matching rule, action and counting scope. |
| Matching rejection in origin/security logs | Hosting, reverse proxy or application | Identify the active rule, interval and counted client identity. |
| A specific WordPress action fails | Plugin, theme, application or upstream service | Trace that action and inspect the actual returned response. |
| Only a scheduled external job fails | Provider and integration caller | Read the provider condition and all jobs sharing its allowance. |
An error-page logo or Server header is not enough to identify a rule. Correlate time, path, method and client context. For the wider investigation, use the technical SEO guide.
Ask hosting which component rejected the request, what identity it counted and which other callers shared that identity. Behind a proxy, verify the intended visitor-IP handling using the provider’s restoration guidance. Trust the intended proxy chain rather than accepting any forwarded header as a client identity.
Keep different responses separate: 403 concerns denied access, 503 temporary unavailability, and 504 a gateway wait. Some rate limiters use a different response code. Server capacity and an overly broad rule need different remedies.
Fix the WordPress action that triggers the limit
Start with the failing path and task. A security plugin may reject an incoming request, while a product-sync plugin may receive 429 from an external API. Disabling the site firewall will not resolve an external provider’s exhausted allowance.
When login, AJAX or REST requests fail
- For
/wp-login.php, match the legitimate login attempt to the relevant security event. Establish whether one user, one network or a wider group is affected. - For
/wp-admin/admin-ajax.php, identify the action and browser initiator. Several unrelated features use that same handler. - For a
/wp-json/request, record its exact route, method and authentication context. Do not treat every REST request as the same operation.
When a scheduled job or external sync fails
Read the response from the external host and check the provider’s documented condition. List other jobs sharing that account or quota. Look for duplicate schedules, overlapping workers or retry policies before changing the website’s inbound protection.
A WordPress developer should distinguish a WP_Error object from an HTTP response. A received 429 is not established as success because is_wp_error() is false. Inspect the response status code and the intended application outcome.
Use the right evidence and isolate one component
- Save the incident record and note recent plugin, theme, deployment or rule changes.
- Use a backup and suitable staging environment. Record the active configuration and restoration steps.
- Isolate the implicated component or setting, then repeat the same action. Restore it if the evidence does not support that cause.
- Compare under the relevant cooldown and workload conditions. One 200 after a pause may only mean a rate window reset.
- Verify the saved result, next job cycle and continuing protection before calling the repair complete.
The WordPress troubleshooting documentation covers recovery procedures. If the dashboard is inaccessible, have the administrator use the host’s supported recovery tools. Changing every plugin at once can obscure the cause and interrupt unrelated features.
A PHP debug log is not a universal HTTP access log. Follow the WordPress debugging handbook for suitable diagnostics, keep errors off public pages and remove temporary logging when finished.
Developer note: HTTP API logging has boundaries
For relevant outbound calls, http_api_debug can help record a redacted response status and correlation details. It does not log a WAF rejection that happened before WordPress executed.
The current WP_Http::request source also returns early when pre_http_request supplies a response. That path precedes its usual later debug action. An injected mock can test application handling without proving that a network call or transport log occurred.
Worked WordPress example: a sync fails while product pages load
Illustrative workflow, not a measured WordPress test or customer incident.
- A product page opens, but an inventory-sync job records 429 from an external host.
- Inspect the provider condition and schedules. If evidence identifies duplicate jobs sharing an allowance, correct that duplication.
- Apply the provider’s permitted pacing or retry policy. If the condition concerns credit or concurrency, address that condition instead.
- Retest the actual sync. Check the stored inventory values and the next relevant job cycle, while keeping the site’s intended security controls.
Investigate a Cloudflare rate limit without removing all protection
Confirm which Cloudflare product is involved. Its 429 documentation distinguishes management-API calls, R2 public access and website end users. An API account quota is not a visitor rule for your website.
- Open the affected zone’s Analytics page and Events tab, following the current Security Events guidance.
- Narrow the time range and filter using the incident’s available host/path, client and request details.
- Expand a matching event. Record the service, action and rule, rather than relying on the visible error page alone.
- Inspect that rule in Security rules. Review its matching criteria, counting characteristics, threshold, period and action using the dashboard guide and parameter reference.
- If the evidence confirms a false positive, preserve the original settings, make a scoped correction and retest the affected legitimate path and intended protection.
Security Events can be sampled and does not list all traffic. Narrowing the time range may improve visibility; a missing event does not prove Cloudflare was uninvolved. One request can generate multiple events. A Ray ID is useful context, but is not guaranteed unique.
Available settings depend on the plan. Check whether cached requests count and how visitors are grouped. Follow the rate-limiting best practices, including consistent path interpretation between edge and application. Retain a rollback and the protection the rule was meant to provide.
For APIs, identify the condition before retrying
Read the response body, provider error code and relevant headers. The same status can represent different conditions. Use the provider’s documentation rather than a universal reset-time assumption.
On a small screen, scroll inside the table to see every column.
| Confirmed condition | Appropriate direction | Insufficient evidence |
|---|---|---|
| Request-rate or token-rate limit | Pace or queue the applicable callers; follow valid retry guidance. | A low average that hides short bursts. |
| Concurrent-request limit | Reduce simultaneous in-flight requests and follow provider recovery guidance. | An acceptable per-minute count by itself. |
| Credit, spend or usage condition | Resolve the reported account condition or follow its documented reset. | Repeated requests while that condition remains. |
| Resource-specific condition or lock | Read the provider’s exact reason and recovery policy. | Treating every 429 as one time window. |
| Reason is unclear | Collect the error code, usage context and provider request ID. | Changing unrelated settings. |
For example, Stripe documents distinct rate and concurrency reasons, plus provider-specific lock behavior. OpenAI documents temporary rate errors separately from exhausted credit and enforced spending or usage conditions. These are provider examples, not rules for every API.
Check who already retries the request
A job runner, application wrapper and SDK may each retry. Account for their combined behavior before adding another loop. OpenAI’s guidance notes existing SDK retries; AWS’s engineering guidance explains how retries can multiply across layers.
Choose a coordinated retry policy that bounds attempts and waits across the operation. Queue or schedule work for later when appropriate. Do not replace one uncontrolled loop with several independent ones.
Read Retry-After and decide whether repetition is appropriate
Retry-After can contain whole seconds or an HTTP date. It may be absent. A header does not explain every provider error or make every operation appropriate to repeat.
On a small screen, scroll inside the table to see every column.
| Received value | Meaning | Decision |
|---|---|---|
Retry-After: 2 |
Wait at least two seconds after receipt. | Do not retry earlier. |
Thu, 15 Oct 2026 00:00:45 GMT |
At 00:00:00 UTC on that date, the indicated wait is 45 seconds. | Use a reliable clock. |
| Missing or invalid value | No usable server delay was supplied. | Consult provider policy; use bounded backoff with jitter where appropriate. |
| 10-second wait; 0.4 seconds available | The server’s requested delay exceeds the remaining budget. | Stop or defer; do not shorten the wait. |
For eligible retries, spread callers with suitable jitter, coordinate shared quotas and bound attempts and waits. Do not assume that a new token creates a new allowance: the provider may count an account or another shared identity.
HTTP semantics define safe methods, PUT and DELETE as idempotent. Automatically repeating a non-idempotent operation needs suitable application semantics or evidence of its state. Use a provider’s documented idempotency mechanism when supported; inventing a header does not create that support.
Check the recorded outcome before resubmitting a payment, order or booking. The 429 specification also prohibits caching the 429 response itself. Caching eligible successful content to reduce origin work is a different action.
Why can 429 appear after one visible request?
The service may have counted earlier calls from another tab, job, worker or user sharing the same identity. One click may also start multiple requests, and the relevant window may be longer than the period you observed. These are hypotheses to verify against the actual rule or provider record.
Ask which counter the rejected request used and what contributed to it. In an earlier local shared-counter test, actor A used both permitted requests and actor B’s first request received 429. The actors were test labels, not real IP or NAT identities. That model illustrates a mechanism; it does not identify your site’s cause.
Measured examples: the same 429, different mechanisms
A controlled Python HTTP run on October 15, 2026 used a server bound to 127.0.0.1. It covered five scenarios with 23 requests and six passing assertions. No WordPress, Cloudflare, real API account or SDK was tested.
On a small screen, scroll inside the table to see every column.
| Local scenario | Observed result | What changed |
|---|---|---|
| One accepted GET per one-second window | 200, 429; wait 1.02 seconds; 200. | The modeled time window reopened. |
| Credit controlled by a test flag | 429, 429, 429; update the flag; 200. | Account state changed in the model. |
| Two simultaneous slots occupied | Two requests admitted; another probe 429; after release, probe 200. | Occupied slots became available. |
| Two retry layers, three attempts each | Nine downstream calls; all returned 429. | Independent layers multiplied attempts. |
| One retry owner, three attempts | Three downstream calls; all returned 429. | One owner bounded the calls. |
Two retry layers: 9 HTTP requests
One retry owner: 3 HTTP requests
The credit case used three attempts 20 ms apart, then an explicit test-state change. It does not prove that waiting can never resolve every account condition. The concurrency case held two admitted requests while probing a third. The retry layers were custom functions, not a measurement of SDK defaults.
Inspect a selected actual model record
{
"condition": "credit_exhausted",
"status": 429,
"retry_after": null
}
This record comes from the model’s credit-condition case. Its condition label is lab data, not a vendor error-code contract.
Reproduce the 9-versus-3 retry comparison
Save this complete example as retry_layers_demo.py and run it with Python 3.10 or later. It makes 12 loopback HTTP requests, prints the two counts and shuts down. It reproduces only the retry-layer comparison, not all five scenarios.
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread
from urllib.error import HTTPError
from urllib.request import ProxyHandler, build_opener
import json
class Handler(BaseHTTPRequestHandler):
def log_message(self, *_args):
pass
def do_GET(self):
self.send_response(429)
self.send_header("Content-Length", "0")
self.end_headers()
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = Thread(target=server.serve_forever, daemon=True)
thread.start()
url = "http://127.0.0.1:" + str(server.server_port)
opener = build_opener(ProxyHandler({}))
def count_calls(outer_attempts, inner_attempts):
calls = 0
for _ in range(outer_attempts):
for _ in range(inner_attempts):
calls += 1
try:
with opener.open(url, timeout=2) as response:
response.read()
raise RuntimeError("expected the lab's 429")
except HTTPError as error:
with error:
error.read()
if error.code != 429:
raise
return calls
try:
print(json.dumps({
"two_layers": count_calls(3, 3),
"one_owner": count_calls(1, 3)
}))
finally:
server.shutdown()
server.server_close()
thread.join(timeout=5)
A socket timeout is not a promised operation deadline
A separate local urllib probe set a 0.2-second timeout and received eight slowly supplied bytes. It completed in 0.406 seconds. In that test, the timeout value did not enforce an absolute elapsed-operation deadline.
Python’s documentation describes that timeout in terms of blocking operations. The local examples illustrate request and retry behavior; production clients need separate treatment of cancellation, slow reads, transport errors, response limits and operation semantics.
Check whether search crawlers receive the same failure
Google’s crawler documentation treats 429 as an overload signal and temporarily slows crawling. Persistently failing indexed URLs can eventually be dropped; there is no universal recovery time. Successful serving can allow crawling to increase gradually.
- Confirm which crawler and exact public URL received the failure.
- Verify claimed Google requests using Google’s documented methods, rather than a user-agent string alone.
- Correlate the failed request with hosting or CDN evidence and the relevant Search Console report.
- Correct the confirmed cause, then monitor subsequent real requests and successful serving.
Google retired Search Console’s manual Crawl Rate Limiter on January 8, 2024. Follow current crawler-response guidance and the crawl-error investigation rather than searching for that old control.
For other automated clients, the crawler-access guide separates permission, firewall decisions and actual fetch success. If the failed request is a sitemap, use the sitemap-fetch workflow. A visitor’s successful page load does not establish crawler access.
Verify the affected action and retain the intended protection
Retest the same permitted action, path and relevant identity after the targeted correction. One unrelated 200 response is not enough.
- Legitimate access: the affected user or client group can complete the original action.
- Application outcome: the save, sync or other operation produced the correct stored result.
- Confirmed cause: the relevant log, rule or caller behavior matches the correction.
- Protection retained: controlled staging checks or appropriate monitoring show intended excess is still handled.
- Relevant duration: observe the cooldown, workload period or next job cycle that originally failed.
Keep the original configuration and rollback record. If symptoms persist, return to the failed request rather than stacking speculative changes.
Copy this incident worksheet
Use this record when asking hosting, a developer or a provider to investigate. Replace the field labels with your details and remove secrets before sharing. This page does not submit a form.
Focus the field, select all and copy. On a phone, touch and hold the text to use your device’s selection controls.
Frequently asked questions
How long does a 429 error last?
Follow the valid Retry-After value and provider policy. There is no universal reset period. Some conditions need an account or configuration change, while a temporary rate window may reopen after waiting.
Will clearing the browser cache fix it?
It does not increase a provider quota or change an owner’s rate-limiting rule. Clearing cookies can change a session identity, so apparent recovery does not prove the original condition was repaired.
Should I upgrade hosting or disable every plugin?
Choose a remedy from the evidence. More capacity may help a measured capacity problem. It does not automatically fix a duplicate job, shared counter, external account condition or incorrectly scoped rule.
Why does a new API key still receive 429?
A provider can group requests by an account, organization, endpoint or another shared scope. Check the actual condition and counting policy instead of assuming each key has an independent allowance.
Can visitors load the page while a crawler or job fails?
Yes. They may use different paths, identities, rates or cache behavior. Capture the failed crawler or job request and verify its specific outcome.
The one thing to do next
Capture one failed request and identify the component and condition that explain it. If legitimate visitors or crawlers remain blocked, a technical SEO review can help trace the request path and organize the evidence needed for hosting or development.
Sources used
- RFC 6585: HTTP 429 and caching; RFC 9110: Retry-After; request semantics
- Chrome Network inspection
- Cloudflare Error 429; Security Events; rate-rule dashboard; parameters; Ray IDs; best practices
- WordPress HTTP API source; response status; debugging; troubleshooting
- Stripe rate/concurrency guidance; OpenAI 429 conditions; AWS retry guidance; Python timeout behavior
- Google crawler status handling; request verification; crawl-rate tool retirement
Documentation checked October 15, 2026. Local-model results are separated from illustrative WordPress and Cloudflare workflows. The runnable comparison above reproduces its stated subset; the models do not establish production-platform behavior.
