🎉 Premium Proxies · 24-Hour Free TrialClaim Now
Guides

Google 'Unusual Traffic' Error With Proxies: Fix

Our systems have detected unusual traffic through proxies? Find the cause in order (IP rate, consent, query strings, subnet) and detect the sorry page in code.

S SparkProxy 0 19 min read
Share

"Our systems have detected unusual traffic from your computer network" is Google telling you that one exit IP, or the network around it, crossed a line it won't name. Through proxies, this usually hits a rank tracker halfway through a run, and the instinct is to rotate harder. That instinct is how a healthy pool gets burned in an afternoon. This guide shows how to read the page you actually received, check the causes in order of cost, and make your code back off the moment the sorry page appears.

The Exact Page You're Getting

We fetched Google's block page directly on 2026-10-30 to record its current wording and headers. This is what came back, with our own IP swapped for a documentation address:

HTTP/1.1 429 Too Many Requests
Cache-Control: no-store, no-cache, must-revalidate
Content-Type: text/html
Server: scaffolding on HTTPServer2

Our systems have detected unusual traffic from your computer network.
Please try your request again later.  Why did this happen?

This page appears when Google automatically detects requests coming from
your computer network which appear to be in violation of the Terms of
Service. The block will expire shortly after those requests stop.
...
Sometimes you may see this page if you are using advanced terms that robots
are known to use, or sending requests very quickly.

IP address: 203.0.113.47
Time: 2026-10-30T13:04:52Z
URL: https://www.google.com/search?q=test

Four details on that page do most of the diagnostic work:

  • The status is 429, not 200. Our capture returned HTTP 429 with the HTML body. Some tutorials say the sorry page arrives as a 200; if you see a 200 with this text, you are probably reading a rendered browser page after the redirect, not the raw response.
  • The path is /sorry/index. Blocked search requests land at https://www.google.com/sorry/index?continue=..., with your original search URL in continue.
  • The IP address: line names the exact IP Google blamed. Through a proxy, this should be the proxy's exit IP. If it shows your server's own address, your proxy isn't being used at all, and no amount of rotation will help.
  • "The block will expire shortly after those requests stop." Google's own wording. It doesn't give a duration, so don't trust anyone who quotes one. It does tell you the remedy is to stop sending from that IP, not to retry it.

Google's help article on the error adds that the page "most likely shows a reCAPTCHA", and that a network, "including VPN networks", can trigger it when it "seems to be sending automated traffic to Google Search". Our direct fetch showed the "try your request again later" variant without a challenge, so expect both.

Free trial

Scraping at scale? Skip the blocks.

Fast, unblockable datacentre proxies with unlimited bandwidth.

Diagnosis Order, Cheapest Check First

Run these in order. Each step takes less time than the next and rules out a whole class of cause.

  1. Confirm which page it is (table above). Thirty seconds with curl -i.
  2. Confirm the exit IP. Compare the sorry page's IP address: line with the IP your proxy should be using. A mismatch means a proxy configuration bug, not a block.
  3. Read the failing URL. The URL: line on the sorry page shows the exact query. Look for stacked operators or parameters you didn't mean to send.
  4. Check the cookie jar. Is the session carrying cookies between requests, and does a request from an EU exit hit consent.google.com first?
  5. Measure rate per IP. Count queries per exit IP over the last ten minutes, from your own logs.
  6. Group blocks by subnet. If sorry pages cluster on one /24 even at low rates, the range is the problem.

Here's step 1 and step 2 in one go, through SparkProxy's rotating port:

# What did Google send back, and where did it redirect?
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" \
  -x "http://USER:PASS@gateway.sparkproxy.io:11000" \
  "https://www.google.com/search?q=rank+tracker&hl=en&gl=us"

# Which IP does Google see? The sorry page prints it.
curl -s -x "http://USER:PASS@gateway.sparkproxy.io:11000" \
  "https://www.google.com/sorry/index" | grep -o "IP address: [0-9a-fA-F:.]*"

The second command is a cheap trick: when we requested /sorry/index directly on 2026-10-30 without being blocked, Google still rendered the page, including the IP address: line. That's one observation, not documented behaviour, so treat it as a convenience rather than a contract.

Cause 1: Too Many Queries From One IP

Google's page says it plainly: "sending requests very quickly." The variable that matters is queries per exit IP over time, not your total query count. Ten thousand queries spread across a large pool can be quieter, per IP, than three hundred from one address.

