n8n Workflow
Intermediate

AI-Powered List Builder for Google Sheets

Automate list building with this n8n workflow. Use AI to scrape websites for specific information and automatically populate a Google Sheet, perfect for lead generation and market research.

Overview

Automatically builds targeted lists by scraping websites with AI and saving the structured data directly into a Google Sheet.

🧠 Description & Use Case

This workflow automates the tedious process of building targeted lists by using AI to intelligently search the web, scrape relevant information, and save it directly to a Google Sheet. Instead of manually searching Google, clicking through links, and copy-pasting data, this workflow does it all for you.

🔄 How It Works:

  1. Trigger & Define Search: The workflow starts manually. The Parameters node is where you define what you're looking for. By default, it's set to find “Top 'Build in Public' influencers” on “X”, but you can change this to anything, like “Head of Marketing” in “London”.

  2. Find Source Pages: The first Airtop node takes your parameters and performs a Google search to find web pages (like articles or lists) that contain the information you want. It intelligently identifies up to 10 relevant, non-sponsored URLs.

  3. Process Links: The Format results node takes the list of URLs found in the previous step and prepares them to be processed one by one.

  4. Extract Data from Each Page: The workflow then loops through each URL. The second Airtop node visits each page and uses its AI extraction capabilities to pull out the specific details you need—in this case, each person's name, handle/ID, and a direct URL.

  5. Clean & Deduplicate: All the extracted data from the various sources is collected by the Dedupe results node. This node cleans up the data (e.g., removes tracking parameters from URLs) and, most importantly, removes any duplicate entries to ensure your final list is clean.

  6. Save to Google Sheets: The final, unique list of contacts is appended to a Google Sheet. The sheet is populated with the person's name, ID/handle, URL, the original search query, and the date they were added.

✅ Real-World Use Cases:

  • Lead Generation: Build a list of potential leads for your sales team (e.g., “VPs of Sales at SaaS companies in Germany”).
  • Market Research: Compile a list of top competitors, influencers, or products in a specific niche.
  • Recruitment: Find potential candidates by searching for profiles on specific platforms (e.g., “senior python developers on GitHub”).
  • Content Creation: Gather data for an article or report, such as a list of “top 10 AI tools for marketers.”
  • Event & Outreach Lists: Create targeted lists for event invitations, PR outreach, or partnership opportunities.

Instructions

Basic steps

  1. Download workflow JSON
  2. Import into n8n
  3. Configure node parameters as needed
  4. Test and enable the workflow

💡 Tips

After importing, verify all connectors and credentials match your environment.

Preview

Workflow Visualization

7 nodes
100%
When clicking ‘Test workflow’manualTriggerFormat resultscodeGet urlsairtopGet peopleairtopDedupe resultscodeAdd to spreadsheetgoogleSheetsParametersset

Nodes

1

When clicking ‘Test workflow’

manualTrigger

v1
2

Format results

code

v2
3

Get urls

airtop

v1
4

Get people

airtop

v1
5

Dedupe results

code

v2
6

Add to spreadsheet

googleSheets

v4.5
7

Parameters

set

v3.4

Statistics

