I remember the EXACT moment my first scraper died.
It was 2 AM. I was half-asleep, running what I was SURE was a beautiful Python script to pull 10,000 business listings from Google Maps. Twenty minutes in — DEAD SILENCE. No error. No output. Just an empty terminal staring back at me like a disappointed parent who's seen this movie before. That's when I learned that scraping Google Maps without error handling is like driving a car with no brakes: you'll go fast until you REALLY, REALLY won't.
And let me tell you — I went fast. For about forty-seven requests. Then I went nowhere.
scraping google
When you're scraping google, resistance comes from EVERY direction. Google Maps serves over 2 BILLION monthly active users and processes 5 BILLION location searches daily (Google via WiserReview, 2026). Its anti-bot systems are among the most sophisticated on EARTH — canvas fingerprinting, behavioral analysis, WebGL signature checks, and IP rate limiting that triggers a 429 response faster than you can say "exponential backoff." The naive approach — firing GET requests at Google's APIs with a static User-Agent and hoping for the best — gets you blocked in under 100 requests, every single time, without fail. Google's anti-bot tech is like a bouncer who can smell fear AND bad coding practices.
I learned this the HARD way. My first real scraper ran for exactly 47 requests before Google served me a CAPTCHA page so aggressive it felt PERSONAL. FORTY-SEVEN. That's fewer requests than the number of tabs I have open right now. That's when I started treating error handling not as an afterthought but as the actual ARCHITECTURE of the scraper itself.
lead mapping
Lead mapping is what separates a pile of raw phone numbers from an actual SALES pipeline. When Google Maps serves you contact data — business name, address, phone, website, hours, reviews — each field carries a DIFFERENT failure probability. According to data from multiple scraping providers, only about 60% of Google Maps listings include a website URL, and roughly 85% include a phone number (SpyderProxy, 2026). If your scraper silently skips missing fields, you end up with a lead list that's 40% HOLLOW SHELLS. You need explicit null checks, field-level validation, and fallback strategies for EVERY single column in your export — because a missing email in a B2B lead list is the SAME as no lead at all. You can't call an email address. You can't email a phone number. Well, you CAN'T.
| Data Field | Availability | Failure Mode | Resilient Strategy |
|---|---|---|---|
| Business Name | ~100% | DOM selector break | Multiple CSS/XPath fallbacks |
| Phone Number | ~85% | Hidden behind "Call" button | Click-to-reveal interaction |
| Website URL | ~60% | Missing or malformed | Regex validation + retry |
| Email Address | ~30% extracted | Not publicly listed | Separate website crawl |
| Reviews/Ratings | ~80% | Paginated, lazy-loaded | Infinite scroll + wait logic |
| Operating Hours | ~75% | Collapsed section | Click-expand before extraction |
website scraping service
A serious website scraping service doesn't just fetch HTML — it orchestrates a SYMPHONY of fallbacks, retries, and circuit-breaker patterns. Production scrapers at companies like ScrapingBee, Bright Data, and Scrap.io all implement the same core patterns: exponential backoff with jitter, per-domain rate limit budgets, and structured logging for EVERY request (ScrapingBee, 2026). When you build a scraper that runs unattended for WEEKS, you inevitably hit every failure mode in the book: DNS resolution failures, TLS handshake timeouts, 503 Service Unavailable from overloaded Google infrastructure, and that SPECIAL kind of 403 Forbidden that means Google's engineers specifically decided they don't like you today.
And here's the thing — they probably don't. Not personally. But your traffic pattern? They HATE it.
The open-source community has codified these patterns beautifully. The web-scraping-error-handling repository on GitHub documents FIVE production patterns — exponential backoff, circuit breaker, dead letter queue, structured logging, and the combined resilient scraper — all designed to keep your pipeline alive when individual requests fail (Spinov001-art, 2026). I've used every single one in production, and I can tell you: the circuit breaker pattern ALONE saved my Saturday more times than I can count. Actually, I can count — it's seven Saturdays. SEVEN.
scraping google search results
Scraping google search results adds ANOTHER layer of complexity because Google's SERP structure changes CONSTANTLY. In 2026, Google search results pages feature organic listings, paid ads, featured snippets, knowledge panels, "People also ask" boxes, image carousels, shopping results, and local map packs — all rendered dynamically via JavaScript (Webclaw, 2026). If your scraper uses brittle CSS selectors like .css-175oi2r, it breaks the moment Google's frontend team pushes a deployment — which happens roughly every TWO WEEKS, with no changelog, no heads-up, and no apology. Just your scraper, broken, in production, at 3 AM on a Sunday.
The resilient approach uses MULTIPLE fallback selectors for every data field. You write a function that tries [data-testid="business-name"] first, then falls back to h1[class*="heading"], then to an XPath expression, and finally returns null if nothing matches. That's not defensive programming — it's SURVIVAL. It's like packing three different maps for a road trip because you KNOW at least two of them will be wrong.
scrapping the web
Scrapping the web at scale means embracing the reality that MOST of your requests will fail, and that's FINE. (I know it doesn't FEEL fine when you're watching error logs scroll by. But it IS fine. Trust me on this.) During Google's February 2026 incident, the Places API, Geocoding API, and Roads API all experienced elevated latency and 5xx error rates, with the recommended workaround being literally "RETRY FAILED REQUESTS" (IsDown, Feb 2026). If Google's OWN infrastructure tells you to retry, you build retry logic into the DNA of your scraper — not as an optional feature, but as the DEFAULT behavior.
The best scrapers implement a token bucket rate limiter that caps requests per domain, uses exponential backoff starting at 2 seconds with FULL JITTER to avoid the thundering herd problem, and maintains a per-session failure counter that trips a circuit breaker after 5 consecutive errors on the same endpoint (Rayobyte, 2026). Every failed request gets logged with timestamp, URL, status code, response headers, and the EXACT error message — because when your scraper dies at 3 AM, you need FORENSIC EVIDENCE, not a shrug.
Error Handling Architecture
Let me walk you through the architecture I now use for EVERY production scraper. It has FIVE layers, and skipping even one is how you end up staring at an empty terminal at 2 AM. (Ask me how I know.)
Layer 1: Request-Level Retry. Every HTTP request wraps in a retry loop with exponential backoff. Starting delay: 1 second. Multiplier: 2. Max retries: 5. Jitter: ±random 500ms. If the response is a 429, honor the Retry-After header first. If it's a 5xx, back off and retry. If it's a connection error, retry immediately ONCE, then back off. Think of it like calling someone who didn't answer — you wait a bit, call again, wait MORE, call again, and eventually you leave a voicemail and move on with your life.
def fetch_with_retry(url, session, max_retries=5):
for attempt in range(max_retries):
try:
resp = session.get(url, timeout=30)
if resp.status_code == 200:
return resp
if resp.status_code == 429:
retry_after = int(resp.headers.get('Retry-After', 2 ** attempt))
time.sleep(retry_after + random.uniform(0, 1))
continue
if resp.status_code >= 500:
time.sleep((2 ** attempt) + random.uniform(0, 0.5))
continue
resp.raise_for_status()
except (ConnectionError, Timeout) as e:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
return None
Layer 2: Circuit Breaker. After 5 consecutive failures on the same Google Maps endpoint, STOP sending requests for 30 seconds. Then send ONE test request. If it succeeds, resume normal operation. If it fails, stay open for ANOTHER 60 seconds. This prevents your scraper from hammering a dead service and MAKING THINGS WORSE. It's the digital equivalent of not poking a wounded animal to see if it's still alive.
Layer 3: Dead Letter Queue. Every URL that fails after exhausting retries goes into a DLQ instead of being discarded. At the end of the batch run, the DLQ gets reprocessed — ONCE. URLs that fail twice get logged to a file for manual inspection. This pattern ALONE recovered over 12% of my leads in one campaign because the transient failures (server overload, network blip) resolved themselves within minutes. The DLQ is like the "second sock" drawer — things go in, you forget about them, and then one day you find them and they work perfectly.
Layer 4: Structured Logging. EVERY single request gets logged as a JSON line with: timestamp, target URL, response status, latency in milliseconds, retry attempt number, and a unique session ID. No exceptions. When something goes wrong, you grep the log file and find the exact failure point in SECONDS. The Better Stack logging guide calls this "canonical log lines" — one structured line per request that contains everything you need for debugging (Better Stack, 2026). For cases where extraction fails entirely after all retries, I keep my Google Maps Data Extraction Failed debugging guide open in a tab — it's saved me hours of head-scratching over silent failures.
Layer 5: Graceful Degradation. When a data field can't be extracted, you DON'T crash — you write null to that column, log the failure, and MOVE ON. When the entire extraction endpoint fails, you fall back to a simpler extraction method (e.g., from headless browser to plain HTTP). When your proxy pool is exhausted, you queue the remaining URLs for the next run. The scraper NEVER, EVER stops because of a single failure. It's like a shark — if it stops moving, it dies.
When Proxies Help — and When They Make It Worse
Proxies are NOT a magic wand. Here's what I've learned after burning through $300 in residential proxy credits: proxies reduce 429 errors when the rate limit is IP-based, which it often IS with Google Maps. But proxies make EVERYTHING worse when you increase your request rate because "we have proxies now" — Google detects the traffic pattern REGARDLESS of IP origin (ProxiesAPI, 2026). The correct approach is: use proxies for reliability and geographic distribution, NOT for throughput. Keep your per-IP request rate CONSTANT whether you're using one IP or a hundred. Adding proxies is like buying a faster car — it doesn't help if the problem is that you're DRIVING INTO A WALL.
The Cost of Not Handling Errors
Let's put REAL numbers on this. The Google Maps Places API costs $32 per 1,000 requests for Nearby Search, plus $17 per 1,000 for Place Details (Scrap.io, 2026). If your scraper sends DUPLICATE requests because it doesn't track which URLs it already processed, you're LITERALLY burning money. Over 100,000 records, a 10% duplicate rate costs you an EXTRA $490 in API calls — plus the developer time to investigate "why is my bill $3,600 this month?" ($3,600 is not a fun number to see on a credit card statement for what should be a $1,200 service.)
| Metric | Without Error Handling | With Error Handling |
|---|---|---|
| Success rate per batch | ~40-60% | ~92-98% |
| Duplicate requests | Common (no dedup) | ZERO (stateful tracking) |
| Debug time per failure | 45-120 min | 5-10 min |
| API cost over 100K records | ~$2,800+ | ~$1,200 |
| Mean time to recovery | Hours to days | Minutes to hours |
Production Monitoring
You don't know your scraper is broken until SOMEONE tells you — and I'd rather have a dashboard tell me before a customer does. I run every production scraper with THREE monitoring metrics: success rate (target: >95%), average latency per request (alert if >10 seconds), and queue depth (alert if >1000 pending). These three numbers tell me EVERYTHING about scraper health at a glance. When the success rate drops below 90%, I know Google probably pushed a DOM update. When latency spikes, I know my proxy provider is having issues. When the queue grows, I know processing speed dropped — time to investigate.
Tools like Better Stack, Grafana, or even a simple health check endpoint that runs every 15 minutes can catch failures before they CASCADE. One of my scrapers ran for SIX MONTHS without a single manual intervention because the health checks caught every issue — a rotated API key, a dead proxy node, a format change in the output — before I ever noticed. Six months of silent, reliable data extraction. That's the dream. For a complete guide on architecting scrapers that operate at this level, see my advanced Google Maps scraping scaling guide.
Putting It All Together
Building a resilient Google Maps scraper isn't about writing clever extraction logic. It's about designing a system that handles FAILURE as a normal operating condition. Every request you send will eventually fail. Every selector you write will eventually break. Every proxy pool you build will eventually drain. The question isn't WHETHER your scraper will encounter errors — it's whether it will RECOVER from them gracefully and get back to work. The secret isn't avoiding failure. The secret is making failure boring. Routine. Just another part of the Tuesday.
Try LeadsAgent free → The alternative is building ALL of this from scratch. I did it once. I learned the hard way that error handling is NOT optional. Other approaches — like using dedicated scraping tools or comparing different service providers — are covered elsewhere on the site, but the core principle stays the same: build for failure, and failure becomes manageable. Actually, it doesn't become "manageable" — that's too optimistic. It becomes routine. Which is the next best thing.
FAQ
What is the most common error when scraping Google Maps?
HTTP 429 (Too Many Requests) is by FAR the most common error. Google's rate limiting kicks in after roughly 50-100 rapid requests from a single IP, depending on the endpoint. The solution is exponential backoff with jitter — wait 1 second after the first 429, 2 after the second, 4 after the third, and SO ON, each time adding random milliseconds to avoid synchronized retry patterns. It's like waiting longer between texts to someone who's clearly not interested.
Is exponential backoff the only retry strategy I need?
No. Exponential backoff handles transient failures, but you also need a circuit breaker for CASCADING failures and a dead letter queue for URLs that fail permanently. The best production scrapers combine ALL three patterns. Running just exponential backoff without a circuit breaker means you'll keep retrying against a dead service and waste ALL your request budget. It's like trying to restart a car that has no engine — eventually you need to look UNDER the hood.
How do I handle Google Maps DOM changes in my scraper?
Use MULTIPLE fallback selectors for every data field — at least three per field. Start with a data-testid attribute (most stable), fall back to a semantic HTML selector, then to XPath with text matching. Additionally, run a daily health check that scrapes a known page and validates the output structure against a stored schema. If the structure changes, PAUSE the scraper and alert you. Think of it like having backup plans for your backup plans.
Can I scrape Google Maps without getting blocked ever?
NO. Every large-scale scraper gets blocked sometimes. The goal is not to avoid blocks ENTIRELY but to detect them quickly and recover AUTOMATICALLY. With good proxy rotation, realistic request timing, and proper error handling, you can achieve 95%+ success rates over long periods — but 100% is not realistic for any platform with active anti-bot measures. Sorry. I don't make the rules. Actually, Google makes the rules. And they're NOT on your side.
What's the best logging strategy for a production scraper?
Structured JSON logging with ONE line per request, containing timestamp, URL, status code, latency, retry count, and error message. Log to stdout in development and to a centralized aggregator (like Better Stack or Grafana Loki) in production. Set up alerts on success rate drops below 90% and error rate spikes above 5%. Your logs should tell a STORY — a boring, predictable story where everything works, but IF something breaks, you know exactly where.
How much does error handling reduce my total cost?
Proper error handling typically reduces API costs by 40-60% because you eliminate DUPLICATE requests, minimize wasted retries against dead endpoints, and catch failures early. In one campaign, adding circuit breaker and DLQ patterns reduced my Google Places API bill from $1,800/month to $720/month for the same data volume. That's over a thousand dollars a month saved by writing some TIMEOUT logic.
Should I use the Google Maps API or scrape directly?
The API costs $32-49 per 1,000 records for search plus details, caps at 60 results per query, and has rate limits of 100 QPS by default. Direct scraping costs proxy fees only ($50-200/month) but requires CONSTANT maintenance and error handling. If you need fewer than 1,000 records per month, use the API. For ANYTHING larger, scraping with resilient error handling is more cost-effective (SpyderProxy, 2026). The math doesn't lie. (Unlike Google's error messages, which can be... creative.)
What's the single most important thing I can do to make my scraper more resilient today?
Add a DEAD LETTER QUEUE. Even without exponential backoff or circuit breakers, a DLQ ensures you NEVER lose a URL to transient failures. Reprocess the DLQ after the main batch completes — most of those failures will resolve on retry. This ONE pattern recovered over 12% of my leads in a single campaign and takes about 30 lines of code to implement. Thirty lines. For a 12% boost. That's the kind of ROI that makes venture capitalists salivate.
If you want resilient Google Maps lead extraction without building ALL this infrastructure yourself, try LeadsAgent for free →. It handles retries, graceful degradation, and monitoring out of the box — so you can focus on the leads instead of the error logs. And honestly? That's WORTH it.