Google publishes no safe rate, and we won't invent one. What you can do is measure your own threshold. Pick a conservative starting interval per IP, log every response with its exit IP, and let the sorry rate tell you where the line is for your query mix. The control loop in Back Off Instead of Burning the Pool does exactly that.

Two mistakes make the per-IP rate far higher than you think:

  • Retries on the same IP. A retry loop that reuses a Sticky Session port fires the next attempt from the IP that was just blocked. On SparkProxy, port 11002 keeps one IP for a five-minute window (Sticky Sessions), which suits paginating one keyword but is the wrong place to retry a block. Retries should go out through port 11000 (Random Rotation), which hands each request a different IP.
  • Pagination bursts. Fetching pages 1 to 5 of a keyword in quick succession from one sticky IP is five fast queries. Spread them, or accept a shallower depth.

Sizing the pool from a target per-IP rate is its own piece of arithmetic, covered in proxies for SERP scraping at scale. This section is about the moment it has already gone wrong.

Cause 3: Query Strings Google Reads as Robotic

The sorry page names this cause in its own words: "advanced terms that robots are known to use". Rank trackers and SEO tools generate exactly those queries. Common offenders:

  • Stacked operators like inurl: with intitle: and site: on every query in a batch, which no ordinary user sends at volume.
  • Leftover parameters copied from an old tool, such as num=100. Google stopped honouring that reliably in September 2025, as explained in our guide to scraping Google search results, so it adds risk and returns nothing extra.
  • Mismatched locale signals, for example gl=de with hl=en from a US exit. Keep IP country, gl and hl consistent, the same rule datacenter proxies for SEO rank tracking applies to local rank checks.

The sorry page's URL: line makes this easy to audit. Pull the last fifty blocked URLs from your logs and look for a shared pattern. If most of them carry operators and your plain keyword queries sail through, the query text is the cause and no proxy upgrade will change it.

Cause 4: A Flagged Subnet

Google's help page lists shared networks, shared VPNs and shared internet providers as reasons the message appears for someone who did nothing wrong. It even advises network administrators to test IP space before buying it from an IP broker. The proxy version of this is a range whose reputation was spent before you arrived.

The signal is clustering. If sorry pages concentrate on one /24 while other ranges at the same rate stay clean, the range is the cause. The script below groups blocks by subnet using the IP address: lines you've already logged.

import ipaddress
from collections import Counter


def subnet_of(ip: str) -> str:
    addr = ipaddress.ip_address(ip)
    prefix = 24 if addr.version == 4 else 48
    return str(ipaddress.ip_network(f"{ip}/{prefix}", strict=False))


def block_rate_by_subnet(rows):
    """rows: iterable of (exit_ip, was_blocked) tuples from your logs."""
    sent, blocked = Counter(), Counter()
    for ip, was_blocked in rows:
        net = subnet_of(ip)
        sent[net] += 1
        if was_blocked:
            blocked[net] += 1
    return sorted(
        ((net, blocked[net] / sent[net], sent[net]) for net in sent),
        key=lambda r: r[1],
        reverse=True,
    )


sample = [("203.0.113.7", True), ("203.0.113.9", True), ("198.51.100.4", False)]
for net, rate, n in block_rate_by_subnet(sample):
    print(f"{net:20} {rate:6.1%} of {n}")

This is the one cause a proxy provider can genuinely fix. Report the clustered ranges with timestamps and the exact sorry-page lines, and ask for a different range. If every range you try clusters at a modest rate, the pool type is the issue, and testing a different one (for example the Scraping API with proxy_type=premium, which the docs price at 10 credits without JavaScript and 25 with it) is the next experiment.

Detect the Sorry Page in Code

Detection has to run on every response, before parsing, and it has to distinguish the four outcomes. A single "blocked or not" boolean throws away the information that tells you what to do next.

import re
from dataclasses import dataclass
from typing import Optional

SORRY_TEXT = "our systems have detected unusual traffic"
IP_LINE = re.compile(r"IP address:\s*([0-9a-fA-F:.]+)")


@dataclass
class Verdict:
    kind: str  # "sorry", "consent", "js_gate", "candidate" (or "api_limit" below)
    flagged_ip: Optional[str] = None


