{
  "id": "AbjcSU4CyJzqjsbz",
  "meta": null,
  "name": "Google Maps Lead Enrichment Workflow",
  "nodes": [
    {
      "id": "3c390cab-3648-4bca-bd94-80ecb2851703",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        0
      ],
      "parameters": {
        "width": 480,
        "height": 768,
        "content": "## Google Maps Lead Enrichment Workflow\n\n### How it works\n\nThis workflow manually launches a Google Maps lead scraping run in Apify, waits for the actor to finish, and then retrieves the resulting business records. It loops through each business, checks whether a website is available, optionally fetches the site HTML to extract an email address, and appends the enriched lead data to Google Sheets.\n\n### Setup steps\n\n- Add or verify the Apify API key used by the configuration node.\n- Set the search query, maximum result count, and target Google Sheets spreadsheet ID in the CONFIGURE HERE node.\n- Configure Google Sheets credentials for the Append to Google Sheets node and ensure the destination sheet has the expected columns.\n- Confirm the Apify actor endpoint and dataset/run ID expressions match the scraper actor being used.\n\n### Customization\n\nAdjust the search query, maxResults value, polling wait times, email extraction code, and Google Sheets column mapping to fit different lead sources or output formats."
      },
      "typeVersion": 1
    },
    {
      "id": "743c8413-e8a2-42a0-af94-78941c0f5494",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Manual setup input\n\nStarts the workflow manually and defines the key runtime settings such as Apify API key, search query, result limit, and spreadsheet ID."
      },
      "typeVersion": 1
    },
    {
      "id": "9d367535-d048-4a80-83a6-96cb4f17399d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        128
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 336,
        "content": "## Start scraper run\n\nSends the initial request to Apify to launch the Google Maps scraping actor, then stores the returned run and dataset identifiers for later API calls."
      },
      "typeVersion": 1
    },
    {
      "id": "cefa8ab1-13d3-4016-8f1a-f74198fff8cf",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 496,
        "content": "## Poll Apify completion\n\nWaits before checking the Apify actor run status, branches based on whether the run is finished, and loops through a retry wait until completion."
      },
      "typeVersion": 1
    },
    {
      "id": "a94fc1a3-9483-401a-b3f7-7ae712511fbf",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 304,
        "content": "## Retrieve business results\n\nDownloads completed dataset items from Apify, loops through each business record, and routes records based on whether they include a website URL."
      },
      "typeVersion": 1
    },
    {
      "id": "9d0c1ba1-1f93-4367-ac65-90228d64adda",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 672,
        "height": 592,
        "content": "## Enrich and save leads\n\nHandles both website and no-website branches: it fetches website HTML and extracts an email when possible, preserves basic data when no website exists, then appends the final lead record to Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "trigger",
      "name": "When Triggered Manually",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        50,
        300
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "config",
      "name": "Set API and Query Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        280,
        300
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "apifyApiKey",
              "type": "string",
              "value": "YOUR_APIFY_API_KEY_HERE"
            },
            {
              "id": "a2",
              "name": "query",
              "type": "string",
              "value": "plumbers in Madrid, Spain"
            },
            {
              "id": "a3",
              "name": "maxResults",
              "type": "number",
              "value": 20
            },
            {
              "id": "a4",
              "name": "spreadsheetId",
              "type": "string",
              "value": "YOUR_GOOGLE_SPREADSHEET_ID"
            }
          ]
        }
      },
      "typeVersion": 3
    },
    {
      "id": "apifystart",
      "name": "Initiate Apify Maps Scraper",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        520,
        300
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/compass~crawler-google-places/runs",
        "method": "POST",
        "jsonBody": "={{ JSON.stringify({ searchString: $('Set API and Query Parameters').first().json.query, maxCrawledPlaces: $('Set API and Query Parameters').first().json.maxResults, proxyConfig: { useApifyProxy: true } }) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set API and Query Parameters').first().json.apifyApiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "storerunid",
      "name": "Save Run and Dataset IDs",
      "type": "n8n-nodes-base.set",
      "position": [
        760,
        300
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "r1",
              "name": "runId",
              "type": "string",
              "value": "={{ $json.data.id }}"
            },
            {
              "id": "r2",
              "name": "datasetId",
              "type": "string",
              "value": "={{ $json.data.defaultDatasetId }}"
            }
          ]
        }
      },
      "typeVersion": 3
    },
    {
      "id": "waitbefore",
      "name": "Wait 15 Seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        1000,
        300
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 15
      },
      "typeVersion": 1.1
    },
    {
      "id": "pollstatus",
      "name": "Check Apify Run Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1240,
        300
      ],
      "parameters": {
        "url": "={{ 'https://api.apify.com/v2/actor-runs/' + $('Save Run and Dataset IDs').first().json.runId }}",
        "method": "GET",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set API and Query Parameters').first().json.apifyApiKey }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "checkdone",
      "name": "If Apify Run Complete",
      "type": "n8n-nodes-base.if",
      "position": [
        1480,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.data.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "waitretry",
      "name": "Wait and Retry Polling",
      "type": "n8n-nodes-base.wait",
      "position": [
        1480,
        500
      ],
      "parameters": {
        "unit": "seconds",
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "getresults",
      "name": "Fetch Dataset Results",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1720,
        300
      ],
      "parameters": {
        "url": "={{ 'https://api.apify.com/v2/datasets/' + $('Save Run and Dataset IDs').first().json.datasetId + '/items?limit=100' }}",
        "method": "GET",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Set API and Query Parameters').first().json.apifyApiKey }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "splitresults",
      "name": "Loop Over Business Results",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1960,
        300
      ],
      "parameters": {
        "options": {},
        "batchSize": 1
      },
      "typeVersion": 3
    },
    {
      "id": "haswebsite",
      "name": "If Business Has Website",
      "type": "n8n-nodes-base.if",
      "position": [
        2200,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "w1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.website != null && $json.website !== '' ? 'yes' : 'no' }}",
              "rightValue": "yes"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "fetchhtml",
      "name": "Retrieve Website HTML",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        2440,
        160
      ],
      "parameters": {
        "url": "={{ $json.website }}",
        "method": "GET",
        "options": {
          "timeout": 8000,
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "extractemail",
      "name": "Extract Email from HTML",
      "type": "n8n-nodes-base.code",
      "position": [
        2680,
        160
      ],
      "parameters": {
        "jsCode": "const biz = $('Loop Over Business Results').first().json;\nconst inputJson = $input.first().json;\nconst html = (inputJson && !inputJson.error)\n  ? (inputJson.body || inputJson.data || '')\n  : '';\n\nconst emailRegex = /[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}/g;\nconst raw = typeof html === 'string' ? (html.match(emailRegex) || []) : [];\n\n// Filter noise: system addresses, placeholders, tracking pixels\nconst noisePattern = /^(noreply|no-reply|donotreply|do-not-reply|mailer-daemon|postmaster|privacy|legal|abuse|spam|unsubscribe|bounce|errors@|nombre@|name@|email@|user@|yourname@)/i;\nconst fakeDomains = /@(example|ejemplo|test|domain|yourdomain|sentry)\\.(com|org|net|es)$/i;\n\nconst filtered = raw.filter(e => !noisePattern.test(e) && !fakeDomains.test(e));\nconst unique = [...new Map(filtered.map(e => [e.toLowerCase(), e])).values()];\n\nreturn [{\n  json: {\n    title:       biz.title        || '',\n    address:     biz.address      || '',\n    phone:       biz.phone        || '',\n    website:     biz.website      || '',\n    email:       unique.length > 0 ? unique[0] : '',\n    rating:      String(biz.totalScore    || ''),\n    reviewCount: String(biz.reviewsCount  || '0'),\n    emailFound:  unique.length > 0\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "nowebsite",
      "name": "Handle Missing Website",
      "type": "n8n-nodes-base.set",
      "position": [
        2440,
        440
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "n1",
              "name": "title",
              "type": "string",
              "value": "={{ $json.title       || '' }}"
            },
            {
              "id": "n2",
              "name": "address",
              "type": "string",
              "value": "={{ $json.address     || '' }}"
            },
            {
              "id": "n3",
              "name": "phone",
              "type": "string",
              "value": "={{ $json.phone       || '' }}"
            },
            {
              "id": "n4",
              "name": "website",
              "type": "string",
              "value": ""
            },
            {
              "id": "n5",
              "name": "email",
              "type": "string",
              "value": ""
            },
            {
              "id": "n6",
              "name": "rating",
              "type": "string",
              "value": "={{ String($json.totalScore   || '') }}"
            },
            {
              "id": "n7",
              "name": "reviewCount",
              "type": "string",
              "value": "={{ String($json.reviewsCount || '0') }}"
            },
            {
              "id": "n8",
              "name": "emailFound",
              "type": "boolean",
              "value": false
            }
          ]
        }
      },
      "typeVersion": 3
    },
    {
      "id": "appendsheets",
      "name": "Add Data to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2920,
        300
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $json.title }}",
            "Email": "={{ $json.email }}",
            "Phone": "={{ $json.phone }}",
            "Rating": "={{ $json.rating }}",
            "Address": "={{ $json.address }}",
            "Reviews": "={{ $json.reviewCount }}",
            "Website": "={{ $json.website }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Address",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Website",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rating",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reviews",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Reviews",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set API and Query Parameters').first().json.spreadsheetId }}"
        }
      },
      "typeVersion": 4
    }
  ],
  "active": false,
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "createdAt": "2026-06-22T12:03:34.568Z",
  "updatedAt": "2026-06-22T12:03:34.568Z",
  "versionId": "699b51d5-b8f0-4192-a56b-aa94b3f05301",
  "isArchived": false,
  "staticData": null,
  "connections": {
    "Wait 15 Seconds": {
      "main": [
        [
          {
            "node": "Check Apify Run Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Dataset Results": {
      "main": [
        [
          {
            "node": "Loop Over Business Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Apify Run Complete": {
      "main": [
        [
          {
            "node": "Fetch Dataset Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait and Retry Polling",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Website HTML": {
      "main": [
        [
          {
            "node": "Extract Email from HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Apify Run Status": {
      "main": [
        [
          {
            "node": "If Apify Run Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Missing Website": {
      "main": [
        [
          {
            "node": "Add Data to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait and Retry Polling": {
      "main": [
        [
          {
            "node": "Check Apify Run Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Email from HTML": {
      "main": [
        [
          {
            "node": "Add Data to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Business Has Website": {
      "main": [
        [
          {
            "node": "Retrieve Website HTML",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Handle Missing Website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Triggered Manually": {
      "main": [
        [
          {
            "node": "Set API and Query Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Run and Dataset IDs": {
      "main": [
        [
          {
            "node": "Wait 15 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Business Results": {
      "main": [
        [
          {
            "node": "If Business Has Website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initiate Apify Maps Scraper": {
      "main": [
        [
          {
            "node": "Save Run and Dataset IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set API and Query Parameters": {
      "main": [
        [
          {
            "node": "Initiate Apify Maps Scraper",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "description": null,
  "triggerCount": 0,
  "versionCounter": 1,
  "activeVersionId": null
}