How to Schedule Recurring Google Maps Scrapes with Webhooks

A complete technical guide to keeping local business lead pipelines fresh automatically. Set up weekly or monthly cron triggers, listen for instant webhook callbacks, and append new prospects directly into your database.

Why Local Business Data Goes Stale (The Lead Decay Problem)

B2B sales teams and lead generation agencies often treat prospect lists as static spreadsheets. However, local business databases decay at an astonishing rate. Every single month across North America and Europe:

New Entrants & Openings

Roughly 8–12% of local service companies (roofers, HVAC, solar, dental clinics) open their doors or launch second locations each quarter. Reaching them first yields the highest conversion rates.

Closures & Rebranding

Businesses relocate, rebrand, or permanently shutter. Outdated contact lists waste SDR calling hours and cause email domain reputation hits due to bounced cold emails.

Review Velocity & Reputation

Customer ratings shift weekly. A contractor rising from 3.8 to 4.7 stars with 80 new reviews indicates aggressive growth and budget for your software or marketing services.

Website & Domain Changes

Local companies upgrade from generic free websites to custom domains and professional email systems, unlocking new direct channels for outbound sales teams.

Instead of doing sporadic manual exports every few months, top-performing agencies set up scheduled recurring scrapes. By automating weekly searches across their core territories, they capture newly listed companies within days of appearing on Google Maps.

Manual Scraping vs. Scheduled Recurring Webhooks

How does a manual workflow compare to an automated webhook architecture? Let's break down the operational differences:

Workflow Step Manual Export Workflow Scheduled Webhook Pipeline
Execution Trigger Someone remembers to log in, type search queries, and click export. Zero-touch cron timer (e.g., Monday 7:00 AM) triggers an automated API call.
Job Notification Waiting for browser downloads or repeatedly checking status screens. Instant HTTP POST webhook callback arrives the exact second extraction finishes.
Duplicate Handling Manual Excel VLOOKUP formulas to remove businesses already in your CRM. Bolt Scraper automatically deduplicates results using unique Google place_ids.
Lead Delivery CSV files downloaded to local hard drives and manually imported. Direct streaming into Postgres, Supabase, Google Sheets, or CRM via webhook payload.

The 3-Component Recurring Pipeline Architecture

Building a resilient recurring scraping engine requires three simple components:

1. Scheduler (Cron / Cloud Function / GitHub Action)

Fires a POST request to https://boltscraper.com/api/v1/maps/jobs on your desired schedule (e.g., every Monday morning). Contains your target keywords, website scraping preferences, and your webhook callback URL.

2. Bolt Scraper Cloud Engine

Processes your search queries in parallel across distributed cloud workers. Crawls business websites for direct emails and social links, and automatically deduplicates listings via place_id.

3. Webhook Receiver Endpoint

A lightweight server endpoint (Node.js, Python, or no-code webhook) that receives the signed gmap_job.completed payload and automatically writes new records into your database or CRM.

Step 1: Setting Up the Scheduled Job Trigger

You can trigger your recurring scrape using a standard crontab, a serverless cloud function, or a lightweight scheduled script. Here is a production-ready Python script that launches a recurring multi-location scrape:

schedule_gmap_scrape.py
import os
import requests

# Bolt Scraper API Endpoint & Credentials
API_URL = "https://boltscraper.com/api/v1/maps/jobs"
API_KEY = os.getenv("BOLT_API_KEY", "bs_YOUR_API_KEY")

# Webhook receiver that will handle the completed job notification
WEBHOOK_CALLBACK = "https://api.yourdomain.com/webhooks/bolt-scraper"

def trigger_weekly_scrape():
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "keywords": [
            "HVAC contractors in Charlotte, NC",
            "HVAC contractors in Raleigh, NC",
            "HVAC contractors in Greensboro, NC"
        ],
        "scrape_websites": True,      # Crawls websites for contact emails and social media
        "ultra_mode": True,            # Optional: up to 3x more leads (Standard mode already bypasses the 120-lead cap)
        "webhook_url": WEBHOOK_CALLBACK
    }
    
    response = requests.post(API_URL, headers=headers, json=payload)
    data = response.json()
    
    if response.status_code == 200 and data.get("success"):
        print(f"Scrape initiated! Job ID: {data['job_id']}")
        print(f"Keywords queued: {data['keywords_count']}")
    else:
        print(f"Failed to start job: {data}")

if __name__ == "__main__":
    trigger_weekly_scrape()

To run this script automatically every Monday at 7:00 AM, add a single entry to your server's crontab:

crontab
# Run weekly Google Maps discovery every Monday at 07:00 UTC
0 7 * * 1 /usr/bin/python3 /opt/leads/schedule_gmap_scrape.py >> /var/log/lead_scrape.log 2>&1

Step 2: Building the Webhook Callback Receiver

Once Bolt Scraper finishes extracting businesses, scraping company websites, and deduplicating records in the cloud, it dispatches an HTTP POST event to your webhook_url.

Webhook Headers Sent by Bolt Scraper
  • X-Bolt-Event: gmap_job.completed: Event identifier.
  • X-Bolt-Signature: sha256=...: HMAC SHA-256 signature calculated over the raw request body using your webhook secret.
  • Content-Type: application/json: UTF-8 JSON payload.

Here is an Express (Node.js) server listening for webhook events and verifying the signature:

webhook_server.js
const express = require('express');
const crypto = require('crypto');
const axios = require('axios');

const app = express();
const WEBHOOK_SECRET = process.env.BOLT_WEBHOOK_SECRET || 'your_whsec_secret';