def classify(status: int, final_url: str, html: str) -> Verdict:
    lowered = html.lower()
    if "/sorry/" in final_url or status == 429 or SORRY_TEXT in lowered:
        match = IP_LINE.search(html)
        return Verdict("sorry", match.group(1) if match else None)
    if "consent.google." in final_url:
        return Verdict("consent")
    if "/httpservice/retry/enablejs" in lowered:
        return Verdict("js_gate")
    # Not a known block page. Your SERP parser still has to find results;
    # zero organic results on a common keyword should be logged as suspect.
    return Verdict("candidate")

Wire it into the fetch so a redirect to /sorry/index is caught even when your HTTP client follows it silently. With requests, response.url is the final URL after redirects:

import requests

PROXY = "http://USER:PASS@gateway.sparkproxy.io:11000"


def fetch_serp(session: requests.Session, query: str, gl: str = "us", hl: str = "en"):
    resp = session.get(
        "https://www.google.com/search",
        params={"q": query, "gl": gl, "hl": hl},
        proxies={"http": PROXY, "https": PROXY},
        timeout=30,
    )
    return classify(resp.status_code, resp.url, resp.text), resp.text

A plain requests client like this one will mostly return js_gate today, as our 25-request test showed. Keep the classifier anyway: you'll reuse it unchanged with a browser or with the Scraping API below, and it's what makes the back-off logic possible.

Back Off Instead of Burning the Pool

The pool-burning pattern looks like this: one IP gets a sorry page, the code retries immediately on a new IP at the same speed, that IP gets blocked too, and the run grinds through hundreds of addresses in minutes. Every one of those IPs now carries a fresh mark.

Two separate controls stop it. A per-IP quarantine respects Google's "expire shortly after those requests stop", and a global pace adjusts the whole job when blocks start appearing anywhere. The second is additive-increase, multiplicative-decrease, the same idea TCP uses for congestion.

import random
import time


class Pacer:
    """Global request pacing that slows sharply on blocks and recovers slowly."""

    def __init__(self, interval=6.0, floor=3.0, ceiling=300.0):
        self.interval = interval  # seconds between requests, across all IPs
        self.floor = floor
        self.ceiling = ceiling

    def wait(self):
        time.sleep(self.interval * random.uniform(0.7, 1.3))

    def on_success(self):
        self.interval = max(self.floor, self.interval - 0.1)

    def on_block(self):
        self.interval = min(self.ceiling, self.interval * 2)


class Quarantine:
    def __init__(self, cooldown=1800):
        self.cooldown = cooldown  # seconds; our assumption, Google publishes none
        self.until = {}

    def add(self, ip):
        if ip:
            self.until[ip] = time.time() + self.cooldown

    def is_benched(self, ip):
        return self.until.get(ip, 0) > time.time()

The starting numbers are placeholders for you to tune against your own logs, not thresholds we measured. A small driver loop ties the pieces together and stops the job outright if blocks keep coming:

def run(queries, session, max_consecutive_blocks=5):
    pacer, bench = Pacer(), Quarantine()
    streak = 0
    for query in queries:
        pacer.wait()
        verdict, html = fetch_serp(session, query)
        if verdict.kind == "sorry":
            bench.add(verdict.flagged_ip)
            pacer.on_block()
            streak += 1
            if streak >= max_consecutive_blocks:
                raise RuntimeError("Repeated sorry pages: stop the job and investigate")
            continue
        streak = 0
        if verdict.kind in ("consent", "js_gate"):
            print(f"{query}: {verdict.kind}, fix the client, not the IP")
            continue
        pacer.on_success()
        yield query, html

The circuit breaker matters more than the pacing. Five sorry pages in a row means something structural changed (a bad range, a query pattern, a broken config), and the cheapest thing you can do is stop spending IPs until a person looks. With Random Rotation, the gateway picks the next exit IP, so the quarantine table is mainly a record you report to support rather than a list you route around yourself.

Running Google Queries Through the SparkProxy Scraping API

If the JavaScript gate is your real blocker, the Scraping API removes it: render_js defaults to true, so each request runs in headless Chromium and costs 5 credits. country_code pins the exit country for 5 more. Ask for json_response=true to get the envelope with the target's status_code, and decode the body, which is base64, before you look for any marker.

import base64
import requests

API = "https://scrape.sparkproxy.io/api/v1"
KEY = "YOUR_API_KEY"


