{
  "id": "tKiKXqkcBMSEA3Ro",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Google Maps Email Scraper",
  "tags": [
    {
      "id": "Ye83S67h8JUhrB7p",
      "name": "\ud83d\udee0\ufe0f In progress",
      "createdAt": "2026-06-16T12:55:35.976Z",
      "updatedAt": "2026-06-16T12:55:35.976Z"
    },
    {
      "id": "rB8MSKGhRY8lPoVX",
      "name": "Secops",
      "createdAt": "2026-06-16T12:55:36.001Z",
      "updatedAt": "2026-06-16T12:55:36.001Z"
    }
  ],
  "nodes": [
    {
      "id": "695ef09a-5779-426e-b748-fb222932a319",
      "name": "Remove Duplicate URLs",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        -4000,
        -624
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "2305475c-9277-43a0-b2ed-2d793095077d",
      "name": "Loop over queries",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        -4624,
        -1312
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "98baa418-d482-466e-a5a3-16ed2bb39500",
      "name": "Search Google Maps with query",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -4592,
        -624
      ],
      "parameters": {
        "url": "=https://www.google.com/maps/search/{{ $json.query }}",
        "options": {
          "allowUnauthorizedCerts": false
        }
      },
      "executeOnce": false,
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "1ce6b969-519a-43e2-9831-246059076cfe",
      "name": "Scrape URLs from results",
      "type": "n8n-nodes-base.code",
      "position": [
        -4400,
        -624
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json.data\n\nconst regex = /https?:\\/\\/[^\\/]+/g\n\nconst urls = data.match(regex)\n\nreturn urls.map(url => ({json: {url: url}}))"
      },
      "typeVersion": 2
    },
    {
      "id": "141294c1-1e21-4cd9-bb0b-fe1bcad6bf84",
      "name": "Filter irrelevant URLs",
      "type": "n8n-nodes-base.filter",
      "position": [
        -4192,
        -624
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "041797f2-2fe2-41dc-902a-d34050b9b304",
              "operator": {
                "type": "string",
                "operation": "notRegex"
              },
              "leftValue": "={{ $json.url }}",
              "rightValue": "=(google|gstatic|ggpht|schema\\.org|example\\.com|sentry-next\\.wixpress\\.com|imli\\.com|sentry\\.wixpress\\.com|ingest\\.sentry\\.io)"
            },
            {
              "id": "eb499a7e-17bc-453c-be08-a47286f726dd",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "734547ec-7fc0-4c83-841c-912e391f3466",
      "name": "Request web page for URL",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        -3472,
        -624
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {}
      },
      "typeVersion": 4.2,
      "alwaysOutputData": false
    },
    {
      "id": "5efd93b7-4309-4981-bbdc-7bd046e1a04c",
      "name": "Loop over URLs",
      "type": "n8n-nodes-base.splitInBatches",
      "onError": "continueErrorOutput",
      "position": [
        -3760,
        -640
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "0a8be2a4-afad-4efc-9fa1-06a137ddf4e1",
      "name": "Loop over pages",
      "type": "n8n-nodes-base.splitInBatches",
      "onError": "continueErrorOutput",
      "position": [
        -3232,
        -800
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3,
      "alwaysOutputData": false
    },
    {
      "id": "dad8f0ba-58c3-4ae7-b3fb-3c9da1f1381c",
      "name": "Scrape emails from page",
      "type": "n8n-nodes-base.code",
      "onError": "continueRegularOutput",
      "position": [
        -3072,
        -704
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const data = $json.data\n\nconst emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.(?!png|jpg|gif|jpeg)[a-zA-Z]{2,}/g\n\nconst emails = data.match(emailRegex)\n\nreturn {json: {emails: emails}}"
      },
      "typeVersion": 2
    },
    {
      "id": "ada720da-53f7-466c-97b6-26df8ce8cf1b",
      "name": "Aggregate arrays of emails",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        -2832,
        -816
      ],
      "parameters": {
        "options": {
          "mergeLists": true
        },
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "emails"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "41e1d8c6-1b97-443a-9a1f-b693c8cc45de",
      "name": "Split out into default data structure",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -2624,
        -816
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "emails"
      },
      "typeVersion": 1
    },
    {
      "id": "4889d7ff-ad7c-491b-b34f-38b216dde6dc",
      "name": "Remove duplicate emails",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        -2400,
        -816
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": "emails"
      },
      "typeVersion": 1.1
    },
    {
      "id": "cd15040b-f299-497f-bfa1-3a8cf9e35961",
      "name": "Filter irrelevant emails",
      "type": "n8n-nodes-base.filter",
      "position": [
        -2192,
        -816
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "041797f2-2fe2-41dc-902a-d34050b9b304",
              "operator": {
                "type": "string",
                "operation": "notRegex"
              },
              "leftValue": "={{ $json.emails }}",
              "rightValue": "=(google|gstatic|ggpht|schema\\.org|example\\.com|sentry\\.wixpress\\.com|sentry-next\\.wixpress\\.com|ingest\\.sentry\\.io|sentry\\.io|imli\\.com)"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "83f04b23-285e-4471-becd-e85e744f9817",
      "name": "Save emails to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2000,
        -816
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "f8c6cd88-73cf-453e-8911-875641de292f",
      "name": "Starts scraper workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -4816,
        -624
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "c2316236-6162-4dca-bec7-5b3533ec5f24",
      "name": "Run workflow",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -4864,
        -1312
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "e3f89abb-8206-4a0a-b60e-7aa3dad854b6",
      "name": "Wait between executions",
      "type": "n8n-nodes-base.wait",
      "position": [
        -4160,
        -1296
      ],
      "parameters": {
        "amount": 2
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1268f15-ccbb-4061-a1c2-76664290c65e",
      "name": "Execute scraper for query",
      "type": "n8n-nodes-base.executeWorkflow",
      "position": [
        -4368,
        -1296
      ],
      "parameters": {
        "mode": "each",
        "options": {
          "waitForSubWorkflow": false
        },
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "0aecd861-9916-4f9b-b6ec-97eba36bfdc5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4896,
        -1552
      ],
      "parameters": {
        "color": 7,
        "width": 660,
        "height": 564,
        "content": "## \ud83d\udee0 Setup\n1. Setup your list of queries in the \"Run workflow\" manual trigger node. Watch  this on how to generate the queries with ChatGPT.\n3. Choose a sheet to populate with data in the **Google Sheets node**\n4. Run the workflow and start getting leads into your Google Sheets document"
      },
      "typeVersion": 1
    },
    {
      "id": "038463ac-4695-4781-8654-9a1fec3def47",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4208,
        -1456
      ],
      "parameters": {
        "color": 7,
        "width": 224,
        "height": 356,
        "content": "**Wait** \ud83d\udc47\nSet wait time between each query workflow execution. Default is 2 seconds."
      },
      "typeVersion": 1
    },
    {
      "id": "85ea0ce9-a063-4c52-86e7-ddb76ffbf6dd",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4928,
        -944
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 516,
        "content": "## Scraper entry\n\nThis sub-workflow starts when another workflow passes in one search query.\n\nInput expected:\n- `query`\n\nThe flow then:\n1. Searches Google Maps using the query\n2. Extracts possible website URLs\n3. Visits each candidate site\n4. Scrapes email addresses\n5. Cleans and saves results to Google Sheets"
      },
      "typeVersion": 1
    },
    {
      "id": "0edc10c8-bdb3-4a67-9f33-1dfab359e708",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2048,
        -1120
      ],
      "parameters": {
        "color": 7,
        "height": 500,
        "content": "## Output\n\nThis final section appends clean email rows to Google Sheets.\n\nBefore running:\n1. Connect Google Sheets credentials\n2. Select the correct spreadsheet\n3. Select the target sheet tab\n\nEach output row should represent one email record."
      },
      "typeVersion": 1
    },
    {
      "id": "b574616b-a6e9-45be-baa4-8011b03b9d43",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -5520,
        -1536
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 140,
        "content": "## Google Maps Automatic Email Scraper\n\nThis workflow automatically scrapes emails from businesses on Google Maps based on a list of queries that you provide."
      },
      "typeVersion": 1
    },
    {
      "id": "3222926c-3c66-45f7-83a4-1a3f3b7da489",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4432,
        -928
      ],
      "parameters": {
        "color": 7,
        "width": 576,
        "height": 500,
        "content": "## URL extraction\n\nThis section pulls URLs from the Google Maps search response.\n\nSteps:\n- Read raw HTML/text response\n- Extract all URLs using regex\n- Filter out junk/system domains\n- Remove duplicate URLs before visiting sites\n\nNote:\nThis depends on Google Maps page HTML, so it may stop working if Google changes the page structure."
      },
      "typeVersion": 1
    },
    {
      "id": "27630da0-7a10-4810-a279-fe2dc62c949d",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3840,
        -1024
      ],
      "parameters": {
        "color": 7,
        "width": 936,
        "height": 596,
        "content": "## URL loop\n\nThis loop processes one candidate URL at a time.\n\nFor each URL:\n1. Request the page\n2. Send the page content into the page/email processing flow\n3. Continue until no URLs are left\n\nRecommended:\nUse batch size = 1 for better control and easier debugging."
      },
      "typeVersion": 1
    },
    {
      "id": "850fbb7e-ba82-4367-b587-7eadcc33d83b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2880,
        -1072
      ],
      "parameters": {
        "color": 7,
        "width": 816,
        "height": 452,
        "content": "## Email extraction\n\nThis section extracts email addresses from each fetched web page.\n\nSteps:\n- Read page HTML/text\n- Match emails using regex\n- Aggregate all emails from all processed pages\n- Split them back into one-item-per-email format\n- Remove duplicate and junk emails"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "e1a2ca8d-ceb4-4396-a58a-8d350ab6af2e",
  "nodeGroups": [],
  "connections": {
    "Run workflow": {
      "main": [
        [
          {
            "node": "Loop over queries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop over URLs": {
      "main": [
        [
          {
            "node": "Loop over pages",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Request web page for URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop over pages": {
      "main": [
        [
          {
            "node": "Aggregate arrays of emails",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Scrape emails from page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop over queries": {
      "main": [
        [],
        [
          {
            "node": "Execute scraper for query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicate URLs": {
      "main": [
        [
          {
            "node": "Loop over URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter irrelevant URLs": {
      "main": [
        [
          {
            "node": "Remove Duplicate URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove duplicate emails": {
      "main": [
        [
          {
            "node": "Filter irrelevant emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape emails from page": {
      "main": [
        [
          {
            "node": "Loop over pages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Starts scraper workflow": {
      "main": [
        [
          {
            "node": "Search Google Maps with query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait between executions": {
      "main": [
        [
          {
            "node": "Loop over queries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter irrelevant emails": {
      "main": [
        [
          {
            "node": "Save emails to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Request web page for URL": {
      "main": [
        [
          {
            "node": "Loop over URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape URLs from results": {
      "main": [
        [
          {
            "node": "Filter irrelevant URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute scraper for query": {
      "main": [
        [
          {
            "node": "Wait between executions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate arrays of emails": {
      "main": [
        [
          {
            "node": "Split out into default data structure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Google Maps with query": {
      "main": [
        [
          {
            "node": "Scrape URLs from results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split out into default data structure": {
      "main": [
        [
          {
            "node": "Remove duplicate emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}