AutomationFlowsWeb Scraping › Google Maps Business Scraper with Contact Extraction

Google Maps Business Scraper with Contact Extraction

Original n8n title: Google Maps Business Scraper with Contact Extraction via Apify and Firecrawl

ByNaveen Choudhary @n8nstein on n8n.io

Marketing agencies, sales teams, lead generation specialists, and business development professionals who need to build comprehensive business databases with contact information for outreach campaigns across any industry.

Cron / scheduled trigger★★★★☆ complexity18 nodesGoogle SheetsHTTP Request
Web Scraping Trigger: Cron / scheduled Nodes: 18 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #4573 — we link there as the canonical source.

This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "95RHN758KyIlB84T",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Google Maps business scraper with contact extraction via Apify and Firecrawl",
  "tags": [],
  "nodes": [
    {
      "id": "17ab0386-4f7f-4f72-ab28-399febe4bf84",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        -125
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 30
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "2e7080b6-a5a4-4571-933f-3a33979037e8",
      "name": "Loop Until Complete",
      "type": "n8n-nodes-base.if",
      "position": [
        1100,
        -125
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "de704919-205a-470f-a417-b297fbbdbaf8",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.data.status }}",
              "rightValue": "SUCCEEDED"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "3e062954-93b9-48d7-b5dc-29ff579793af",
      "name": "Read Pending Queries",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        220,
        -125
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "false",
              "lookupColumn": "Status"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit#gid=0",
          "cachedResultName": "Query"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit?usp=drivesdk",
          "cachedResultName": "Google Maps Scraper"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "63d7b375-c4a6-42ae-b9ef-51f963edaa72",
      "name": "Start Apify Scraping Job",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        440,
        -125
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/compass~crawler-google-places/runs",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"searchStringsArray\": [\n    \"restaurant\"\n  ],\n  \"locationQuery\": \"New York, USA\",\n  \"maxCrawledPlacesPerSearch\": 15,\n  \"language\": \"en\",\n  \"maximumLeadsEnrichmentRecords\": 0,\n  \"maxImages\": 0\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "958eb9fc-14f5-41f1-9055-10e0f889c039",
      "name": "Wait for Job Succeed",
      "type": "n8n-nodes-base.wait",
      "position": [
        660,
        -125
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "32fa03e3-a5ac-4ae3-b03a-2d99cb5d3079",
      "name": "Check Scraping Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        880,
        -200
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/actor-runs/{{ $json.data.id }}",
        "options": {
          "timeout": 10000
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "97c84abb-c21e-497c-a2eb-1bf4b01db0ca",
      "name": "Fetch Scraped Results",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1320,
        -125
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/datasets/{{ $json.data.defaultDatasetId }}/items",
        "options": {
          "timeout": 10000
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "383d8508-54ba-4fbf-9d4b-f13487d1ff25",
      "name": "Save Business Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1540,
        -125
      ],
      "parameters": {
        "columns": {
          "value": {
            "phone": "={{ $json.phone }}",
            "title": "={{ $json.title }}",
            "status": "false",
            "address": "={{ $json.address }}",
            "website": "={{ $json.website }}",
            "categoryName": "={{ $json.categoryName }}",
            "searchString": "={{ $json.searchString }}"
          },
          "schema": [
            {
              "id": "searchString",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "searchString",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "categoryName",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "categoryName",
              "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": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "searchString"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1948906848,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit#gid=1948906848",
          "cachedResultName": "Data"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit?usp=drivesdk",
          "cachedResultName": "Google Maps Scraper"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "cd083a40-2270-44e2-8683-ae3b4d5b85a8",
      "name": "Filter Businesses with Websites",
      "type": "n8n-nodes-base.filter",
      "position": [
        1760,
        -125
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2218f0be-2c48-4a1a-bd21-8bdb67c495a1",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": ""
            },
            {
              "id": "d0ef3194-ee94-45c5-b9c3-5db32f08d1b5",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "false"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "234a63b0-3741-43d2-8a8b-ac29d6137ddb",
      "name": "Batch Processing Logic",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1980,
        -125
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "63e8e91a-6012-4073-b03d-2158672288ee",
      "name": "Scrape Website Content",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2260,
        -220
      ],
      "parameters": {
        "url": "https://api.firecrawl.dev/v1/scrape",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"url\": \"{{ $json.website }}\",\n  \"formats\": [\n    \"html\"\n  ]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d9c1b4dc-c741-4dff-a9cd-0dd6527d514a",
      "name": "Extract Contact Information",
      "type": "n8n-nodes-base.code",
      "position": [
        2480,
        -220
      ],
      "parameters": {
        "jsCode": "// N8N Code Node - Extract emails, LinkedIn, Facebook, Instagram for single item\nconst item = $input.first();\n\n// Get the text content to search (adjust field names as needed)\nconst textContent = item.json.data.html;\n\n// Initialize result object\nconst result = {\n  website: $('Batch Processing Logic').first().json.website,\n  emails: \"None\",\n  linkedin: \"None\", \n  facebook: \"None\",\n  instagram: \"None\",\n  twitter: \"None\"\n};\n\n// Email extraction regex - matches common email patterns\nconst emailRegex = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b/gi;\nconst emails = textContent.match(emailRegex);\n\nif (emails && emails.length > 0) {\n  // Remove duplicates and filter out common non-email matches\n  const uniqueEmails = [...new Set(emails)].filter(email => {\n    const lowerEmail = email.toLowerCase();\n    return !lowerEmail.includes('example.com') && \n           !lowerEmail.includes('test@') &&\n           !lowerEmail.includes('noreply@') &&\n           !lowerEmail.includes('no-reply@') &&\n           lowerEmail.length > 5;\n  });\n  \n  if (uniqueEmails.length > 0) {\n    result.emails = uniqueEmails.length === 1 ? uniqueEmails[0] : uniqueEmails.join(', ');\n  }\n}\n\n// LinkedIn extraction - improved pattern\nconst linkedinRegex = /(?:https?:\\/\\/)?(?:www\\.)?linkedin\\.com\\/(?:in\\/|company\\/|pub\\/)[a-zA-Z0-9\\-._]+\\/?/gi;\nconst linkedinMatches = textContent.match(linkedinRegex);\nif (linkedinMatches && linkedinMatches.length > 0) {\n  const cleanLinkedin = linkedinMatches[0].replace(/^(?:https?:\\/\\/)?(?:www\\.)?/i, 'https://www.');\n  result.linkedin = cleanLinkedin;\n}\n\n// Facebook extraction - improved to match numeric IDs and usernames\nconst facebookRegex = /(?:https?:\\/\\/)?(?:www\\.|m\\.|mobile\\.)?facebook\\.com\\/(?:[^\\/\\s]+\\/)*[^\\/\\s?#]+/gi;\nconst facebookMatches = textContent.match(facebookRegex);\nif (facebookMatches && facebookMatches.length > 0) {\n  const cleanFacebook = facebookMatches[0].replace(/^(?:https?:\\/\\/)?(?:www\\.)?/i, 'https://www.');\n  result.facebook = cleanFacebook;\n}\n\n// Instagram extraction - improved to match various URL formats\nconst instagramRegex = /(?:https?:\\/\\/)?(?:www\\.)?instagram\\.com\\/[a-zA-Z0-9\\-._]+\\/?/gi;\nconst instagramMatches = textContent.match(instagramRegex);\nif (instagramMatches && instagramMatches.length > 0) {\n  const cleanInstagram = instagramMatches[0].replace(/^(?:https?:\\/\\/)?(?:www\\.)?/i, 'https://www.');\n  result.instagram = cleanInstagram;\n}\n\n// Twitter/X extraction - improved to match usernames properly\nconst twitterRegex = /(?:https?:\\/\\/)?(?:www\\.)?(?:twitter\\.com|x\\.com)\\/[a-zA-Z0-9_]+\\/?/gi;\nconst twitterMatches = textContent.match(twitterRegex);\nif (twitterMatches && twitterMatches.length > 0) {\n  const cleanTwitter = twitterMatches[0].replace(/^(?:https?:\\/\\/)?(?:www\\.)?/i, 'https://www.');\n  result.twitter = cleanTwitter;\n}\n\n// Alternative extraction for social handles without full URLs\n// Look for @username patterns for Instagram and Twitter\nif (result.instagram === \"None\") {\n  const igHandleRegex = /@([a-zA-Z0-9._]{1,30})/gi;\n  const igHandles = textContent.match(igHandleRegex);\n  if (igHandles && igHandles.length > 0) {\n    const username = igHandles[0].replace('@', '');\n    if (username.length > 0 && !username.includes(' ')) {\n      result.instagram = `https://www.instagram.com/${username}`;\n    }\n  }\n}\n\n// Look for Twitter handles without full URLs\nif (result.twitter === \"None\") {\n  const twitterHandleRegex = /@([a-zA-Z0-9_]{1,15})/gi;\n  const twitterHandles = textContent.match(twitterHandleRegex);\n  if (twitterHandles && twitterHandles.length > 0) {\n    const username = twitterHandles[0].replace('@', '');\n    if (username.length > 0 && !username.includes(' ')) {\n      result.twitter = `https://www.x.com/${username}`;\n    }\n  }\n}\n\n// Clean up URLs - remove trailing slashes and parameters\n['linkedin', 'facebook', 'instagram', 'twitter'].forEach(platform => {\n  if (result[platform] !== \"None\") {\n    result[platform] = result[platform].split('?')[0].split('#')[0].replace(/\\/$/, '');\n  }\n});\n\nreturn result;"
      },
      "typeVersion": 2
    },
    {
      "id": "32ef5f5d-6457-4edc-9299-afea61af4784",
      "name": "Save Contact Details",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2700,
        -220
      ],
      "parameters": {
        "columns": {
          "value": {
            "emails": "={{ $json.emails }}",
            "twitter": "={{ $json.twitter }}",
            "website": "={{ $json.website }}",
            "facebook": "={{ $json.facebook }}",
            "linkedin": "={{ $json.linkedin }}",
            "instagram": "={{ $json.instagram }}"
          },
          "schema": [
            {
              "id": "website",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "emails",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "emails",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "linkedin",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "linkedin",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "facebook",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "facebook",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "instagram",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "instagram",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "twitter",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "twitter",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 2056137853,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit#gid=2056137853",
          "cachedResultName": "Details"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit?usp=drivesdk",
          "cachedResultName": "Google Maps Scraper"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "374d7e2f-6e14-4dd1-a51a-f0cce37945d8",
      "name": "Mark as Processed",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2920,
        -160
      ],
      "parameters": {
        "columns": {
          "value": {
            "status": "true",
            "website": "={{ $json.website }}"
          },
          "schema": [
            {
              "id": "searchString",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "searchString",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "categoryName",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "categoryName",
              "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,
              "removed": false,
              "required": false,
              "displayName": "website",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "hasWebsite",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "hasWebsite",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "website"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1948906848,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit#gid=1948906848",
          "cachedResultName": "Data"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit?usp=drivesdk",
          "cachedResultName": "Google Maps Scraper"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "52c246a4-c483-4539-b22d-f4b046c16724",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        20,
        -360
      ],
      "parameters": {
        "color": 4,
        "width": 640,
        "height": 180,
        "content": "## \ud83d\ude80 INITIALIZATION PHASE\n- Triggers every 30 minutes\n- Reads unprocessed records from [Google Sheet](https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit?usp=sharing)\n- Starts Google Places scraper for restaurants\n- Waits for completion"
      },
      "typeVersion": 1
    },
    {
      "id": "2983ac2d-5627-4ab1-9fbf-c09ed8886bf6",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        940,
        -420
      ],
      "parameters": {
        "width": 660,
        "height": 180,
        "content": "## \ud83d\udcca DATA COLLECTION PHASE\n- Monitors scraper job status\n- Loops until job completes\n- Fetches scraped restaurant data\n- Saves to \"Data\" sheet in [Google Sheets](https://docs.google.com/spreadsheets/d/1DHezdcetT0c3Ie1xB3z3jDc5WElsLN87K4J9EQDef9g/edit?usp=sharing)"
      },
      "typeVersion": 1
    },
    {
      "id": "dc13180b-cf28-44a1-b6e7-c4f0c00f57c2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1940,
        -460
      ],
      "parameters": {
        "color": 6,
        "width": 740,
        "height": 200,
        "content": "## \ud83c\udf10 WEBSITE PROCESSING PHASE\n- Filters restaurants with valid websites\n- Loops through each website\n- Scrapes website content with Firecrawl\n- Extracts contact information (emails, social media)\n- Saves to \"Details\" sheet and marks as processed"
      },
      "typeVersion": 1
    },
    {
      "id": "7a23b11a-22ce-4816-a336-6cd28a90aa49",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2260,
        80
      ],
      "parameters": {
        "width": 580,
        "height": 120,
        "content": "## \ud83d\udd04 LOOPS\n**Status Check Loop**: Continues until scraper job is complete\n**Website Processing Loop**: Processes each restaurant website individually"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "b94bf772-530d-4a3c-b024-795c2a7cfe4a",
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Read Pending Queries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mark as Processed": {
      "main": [
        [
          {
            "node": "Batch Processing Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Business Data": {
      "main": [
        [
          {
            "node": "Filter Businesses with Websites",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Until Complete": {
      "main": [
        [
          {
            "node": "Wait for Job Succeed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch Scraped Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Pending Queries": {
      "main": [
        [
          {
            "node": "Start Apify Scraping Job",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Contact Details": {
      "main": [
        [
          {
            "node": "Mark as Processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Job Succeed": {
      "main": [
        [
          {
            "node": "Check Scraping Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Scraping Status": {
      "main": [
        [
          {
            "node": "Loop Until Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Scraped Results": {
      "main": [
        [
          {
            "node": "Save Business Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Batch Processing Logic": {
      "main": [
        [],
        [
          {
            "node": "Scrape Website Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Website Content": {
      "main": [
        [
          {
            "node": "Extract Contact Information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Apify Scraping Job": {
      "main": [
        [
          {
            "node": "Wait for Job Succeed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Contact Information": {
      "main": [
        [
          {
            "node": "Save Contact Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Businesses with Websites": {
      "main": [
        [
          {
            "node": "Batch Processing Logic",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Marketing agencies, sales teams, lead generation specialists, and business development professionals who need to build comprehensive business databases with contact information for outreach campaigns across any industry.

Source: https://n8n.io/workflows/4573/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

&gt; Watch the full Youtube Video Tutorial [](https://youtu.be/Y-wUr2-UYZk)

Data Table, HTTP Request, Google Sheets +1
Web Scraping

This workflow automatically scrapes business leads from Google Maps on a daily schedule and ensures only high-quality, unique leads are processed. New businesses are cleaned, validated, and deduplicat

Google Sheets, Gmail, HTTP Request
Web Scraping

Women creators, homemakers-turned-entrepreneurs, and feminine lifestyle brands who want a graceful, low-lift way to keep an eye on competitor content and spark weekly ideas.

HTTP Request, Google Sheets, Email Send +1
Web Scraping

This workflow automatically searches YouTube Data API for videos related to specific keywords, extracts channel data, filters channels based on performance metrics, and saves the results into Google S

Google Sheets, @Apify/N8N Nodes Apify, HTTP Request
Web Scraping

This n8n workflow automates Amazon price tracking and competitor monitoring by scraping product pricing via Apify and updating your Google Sheet every day.

Google Sheets, @Apify/N8N Nodes Apify, HTTP Request