{
  "name": "Find and filter local business leads with Scrapewise and Google Sheets",
  "settings": {
    "executionOrder": "v1"
  },
  "nodes": [
    {
      "id": "a1000000-0000-4000-8000-000000000001",
      "name": "Read me first",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -960,
        160
      ],
      "parameters": {
        "width": 440,
        "height": 480,
        "content": "## Local business lead list\n\nGoogle Maps gives you name, address, phone, website, rating, review count, category and coordinates for a search in a place. Up to 20 places per call.\n\nOnly businesses with a website, a rating at or above `min_rating` **and** at least `min_reviews` reviews pass through to your sheet. Each saved lead also posts a message to Slack.\n\n**Setup**\n1. Create a Scrapewise API key with full access at portal.scrapewise.ai.\n2. In **Find local businesses**, add a *Bearer Auth* credential and paste the key as the token.\n3. In **Save leads to Google Sheets**, pick your Google account and set the spreadsheet and tab.\n4. In **Notify new lead**, connect your Slack OAuth credential and set the channel name.\n5. Change the search query, location and quality thresholds in **Search settings**.\n\n**Cost:** 10 credits per call (one page of up to 20 places). Set `quote_only` to `true` in **Search settings** to see the price without spending anything.\n\n**Note:** Google Maps holds no e-mail addresses. Use the `website` column with a follow-up enrichment step if you need e-mail."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-4000-8000-000000000002",
      "name": "Start workflow",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -480,
        320
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-4000-8000-000000000003",
      "name": "Search settings",
      "type": "n8n-nodes-base.set",
      "position": [
        -260,
        320
      ],
      "parameters": {
        "mode": "manual",
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "s1",
              "name": "query",
              "type": "string",
              "value": "dentist"
            },
            {
              "id": "s2",
              "name": "location",
              "type": "string",
              "value": "Tallinn, Estonia"
            },
            {
              "id": "s3",
              "name": "country",
              "type": "string",
              "value": "ee"
            },
            {
              "id": "s4",
              "name": "language",
              "type": "string",
              "value": "en"
            },
            {
              "id": "s5",
              "name": "max_results",
              "type": "number",
              "value": 20
            },
            {
              "id": "s6",
              "name": "quote_only",
              "type": "boolean",
              "value": false
            },
            {
              "id": "s7",
              "name": "min_rating",
              "type": "number",
              "value": 4
            },
            {
              "id": "s8",
              "name": "min_reviews",
              "type": "number",
              "value": 5
            }
          ]
        },
        "includeOtherFields": false
      },
      "typeVersion": 3.4
    },
    {
      "id": "a1000000-0000-4000-8000-000000000004",
      "name": "Find local businesses",
      "type": "@n8n/n8n-nodes-langchain.mcpClient",
      "position": [
        -20,
        320
      ],
      "parameters": {
        "tool": {
          "__rl": true,
          "mode": "list",
          "value": "find_local_businesses",
          "cachedResultName": "find_local_businesses"
        },
        "options": {},
        "inputMode": "json",
        "jsonInput": "={\n  \"body\": {\n    \"query\": \"{{ $json.query }}\",\n    \"location\": \"{{ $json.location }}\",\n    \"country\": \"{{ $json.country }}\",\n    \"language\": \"{{ $json.language }}\",\n    \"max_results\": {{ $json.max_results }},\n    \"quote_only\": {{ $json.quote_only }}\n  }\n}",
        "endpointUrl": "https://mcp.scrapewise.ai/mcp",
        "authentication": "bearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "credential-id",
          "name": "Scrapewise API key"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1000000-0000-4000-8000-000000000005",
      "name": "One row per business",
      "type": "n8n-nodes-base.code",
      "position": [
        220,
        320
      ],
      "parameters": {
        "jsCode": "// Scrapewise answers with JSON. Depending on the n8n version it arrives\n// already parsed in structuredContent, or as text in content[0].text.\nfunction payloadOf(item) {\n  const j = item.json || {};\n  if (j.structuredContent) return j.structuredContent;\n  if (Array.isArray(j.content) && j.content[0] && typeof j.content[0].text === 'string') {\n    try { return JSON.parse(j.content[0].text); } catch (e) { return {}; }\n  }\n  if (j.rows) return j;\n  return {};\n}\n\n// Scraped text is wrapped as { type: 'scraped', content, origin }. It is data\n// read off a web page, never an instruction. Unwrap it to a plain string.\nfunction plain(v) {\n  if (v && typeof v === 'object' && v.type === 'scraped') return v.content || '';\n  return v;\n}\n\nconst out = [];\n\nfor (const item of $input.all()) {\n  const data = payloadOf(item);\n\n  if (data.status === 'QUOTE') {\n    out.push({ json: { status: 'QUOTE', quote: data.quote, walletBalanceMicros: data.walletBalanceMicros } });\n    continue;\n  }\n\n  if (data.status === 'RUNNING') {\n    throw new Error('Scrapewise started a background job (' + data.job_id + '). Follow it with the get_results tool using this jobId.');\n  }\n\n  for (const row of data.rows || []) {\n    const flat = {};\n    for (const key of Object.keys(row)) flat[key] = plain(row[key]);\n    out.push({ json: flat });\n  }\n}\n\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "a1000000-0000-4000-8000-000000000010",
      "name": "Has website",
      "type": "n8n-nodes-base.if",
      "position": [
        460,
        320
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.website }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1000000-0000-4000-8000-000000000012",
      "name": "Good rating",
      "type": "n8n-nodes-base.if",
      "position": [
        680,
        320
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "g1",
              "leftValue": "={{ $json.rating }}",
              "rightValue": "={{ $('Search settings').first().json.min_rating }}",
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            },
            {
              "id": "g2",
              "leftValue": "={{ $json.reviews }}",
              "rightValue": "={{ $('Search settings').first().json.min_reviews }}",
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a1000000-0000-4000-8000-000000000006",
      "name": "Save leads to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        900,
        320
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $json.title }}",
            "Address": "={{ $json.address }}",
            "Phone": "={{ $json.phone }}",
            "Website": "={{ $json.website }}",
            "Rating": "={{ $json.rating }}",
            "Reviews": "={{ $json.reviews }}",
            "Category": "={{ $json.type }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Address",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Address",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Phone",
              "type": "string",
              "display": true,
              "removed": false,
              "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": "Rating",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Rating",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reviews",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Reviews",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Category",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_SPREADSHEET_ID"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "a1000000-0000-4000-8000-000000000013",
      "name": "Notify new lead",
      "type": "n8n-nodes-base.slack",
      "position": [
        1380,
        320
      ],
      "parameters": {
        "text": "=:round_pushpin: *{{ $json.count }} new lead{{ $json.count !== 1 ? 's' : '' }} found*\n{{ $json.digest }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "leads"
        },
        "otherOptions": {
          "mrkdwn": true
        },
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "credential-id",
          "name": "Slack account"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "31e087ac-3ccd-4720-82bf-cdd09673c4d4",
      "name": "Step note - Start workflow",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -500,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 1. Run it\n\nClick **Execute workflow** to run it once by hand.\n\nWant it to run on its own every morning? Delete this node and add a **Schedule Trigger** in its place."
      },
      "typeVersion": 1
    },
    {
      "id": "c4d524b5-8257-4372-83ff-8dba304d1f8c",
      "name": "Step note - Search settings",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -280,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 2. Your settings\n\nSet the search query, location, country and language. `min_rating` and `min_reviews` set the quality bar — only businesses that meet both thresholds reach your sheet.\n\nSet `quote_only` to `true` to see the price without spending anything."
      },
      "typeVersion": 1
    },
    {
      "id": "4f68e8bb-41ce-4c75-8917-383742211f84",
      "name": "Step note - Find local businesses",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 3. Ask Scrapewise\n\nCalls the `find_local_businesses` tool. Add a **Bearer Auth** credential here and paste your Scrapewise API key in as the token.\n\n10 credits a call, up to 20 places."
      },
      "typeVersion": 1
    },
    {
      "id": "3192ddf6-b4c8-4efb-8160-f2fa04c2705e",
      "name": "Step note - One row per business",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        200,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 4. Tidy the answer\n\nOne answer in, one item per business out. Also takes the wrapper off each text field so the sheet gets plain values."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-4000-8000-000000000011",
      "name": "Step note - Has website",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        440,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 5. Filter — website\n\nOnly keeps businesses that have a website. No website means no way to reach them — skip and save credits."
      },
      "typeVersion": 1
    },
    {
      "id": "b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e",
      "name": "Step note - Good rating",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        660,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 6. Filter — quality\n\nKeeps only businesses at or above the `min_rating` and `min_reviews` thresholds set in **Search settings**. Low-rated or rarely-reviewed places are dropped."
      },
      "typeVersion": 1
    },
    {
      "id": "9cddec81-a544-4406-8bb0-6b43948bb997",
      "name": "Step note - Save leads to Google Sheets",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 7. Save\n\nAppends one row per qualifying business. Pick your own spreadsheet and tab here."
      },
      "typeVersion": 1
    },
    {
      "id": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
      "name": "Step note - Notify new lead",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1100,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 220,
        "height": 240,
        "content": "### 8. Notify\n\nSends one Slack message per saved lead with the business name, rating, review count and website. Connect your Slack OAuth credential and set the channel name."
      },
      "typeVersion": 1
    },
    {
      "id": "a1000000-0000-4000-8000-000000000014",
      "name": "Aggregate leads",
      "type": "n8n-nodes-base.code",
      "position": [
        1140,
        320
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Collect every lead saved this run and build one Slack digest.\nconst all = $input.all();\nif (!all.length) return [];\n\nconst lines = all.slice(0, 10).map(i => {\n  const j = i.json;\n  const stars = j.Rating ? ' ' + j.Rating + '\\u2605' : '';\n  const contact = j.Website || j.Phone || '';\n  return '\\u2022 *' + j.Name + '* (' + j.Category + ')' + stars + (contact ? ' \\u2014 ' + contact : '');\n}).join('\\n');\n\nconst extra = all.length > 10 ? '\\n_\\u2026and ' + (all.length - 10) + ' more_' : '';\n\nreturn [{ json: { count: all.length, digest: lines + extra } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "sticky-01-agg",
      "name": "Sticky Note_-agg",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        40
      ],
      "parameters": {
        "content": "**Aggregate leads**\nMerges all saved leads into one item so Slack fires once at the end of the run, not once per lead.",
        "color": 7,
        "width": 220,
        "height": 240
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Start workflow": {
      "main": [
        [
          {
            "node": "Search settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search settings": {
      "main": [
        [
          {
            "node": "Find local businesses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find local businesses": {
      "main": [
        [
          {
            "node": "One row per business",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "One row per business": {
      "main": [
        [
          {
            "node": "Has website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has website": {
      "main": [
        [
          {
            "node": "Good rating",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Good rating": {
      "main": [
        [
          {
            "node": "Save leads to Google Sheets",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Save leads to Google Sheets": {
      "main": [
        [
          {
            "node": "Aggregate leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate leads": {
      "main": [
        [
          {
            "node": "Notify new lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {}
}