This workflow corresponds to n8n.io template #3479 — we link there as the canonical source.
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 →
{
"id": "VwU1zMhcgzgPS9ak",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "List Builder",
"tags": [
{
"id": "a8B9vqj0vNLXcKVQ",
"name": "template",
"createdAt": "2025-04-04T15:38:37.785Z",
"updatedAt": "2025-04-04T15:38:37.785Z"
}
],
"nodes": [
{
"id": "1a6aa574-467d-40b0-a9a5-a5537bede3de",
"name": "When clicking \u2018Test workflow\u2019",
"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": {
"name": "<your credential>"
}
},
"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": {
"name": "<your credential>"
}
},
"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": {
"name": "<your credential>"
}
},
"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
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "a2572aec-54cb-4bbb-b503-09d1d0beb64f",
"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 \u2018Test workflow\u2019": {
"main": [
[
{
"node": "Parameters",
"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.
airtopApigoogleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This automation will guide you on how to automate list building using Airtop. You’ll have a streamlined workflow that can reduce your research time by up to 90% while improving the accuracy of your target lists.
Source: https://n8n.io/workflows/3479/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Automating web scraping with recursive depth is ideal for collecting content across multiple linked pages—perfect for content aggregation, lead generation, or research projects.
List Builder. Uses manualTrigger, airtop, googleSheets. Event-driven trigger; 7 nodes.
Manual Googlesheets. Uses manualTrigger, airtop, googleSheets. Event-driven trigger; 5 nodes.
ICP Company Scoring. Uses manualTrigger, googleSheets, airtop. Event-driven trigger; 5 nodes.
LinkedIn Profile Discovery. Uses manualTrigger, googleSheets, airtop. Event-driven trigger; 5 nodes.