Total nodes:7
Disabled nodes:0
Node types:5
Connections:6
View full JSON structure
{
  "nodes": [
    {
      "id": "1a6aa574-467d-40b0-a9a5-a5537bede3de",
      "name": "When clicking ‘Test workflow’",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "62db9366-7e6f-4346-9de8-9fa730d059ed",
      "name": "Format results",
      "type": "n8n-nodes-base.code",
      "position": [
        660,
        0
      ],
      "parameters": {
        "jsCode": "// Get first input item\nconst input = $input.first().json.data.modelResponse\n// Parse list of links\nconst listOfLinks = JSON.parse(input).results\n// Format node's output\nconst output = listOfLinks.map((item) => ({\n  json: { url: item.url }\n}))\n\nreturn output;"
      },
      "typeVersion": 2
    },
    {
      "id": "fa960de3-8dd6-40a3-aa59-634ad250f5d1",
      "name": "Get urls",
      "type": "n8n-nodes-base.airtop",
      "position": [
        440,
        0
      ],
      "parameters": {
        "url": "=https://www.google.com/search?q={{ encodeURI($json.who+' on ' + $json.where) }}",
        "prompt": "=Those are search results, return up to 10 non-sponsored results that lead to a web page with a list of {{$json.who}} on {{$json.where}}. For each return the title and URL.",
        "resource": "extraction",
        "operation": "query",
        "sessionMode": "new",
        "additionalFields": {
          "outputSchema": "{  \"type\": \"object\",  \"properties\": {    \"results\": {      \"type\": \"array\",      \"items\": {        \"type\": \"object\",        \"properties\": {          \"title\": {            \"type\": \"string\",            \"description\": \"The title of the search result.\"          },          \"url\": {            \"type\": \"string\",            \"description\": \"The URL of the webpage.\"          }        },        \"required\": [          \"title\",          \"url\"        ],        \"additionalProperties\": false      },      \"description\": \"A list of up to 10 non-sponsored search results.\"    }  },  \"required\": [    \"results\"  ],  \"additionalProperties\": false,  \"$schema\": \"http://json-schema.org/draft-07/schema#\"}"
        }
      },
      "credentials": {
        "airtopApi": {
          "id": "byhouJF8RLH5DkmY",
          "name": "Airtop"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d75dbdce-7a7e-4a30-81b0-6e8fa5221f55",
      "name": "Get people",
      "type": "n8n-nodes-base.airtop",
      "position": [
        880,
        0
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "prompt": "=This is a list of {{ $('Parameters').item.json.who }} on {{ $('Parameters').item.json.where }}.\nExtract up to 20 items. For each person extract: \n- name \n- handle or ID \n- URL",
        "resource": "extraction",
        "operation": "query",
        "sessionMode": "new",
        "additionalFields": {
          "outputSchema": "{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"items\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"name\": {\n            \"type\": \"string\",\n            \"description\": \"The name of the item.\"\n          },\n          \"identifier\": {\n            \"type\": \"string\",\n            \"description\": \"The unique identifier or handle for the item.\"\n          },\n          \"url\": {\n            \"type\": \"string\",\n            \"description\": \"The URL to access the item or its related resource.\"\n          }\n        },\n        \"required\": [\n          \"name\",\n          \"identifier\",\n          \"url\"\n        ],\n        \"additionalProperties\": false\n      }\n    }\n  },\n  \"required\": [\n    \"items\"\n  ],\n  \"additionalProperties\": false\n}"
        }
      },
      "credentials": {
        "airtopApi": {
          "id": "byhouJF8RLH5DkmY",
          "name": "Airtop"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "285ce6af-bda8-4025-872e-f5f8c4a56b3c",
      "name": "Dedupe results",
      "type": "n8n-nodes-base.code",
      "position": [
        1100,
        0
      ],
      "parameters": {
        "jsCode": "const allResults = []\n\nfor (const inputItem of $input.all()) {\n  // Parse input to JSON\n  const input = inputItem.json.data.modelResponse\n  const results = JSON.parse(input).items\n  // clean results\n  const cleanedResults = results\n    .filter((res) => res.name) // only those with name\n    .map((res) => ({\n      ...res,\n      url: res.url.split('?')[0] // clean url\n    }))\n  // add results to list\n  allResults.push(...cleanedResults)\n}\n\n// Dedupe urls\nconst uniqueList = allResults.filter((item, index, self) =>\n  index === self.findIndex((t) => (t.url === item.url))\n);\n\nreturn uniqueList.map((item) => ({\n  json: {...item}\n}));"
      },
      "typeVersion": 2
    },
    {
      "id": "fdc86d5c-4df3-48b9-9cf6-a5ddc3b45c90",
      "name": "Add to spreadsheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1320,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "URL": "={{ $json.url }}",
            "Name": "={{ $json.name }}",
            "Who?": "={{ $('Parameters').first().json.who }}",
            "Where?": "={{ $('Parameters').first().json.where }}",
            "Added on": "={{ $now }}",
            "ID or Handle": "={{ $json.identifier }}"
          },
          "schema": [
            {
              "id": "Who?",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Who?",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Where?",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Where?",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID or Handle",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "ID or Handle",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Added on",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Added on",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/150eh4t5GyEBN_TcO5TDeNWpE2GzHR4hQWoNRbUpw7A0/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "150eh4t5GyEBN_TcO5TDeNWpE2GzHR4hQWoNRbUpw7A0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/150eh4t5GyEBN_TcO5TDeNWpE2GzHR4hQWoNRbUpw7A0/edit?usp=drivesdk",
          "cachedResultName": "List Builder"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "CwpCAR1HwgHZpRtJ",
          "name": "Google Drive"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "44c54497-741c-4c48-b4f9-0c5c836d10ad",
      "name": "Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        220,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "bc2d3cb9-b7d0-4c3f-b392-53262d60441e",
              "name": "who",
              "type": "string",
              "value": "Top \"Build in Public\" influencers"
            },
            {
              "id": "b2cfa361-c80a-4945-bc0e-23eac5edebd6",
              "name": "where",
              "type": "string",
              "value": "X"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "connections": {
    "Get urls": {
      "main": [
        [
          {
            "node": "Format results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get people": {
      "main": [
        [
          {
            "node": "Dedupe results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parameters": {
      "main": [
        [
          {
            "node": "Get urls",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe results": {
      "main": [
        [
          {
            "node": "Add to spreadsheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format results": {
      "main": [
        [
          {
            "node": "Get people",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Test workflow’": {
      "main": [
        [
          {
            "node": "Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Actions

Share
Categories:
Tags:
manualtriggersetgooglesheetscodeairtop

Technical Specs

Complexity:
Intermediate
Node Count:
7 nodes
Published:11 months ago
Updated:11 days ago

Compatibility

Platform:
N8N Cloud & Self-hosted
Min Version:
N8N v1.0.0+
Supports cloud and self-hosted deployment

License

This workflow template follows MIT license, you can freely use, modify and distribute.

Please comply with relevant third-party service terms when using.

Related Workflows