This workflow corresponds to n8n.io template #7373 — 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 →
{
"nodes": [
{
"id": "77e9fdfd-ee83-4394-a938-ceb6be4a58a8",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-1840,
-32
],
"parameters": {},
"typeVersion": 1
},
{
"id": "04569eb9-91d9-4eda-a3b7-f1044b3aacf2",
"name": "make hashtag links",
"type": "n8n-nodes-base.code",
"position": [
-1216,
-32
],
"parameters": {
"jsCode": "// Mode: Run once for all items\nconst tags = $input.first().json.Hashtag || []; // [\"vegan\", \"travel\", \u2026]\n\nconst startUrls = tags.map(\n t => `\"https://www.instagram.com/explore/tags/${encodeURIComponent(t)}/\"`\n);\n\n// Build the final body Apify expects\nreturn [{ // or delete if you don\u2019t need the date filter\n startUrls // \u2190 trailing slash now added\n // customMapFunction left out \u2014 see note below\n}];\n"
},
"typeVersion": 2
},
{
"id": "45305b08-fd9f-4872-b4e3-e37d38b866d0",
"name": "Scrape instagram hashtag posts",
"type": "n8n-nodes-base.httpRequest",
"position": [
-976,
-32
],
"parameters": {
"url": "https://api.apify.com/v2/acts/culc72xb7MP3EbaeX/run-sync-get-dataset-items",
"method": "POST",
"options": {
"redirect": {
"redirect": {}
}
},
"jsonBody": "={\n \"maxItems\": 300,\n \"startUrls\": [\n{{ $json.startUrls }}\n ],\n \"until\": \"2025-06-20\"\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/json"
},
{
"name": "Authorization",
"value": "Bearer YOUR_TOKEN_HERE"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "067ed683-3a63-4d59-8c23-2bb87d8d3287",
"name": "IF \u2014 Hashtags Only",
"type": "n8n-nodes-base.if",
"position": [
-560,
-32
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-only-hashtags",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.hasHashtags && ($json.textNoTags || '').trim() === '' }}"
}
]
}
},
"typeVersion": 2
},
{
"id": "43e985f4-579d-49ed-839c-7b5809d8ddd8",
"name": "IF \u2014 English Only",
"type": "n8n-nodes-base.if",
"position": [
-352,
64
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-english-only",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.enHits > 0 && $json.frHits === 0 && $json.esHits === 0 }}"
}
]
}
},
"typeVersion": 2
},
{
"id": "2b17b052-1474-40a3-8c86-6eb450969b2b",
"name": "Aggregate Hashtags",
"type": "n8n-nodes-base.aggregate",
"position": [
-1424,
-32
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "Hashtag"
}
]
}
},
"typeVersion": 1
},
{
"id": "9640b845-8f56-4dea-bcdc-f315e30b1224",
"name": "Format captions, usernames and data",
"type": "n8n-nodes-base.code",
"position": [
-768,
-32
],
"parameters": {
"jsCode": "return items.map(item => {\n const json = item.json;\n // 1) Original caption or title\n const caption = json.caption || json.title || '';\n json.caption = caption;\n // 2) Count hashtags & strip them + strip URLs\n const hashtags = caption.match(/#\\S+/g) || [];\n json.hashtagCount = hashtags.length;\n json.hasHashtags = json.hashtagCount > 0;\n const noUrls = caption.replace(/https?:\\/\\/\\S+/gi, '');\n const textNoTags = noUrls.replace(/#[^\\s#]+/g, '').replace(/\\.{2,}/g, ' ').replace(/\\s+/g, ' ').trim();\n json.textNoTags = textNoTags;\n json.hasOnlyHashtags = json.hasHashtags && textNoTags === '';\n // 3) Word array (no numbers-only, min length 2)\n let words = textNoTags.toLowerCase().split(/\\W+/).filter(w => w.length > 1 && !/^\\d+$/.test(w));\n const totalWords = words.length;\n json.totalWords = totalWords;\n // 4) Keyword hit lists\n const enKw = ['the','and','to','of','in','for','on','with','is','are','this','that','it','be','at','by','from','as','was','were','have','has','you','we','i'];\n const frKw = ['le','la','les','des','un','une','du','de','et','en','dans','pour','avec','est','sur','au','aux','ce','cet','cette','\u00e7a','pas','plus','que','qui','o\u00f9','je','tu','il','elle','nous','vous','ils','elles'];\n const esKw = ['el','la','los','las','de','del','y','en','por','para','con','es','un','una','que','como','muy','pero','si','porque'];\n json.enHits = words.filter(w => enKw.includes(w)).length;\n json.frHits = words.filter(w => frKw.includes(w)).length;\n json.esHits = words.filter(w => esKw.includes(w)).length;\n // 5) Ratios\n json.enRatio = totalWords ? json.enHits / totalWords : 0;\n json.frRatio = totalWords ? json.frHits / totalWords : 0;\n json.esRatio = totalWords ? json.esHits / totalWords : 0;\n // 6) ASCII vs non-ASCII\n const asciiCount = textNoTags.replace(/[^\\x00-\\x7F]/g, '').length;\n json.asciiRatio = textNoTags.length ? asciiCount / textNoTags.length : 0;\n // 7) Accent & script flags\n json.hasFrenchAccent = /[\u00e9\u00e8\u00ea\u00eb\u00e0\u00e2\u00ee\u00ef\u00f4\u00f6\u00f9\u00fb\u00e7\u0153\u00e6]/i.test(textNoTags);\n json.hasSpanishAccentStrict = /[\u00f1\u00a1\u00bf]/.test(textNoTags);\n json.hasArabicOrCJK = /[\\u0600-\\u06FF\\u0750-\\u077F\\u4E00-\\u9FFF\\u3040-\\u30FF\\uAC00-\\uD7AF]/u.test(textNoTags);\n return { json };\n});"
},
"typeVersion": 2
},
{
"id": "6626209f-fc93-4688-a41c-51606d03c887",
"name": "Combine all usernames",
"type": "n8n-nodes-base.aggregate",
"position": [
112,
-64
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "owner.username"
}
]
}
},
"typeVersion": 1
},
{
"id": "1d876779-5409-4214-b6b0-48276e44ab41",
"name": "Remove Duplicate Usernames",
"type": "n8n-nodes-base.removeDuplicates",
"position": [
-112,
-64
],
"parameters": {
"compare": "selectedFields",
"options": {},
"fieldsToCompare": "owner.username"
},
"typeVersion": 2
},
{
"id": "12f7a1bd-4ce4-47c0-ae1d-2444ecd7ac6d",
"name": "Scrape instagram Profiles",
"type": "n8n-nodes-base.httpRequest",
"position": [
384,
-64
],
"parameters": {
"url": "https://api.apify.com/v2/acts/dSCLg0C3YEZ83HzYX/run-sync-get-dataset-items",
"method": "POST",
"options": {
"redirect": {
"redirect": {}
}
},
"jsonBody": "={\n \"usernames\": [\n \t{{ $json.username.map(u => `\"${u}\"`).join(\", \") }}\n ]\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Accept",
"value": "application/json"
},
{
"name": "Authorization",
"value": "Bearer YOUR_TOKEN_HERE"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "3288d091-4f14-4c7b-8813-7a4a2b64db66",
"name": "If followers are in a certain range continue",
"type": "n8n-nodes-base.if",
"position": [
592,
-64
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "7b585f6a-c548-4b62-a6e8-0bcf6c7ba771",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.followersCount }}",
"rightValue": 10000
},
{
"id": "4753a598-fe31-4670-99f2-a0d5ad164074",
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ $json.followersCount }}",
"rightValue": 100000
}
]
}
},
"typeVersion": 2.2
},
{
"id": "79d0609c-f68b-41fa-96ec-cc0676fdd706",
"name": "Get list of Hashtags",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1632,
-32
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "id",
"value": "SHEET_ID"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "SPREADSHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "08bc2863-b41f-4e1d-99db-30e03b1b1c01",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-704,
-736
],
"parameters": {
"color": 5,
"width": 512,
"height": 240,
"content": "# Workflow Overview\n- Start: Get a list of hashtags from Google Sheets\n- Scrape Instagram posts for each hashtag (via Apify)\n- Analyze captions: extract keywords, check for language & hashtags\n- Gather/post-process usernames, scrape their profiles\n- Filter users based on followers count\n- Result: List of users meeting your criteria"
},
"typeVersion": 1
},
{
"id": "1066082c-43db-4e5b-b9be-4beb44d9061a",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1680,
-272
],
"parameters": {
"color": 4,
"width": 528,
"height": 144,
"content": "## Read Hashtags & Prepare Instagram Links\n- Retrieve all hashtags from a Google Sheet\n- Build direct Instagram tag-explore URLs for each hashtag\n- Output is a list of start URLs for scraping Instagram hashtag feeds"
},
"typeVersion": 1
},
{
"id": "f5527a08-5916-4234-9225-835262206952",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-752,
-288
],
"parameters": {
"color": 2,
"width": 560,
"height": 176,
"content": "## Analyze Post Captions\n- Extract and process each Instagram caption\n- Count & strip hashtags, links, and analyze language (English/French/Spanish) using keyword lists\n- Mark posts as 'hashtags only', and apply logic to filter for language or other text patterns"
},
"typeVersion": 1
},
{
"id": "4e0a1454-b1c2-4af2-b3f0-d3328d3956c4",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
112,
-288
],
"parameters": {
"color": 3,
"width": 592,
"content": "# Gather & Filter User Profiles\n- Combine all usernames from found posts, remove duplicates\n- Scrape profile details (like follower count) for each username\n- Only allow users whose follower counts are within a target range for your use case"
},
"typeVersion": 1
}
],
"connections": {
"Aggregate Hashtags": {
"main": [
[
{
"node": "make hashtag links",
"type": "main",
"index": 0
}
]
]
},
"make hashtag links": {
"main": [
[
{
"node": "Scrape instagram hashtag posts",
"type": "main",
"index": 0
}
]
]
},
"IF \u2014 English Only": {
"main": [
[
{
"node": "Remove Duplicate Usernames",
"type": "main",
"index": 0
}
]
]
},
"Get list of Hashtags": {
"main": [
[
{
"node": "Aggregate Hashtags",
"type": "main",
"index": 0
}
]
]
},
"IF \u2014 Hashtags Only": {
"main": [
[
{
"node": "Remove Duplicate Usernames",
"type": "main",
"index": 0
}
],
[
{
"node": "IF \u2014 English Only",
"type": "main",
"index": 0
}
]
]
},
"Combine all usernames": {
"main": [
[
{
"node": "Scrape instagram Profiles",
"type": "main",
"index": 0
}
]
]
},
"Scrape instagram Profiles": {
"main": [
[
{
"node": "If followers are in a certain range continue",
"type": "main",
"index": 0
}
]
]
},
"Remove Duplicate Usernames": {
"main": [
[
{
"node": "Combine all usernames",
"type": "main",
"index": 0
}
]
]
},
"Scrape instagram hashtag posts": {
"main": [
[
{
"node": "Format captions, usernames and data",
"type": "main",
"index": 0
}
]
]
},
"Format captions, usernames and data": {
"main": [
[
{
"node": "IF \u2014 Hashtags Only",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Get list of Hashtags",
"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.
googleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Automate the process of finding and qualifying Instagram leads based on hashtags. This workflow reads hashtags from Google Sheets, scrapes Instagram for posts using Apify, analyzes caption content and language, compiles unique usernames, gathers detailed user info, and filters…
Source: https://n8n.io/workflows/7373/ — 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.
Automate LinkedIn lead generation by scraping comments from targeted posts and enriching profiles with detailed data
This automated n8n workflow scrapes job listings from Upwork using Apify, processes and cleans the data, and generates daily email reports with job summaries. The system uses Google Sheets for data st
Transform LinkedIn profile URLs into comprehensive enriched lead profiles, quickly and automatically.
Transform any website into a structured knowledge repository with this intelligent crawler that extracts hyperlinks from the homepage, intelligently filters images and content pages, and aggregates fu
Content creators, researchers, educators, and digital marketers who need to discover high-quality YouTube training videos on specific topics. Perfect for building curated learning resource lists, comp