// Preserve raw body buffer for HMAC signature verification
app.use(express.json({
    verify: (req, res, buf) => { req.rawBody = buf; }
}));

app.post('/webhooks/bolt-scraper', async (req, res) => {
    const signature = req.headers['x-bolt-signature'];
    const eventType = req.headers['x-bolt-event'];

    // 1. Verify HMAC SHA-256 Signature (Optional but recommended)
    if (signature && WEBHOOK_SECRET) {
        const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
        const digest = 'sha256=' + hmac.update(req.rawBody).digest('hex');
        if (signature !== digest) {
            return res.status(401).send('Invalid webhook signature');
        }
    }

    // 2. Handle gmap_job.completed event
    if (eventType === 'gmap_job.completed') {
        const { job_id, total_leads, download_csv_url, leads } = req.body;
        
        console.log(`Job ${job_id} finished with ${total_leads} unique business leads.`);
        
        // Process inline leads or download the complete CSV export
        if (download_csv_url) {
            console.log(`Downloading full CSV from: ${download_csv_url}`);
            // Ingest CSV directly into your master database or CRM
            await syncLeadsToDatabase(download_csv_url);
        }
    }

    // Acknowledge receipt promptly with HTTP 200
    res.status(200).json({ received: true });
});

async function syncLeadsToDatabase(csvUrl) {
    // Fetch CSV stream and upsert records into Postgres / Airtable
    const response = await axios.get(csvUrl, { responseType: 'stream' });
    // Records are already deduplicated in cloud by Bolt Scraper!
}

app.listen(3000, () => console.log('Webhook receiver listening on port 3000'));

Zero-Effort Deduplication: How Place ID Protects Your Database

The biggest fear with scheduled recurring scraping is bloating your database with redundant records. If you search for contractors every week, won't you receive the same companies over and over?

Bolt Scraper Automatically Deduplicates in the Cloud

You don't have to write custom deduplication algorithms or clean overlapping CSV files. Every business listing returned includes Google's permanent, globally unique place_id. Bolt Scraper automatically deduplicates results across overlapping query boundaries in the cloud so you only receive clean, unique records.

When syncing leads into your own database (PostgreSQL, MySQL, Supabase, or MongoDB), use the place_id as your unique index for idempotent upserts:

schema.sql
-- Create leads table with unique place_id constraint
CREATE TABLE IF NOT EXISTS gmap_leads (
    place_id VARCHAR(255) PRIMARY KEY,
    business_name VARCHAR(255) NOT NULL,
    phone VARCHAR(50),
    website TEXT,
    email VARCHAR(255),
    address TEXT,
    rating NUMERIC(2, 1),
    reviews_count INTEGER,
    last_scraped_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Safe upsert: Insert fresh leads or update review ratings on existing leads
INSERT INTO gmap_leads (place_id, business_name, phone, website, email, rating, reviews_count)
VALUES ('ChIJN1t_tDeuEmsRUsoyG83frY4', 'Alpha HVAC Services', '+1-704-555-0199', 'https://alphahvac.com', '[email protected]', 4.8, 142)
ON CONFLICT (place_id) 
DO UPDATE SET 
    rating = EXCLUDED.rating,
    reviews_count = EXCLUDED.reviews_count,
    last_scraped_at = CURRENT_TIMESTAMP;

Connecting Webhooks to Google Sheets & No-Code CRMs

If your team uses Google Sheets or Airtable instead of a raw SQL database, you can connect your webhook receiver directly via Make.com, n8n, or Zapier:

1. Webhook Trigger

Create a Custom Webhook module in Make/n8n and paste the URL into your Bolt Scraper webhook_url field.

2. Parse CSV / Leads

Use the CSV Parse module to unpack incoming lead records or iterate over the leads array payload.

3. Append & Notify

Add new rows to Google Sheets and send a Slack notification: "180 new HVAC leads added this morning!"

To dive into the full parameter list, JSON responses, and HTTP response codes, explore our flagship Google Maps Scraper API guide and the official Bolt Scraper API documentation. If you prefer configuring visual drag-and-drop webhooks without running cron servers, see our Google Maps scraper n8n / Make integration guide. For sweeping hundreds of regions simultaneously, review our blueprint for multi-city lead generation at scale.

Google Maps API Series

Part of the Google Maps Scraping Playbook

Explore the other core guides in this topic cluster to build robust, automated prospecting pipelines:

Set Up Your Automated Lead Scrapes Today

Never run manual searches again. Connect the Bolt Scraper API to your scheduling workflows, bypass the 120-lead limit natively, and receive fresh leads on autopilot.

No Credit Card Required 1,000 Free Cloud Leads/Mo Auto-Deduplication by Place ID

Instant cloud setup • Webhooks & REST API • Test live search API for free & view docs

Frequently Asked Questions

Bolt Scraper retries failed webhook deliveries with exponential backoff. Additionally, scrape results are persisted safely in the cloud, so you can always poll GET /api/v1/maps/jobs/{job_id} or download the CSV at any time.

Signed CSV download URLs delivered in the webhook payload remain active for 24 hours. You can download the complete dataset immediately upon receiving the event or request a refreshed download link via the API.

No. Bolt Scraper automatically deduplicates business listings across multiple target keywords and locations in the cloud using Google's unique place_id. When storing records in your own CRM, indexing by place_id allows seamless, zero-duplicate upserts.

You can pass arrays containing dozens or hundreds of search queries in a single POST request. Bolt Scraper distributes them across parallel cloud execution threads and sends a single consolidated webhook callback when all queries complete.