def serp_via_api(query: str, country: str = "us"):
    target = requests.Request(
        "GET", "https://www.google.com/search", params={"q": query, "gl": country, "hl": "en"}
    ).prepare().url
    resp = requests.get(
        API,
        headers={"X-API-Key": KEY},
        params={"url": target, "country_code": country, "json_response": "true"},
        timeout=120,
    )
    if resp.status_code == 429:
        # The API's own rate or concurrency limit, not Google's sorry page.
        return Verdict("api_limit"), ""
    resp.raise_for_status()
    env = resp.json()
    html = base64.b64decode(env["body"]).decode("utf-8", "replace")
    status = env.get("status_code") or 0
    return classify(status, target, html), html

Two traps to avoid here. First, format=json is content conversion (headings, links, metadata), not the envelope; only json_response=true returns status_code and body. Second, a 429 from the API itself means you've hit your plan's rate or concurrency limit, which the docs pair with a retry_after_seconds value. It is a different problem from Google's 429, and your code should keep them apart, as the function does. The docs also list a 530 for a failed scrape, including an anti-bot block, with credits refunded, which raise_for_status() surfaces as an exception.

We couldn't run this function live on 2026-10-30 because the API endpoint was returning a Cloudflare 521 during our test window, so the snippet is written against the published parameter reference rather than a captured response.

What a Proxy Can and Cannot Fix

Be clear about this before buying more IPs. More addresses solve one cause well and the rest not at all.

CauseDoes a proxy fix it?The actual fix
Too many queries per IPYes, by spreading loadRandom Rotation, lower per-IP pace, no retries on a sticky IP
Flagged subnetYesDifferent range or pool type, reported with sorry-page evidence
Proxy not in use (your own IP on the sorry page)NoFix the client's proxy configuration
JavaScript gateNoReal browser or a rendering API
Missing consent stateNoPersistent cookie jar, handle `consent.google.` separately
Robotic query stringsNoDrop stacked operators and dead parameters
Total volume against Google's termsNoReduce volume; use Search Console for your own site's data

For the causes a proxy does fix, SparkProxy plans start from $75/month (USD, read from sparkproxy.io on 2026-10-09) with unlimited bandwidth, so you pay for the plan and its concurrency, not for traffic. The pool is 1M+ premium IPs across 80+ countries over HTTP/HTTPS and SOCKS5, with user:pass or IP whitelist auth. There's a 24-hour free trial with no credit card, which is enough time to run the subnet grouping above against your own keyword set. If CAPTCHAs, rather than sorry pages, are your main issue, how to avoid CAPTCHAs when web scraping covers that side.

Frequently asked questions

FAQ

Google has decided that requests from the proxy's exit IP, or from the network around it, look automated. The page is served at /sorry/index with HTTP 429 in our 2026-10-30 capture, and its IP address: line shows which IP was blamed.

Google doesn't publish a duration. The page itself says "The block will expire shortly after those requests stop", so stop sending from that IP and give it a cooldown you tune from your own logs.

Either the IP's range was already flagged by earlier users, or your queries trip the "advanced terms that robots are known to use" check. Group blocks by subnet and audit the blocked URLs to tell the two apart.

No. In our test, a plain HTTP client received HTTP 200 with a meta refresh to /httpservice/retry/enablejs, which is Google's JavaScript gate. A new IP won't fix it; a browser or a rendering API will.

No. Retrying from the blocked IP extends the block. Send the retry through Random Rotation after a delay, and stop the job entirely if several sorry pages arrive in a row.

They can help when the cause is a flagged range, because the exits sit on consumer ISP networks instead of hosting ranges. They don't help with the JavaScript gate, missing consent state, or robotic query strings.

Special Discount · 20% off

Get 20% off your first month

Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.

Save up to 15% more on quarterly, half-yearly and yearly plans

Claim Discount

About the Author

SparkProxy Technical Team runs SparkProxy's proxy network (1M+ premium IPs in 80+ countries over HTTP/HTTPS and SOCKS5) and the SparkProxy Scraping API. The sorry-page wording, headers and JavaScript-gate behaviour in this guide were captured by us from Google on 2026-10-30 with curl from a single server IP.

Sources (read 2026-10-30): Google Search Help: "Unusual traffic from your computer network" · Google cookie policy (SOCS, NID, AEC) · SparkProxy Scraping API reference

Keep reading

Related articles