Workflow: TopSourcer - Finds LinkedIn Profiles using natural language
Integrations: code, googleSheets, httpRequest, if, manualTrigger, noOp, stickyNote, wait

{"id": "UsBaGY83vnyZjRoB", "meta": {"instanceId": "workflow-d4ec16e1", "versionId": "1.0.0", "createdAt": "2025-09-29T07:07:59.496285", "updatedAt": "2025-09-29T07:07:59.496294", "owner": "n8n-user", "license": "MIT", "category": "automation", "status": "active", "priority": "high", "environment": "production"}, "name": "TopSourcer - Finds LinkedIn Profiles using natural language", "tags": ["automation", "n8n", "production-ready", "excellent", "optimized"], "nodes": [{"id": "trigger-0f3d32a3", "name": "Manual Trigger", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [100, 100], "parameters": {}}, {"id": "16a5f4a2-6e00-40f5-bab7-35526550eacd", "name": "Wait", "type": "n8n-nodes-base.wait", "position": [3240, -280], "webhookId": "88c6a5cc-4b33-438c-ba85-2e075a276a78", "parameters": {}, "typeVersion": 1.1, "notes": "This wait node performs automated tasks as part of the workflow."}, {"id": "f9ff2e4f-176b-453d-8743-cab4d9fd408d", "name": "When chat message received", "type": "n8n-nodes-base.noOp", "position": [1040, -180], "webhookId": "475042df-7c36-4658-ab1c-ff55c237621f", "parameters": {"options": {}}, "typeVersion": 1.1, "notes": "This chatTrigger node performs automated tasks as part of the workflow."}, {"id": "b988c049-2400-4a3a-b615-f4048832bd8d", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [980, -340], "parameters": {"content": "Click \"Open Chat\" after activating the workflow.\n\nHere, paste in a job description or describe your ideal candidate."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "74cec892-07d6-4e7d-9c6f-becfb51241c8", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [1300, -300], "parameters": {"width": 300, "content": "Under \"Credential to connect with\" add your openAI API key. Find at: {{ $env.API_BASE_URL }}\n"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "940373af-ca88-44f1-b3c3-fb125ab6daf9", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [2800, -360], "parameters": {"width": 300, "content": "For the first condition: {{ $json.start }} is less than 50, so change \"50\" to your desired number of results. \n\nEach loop fetches the next page, returning 10 results per iteration."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "c2bc0757-753b-4fee-b42b-65e5a0ff4750", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [3160, -440], "parameters": {"color": 5, "width": 200, "content": "Waits 5 seconds to avoid rate limiting by Google. While it's unlikely you'll be rate-limited since you're authenticated with your cookie, this is just a precaution."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "9007b42b-1a79-4b98-9d75-71894d660c1d", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [3400, -600], "parameters": {"color": 4, "width": 380, "height": 280, "content": "Get this Cookie-Editor. {{ $env.WEBHOOK_URL }}\n\nDo a google search --> click this extension --> Export --> Header string.\n\nThen, open this node --> under Header Auth --> edit --> and under cookie value paste in your header string.  \n\nThis is to perform an authenticated google search.\n"}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "b1d2f9dd-227d-4372-89f8-e6d54e94f2fc", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [1680, -320], "parameters": {"content": "Connect your google sheets account and create a document."}, "typeVersion": 1, "notes": "This stickyNote node performs automated tasks as part of the workflow."}, {"id": "df0fb397-55d0-41ec-a9df-2c39019ad68e", "name": "Create a new sheet", "type": "n8n-nodes-base.googleSheets", "position": [1740, -180], "parameters": {"title": "={{ $('Generate a Boolean Search String').item.json.choices[0].message.content.sheet_name + ' ' + $now }}\n", "options": {}, "operation": "create", "documentId": {"__rl": true, "mode": "list", "value": "1M9UUgw1wPZIBSoPiGTvNIgA19ERgOo5KmD9wx__Y8ZY", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Candidates"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "6wBRjmD77d71tAqP", "name": "Google Sheets account"}}, "typeVersion": 4.5, "notes": "This googleSheets node performs automated tasks as part of the workflow."}, {"id": "678a0b65-de67-41f0-ada6-23cef1226228", "name": "Add columns to new sheet", "type": "n8n-nodes-base.googleSheets", "position": [2220, -180], "parameters": {"columns": {"value": {}, "schema": [{"id": "linkedin_url", "type": "string", "display": true, "removed": false, "required": false, "displayName": "linkedin_url", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "autoMapInputData", "matchingColumns": [], "attemptToConvertTypes": false, "convertFieldsToString": false}, "options": {}, "operation": "append", "sheetName": {"__rl": true, "mode": "id", "value": "={{ $('Create a new sheet').item.json.sheetId }}"}, "documentId": {"__rl": true, "mode": "list", "value": "1M9UUgw1wPZIBSoPiGTvNIgA19ERgOo5KmD9wx__Y8ZY", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Candidates"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "6wBRjmD77d71tAqP", "name": "Google Sheets account"}}, "typeVersion": 4.5, "notes": "This googleSheets node performs automated tasks as part of the workflow."}, {"id": "767491ab-f7dd-4e23-816b-840bc24e5268", "name": "set page number for google search", "type": "n8n-nodes-base.code", "position": [2480, -180], "parameters": {"jsCode": "return [{ json: { start: 0 } }];\n"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "f76f28a5-8444-4ff9-b62c-0d94a07c6447", "name": "Extracts all linkedin urls from the google http response", "type": "n8n-nodes-base.code", "position": [3740, -280], "parameters": {"jsCode": "// Extract LinkedIn profile URLs from HTML\nfunction extractLinkedInUrls(html) {\n    // First decode any encoded HTML entities\n    html = html.replace(/&amp;/g, '&')\n               .replace(/\\\\u003d/g, '=')\n               .replace(/\\\\x22/g, '\"')\n               .replace(/\\\\x26/g, '&')\n               .replace(/\\\\x3e/g, '>')\n               .replace(/\\\\x3c/g, '<');\n\n    const patterns = [\n        // Standard LinkedIn URLs in href\n        /(?:https?:)?\\/\\/(?:[a-z]{2,}\\.)?linkedin\\.com\\/in\\/[a-zA-Z0-9._-]+(?:\\/[a-z]{2})?/gi,\n        // URLs in encoded strings\n        /(?:\"url\"|url=)(?:[^\"&]*?)(?:https?:)?\\/\\/(?:[a-z]{2,}\\.)?linkedin\\.com\\/in\\/[a-zA-Z0-9._-]+(?:\\/[a-z]{2})?/gi,\n        // URLs in JSON strings\n        /\"(?:https?:)?\\/\\/(?:[a-z]{2,}\\.)?linkedin\\.com\\/in\\/[a-zA-Z0-9._-]+(?:\\/[a-z]{2})?\"/gi\n    ];\n\n    const urls = new Set();\n    \n    patterns.forEach(pattern => {\n        const matches = html.matchAll(pattern);\n        for (const match of matches) {\n            let url = match[0];\n            \n            // Clean up the URL\n            url = url.replace(/^\"url\"|^url=|\"$/g, '') // Remove url= prefix and quotes\n                    .replace(/^[\"']|[\"']$/g, '')      // Remove surrounding quotes\n                    .replace(/\\\\+/g, '')              // Remove backslashes\n                    .trim();\n            \n            // Ensure URL has protocol\n            if (!url.startsWith('http')) {\n                url = 'https://' + url.replace(/^\\/\\//, '');\n            }\n            \n            // Only include if it's a LinkedIn profile URL\n            if (url.includes('linkedin.com/in/')) {\n                // Clean the URL: remove tracking parameters and fragments\n                url = url.split(/[?#&]/)[0];\n                \n                // Remove any trailing slashes\n                url = url.replace(/\\/$/, '');\n                \n                // Add to Set to remove duplicates\n                urls.add(url);\n            }\n        }\n    });\n\n    return Array.from(urls);\n}\n\n// Get the HTML from input\nconst html = $input.first().json.data;\n\n// Extract URLs and create array of objects\nconst linkedInProfiles = extractLinkedInUrls(html)\n    .filter(url => !url.includes('google.com')) // Extra safety check to remove any Google URLs\n    .map(url => ({\n        linkedin_url: url\n    }));\n\n// Return the array of objects directly\nreturn linkedInProfiles;"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "5a93c8f2-f55b-4d0e-92f8-0d86147f8d13", "name": "Google Boolean Search", "type": "n8n-nodes-base.httpRequest", "position": [3500, -300], "parameters": {"url": "{{ $env.WEBHOOK_URL }}", "options": {}, "sendQuery": true, "sendHeaders": true, "authentication": "{{ $credentials.genericCredentialType }}", "genericAuthType": "httpHeaderAuth", "queryParameters": {"parameters": [{"name": "q", "value": "={{ $('Generate a Boolean Search String').first().json.choices[0].message.content.search_string }}\n"}, {"name": "start", "value": "={{ $json.start }}"}]}, "headerParameters": {"parameters": [{"name": "User-Agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"}]}}, "credentials": {"httpHeaderAuth": {"id": "5T6POWjsPfV558Ta", "name": "Header Auth account"}}, "typeVersion": 4.2, "notes": "This httpRequest node performs automated tasks as part of the workflow."}, {"id": "37b4f264-34f0-47bb-9b1b-fa53beafb2a9", "name": "Generate a Boolean Search String", "type": "n8n-nodes-base.noOp", "position": [1320, -180], "parameters": {"modelId": {"__rl": true, "mode": "list", "value": "gpt-4o-mini", "cachedResultName": "GPT-4O-MINI"}, "options": {}, "messages": {"values": [{"role": "system", "content": "You are an expert in Boolean search techniques for Google. When the user send a job description, generate a search string specifically for finding LinkedIn profiles. Your response must always follow this exact format:\nsite:linkedin.com/in [Boolean search string]\nCreate the Boolean search string using precise operators (AND, OR, \"\", *, -) to match the job requirements. Focus only on generating the search string - provide no additional commentary or explanations unless specifically requested.\n\nAlso return sheet_name (less than 100 char)"}, {"content": "={{ $json.chatInput }}"}]}, "simplify": false, "jsonOutput": true}, "credentials": {"openAiApi": {"id": "EX7mky4RGLDD6udW", "name": "OpenAi account"}}, "retryOnFail": false, "typeVersion": 1.8, "notes": "This openAi node performs automated tasks as part of the workflow."}, {"id": "99041eff-f094-4c2a-a75a-4b01faf33d1b", "name": "If desired results not reached", "type": "n8n-nodes-base.if", "position": [2920, -200], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "da9f8de0-1e75-4ff3-9f81-8e911251416b", "operator": {"type": "number", "operation": "lt"}, "leftValue": "={{ $json.start }}", "rightValue": 50}, {"id": "a891c085-7f49-4523-8610-40577b3ffd3b", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "", "rightValue": ""}]}}, "typeVersion": 2.2, "notes": "This if node performs automated tasks as part of the workflow."}, {"id": "d218a1e8-2959-4b7c-a84d-f8e0df82c5e7", "name": "Appends the results to the sheet", "type": "n8n-nodes-base.googleSheets", "position": [4040, -280], "parameters": {"columns": {"value": {"linkedin_url": "{{ $env.BASE_URL }}"}, "schema": [{"id": "linkedin_url", "type": "string", "display": true, "removed": false, "required": false, "displayName": "linkedin_url", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Full Name", "type": "string", "display": true, "required": false, "displayName": "Full Name", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "First Name", "type": "string", "display": true, "required": false, "displayName": "First Name", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Headline", "type": "string", "display": true, "required": false, "displayName": "Headline", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Candidate Summary", "type": "string", "display": true, "required": false, "displayName": "Candidate Summary", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Experiences Summary", "type": "string", "display": true, "removed": false, "required": false, "displayName": "Experiences Summary", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Education Summary", "type": "string", "display": true, "removed": false, "required": false, "displayName": "Education Summary", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Skills", "type": "string", "display": true, "required": false, "displayName": "Skills", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "City", "type": "string", "display": true, "removed": false, "required": false, "displayName": "City", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Country", "type": "string", "display": true, "removed": false, "required": false, "displayName": "Country", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Criteria_Assessment", "type": "string", "display": true, "removed": false, "required": false, "displayName": "Criteria_Assessment", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "overall_fit_score", "type": "string", "display": true, "removed": false, "required": false, "displayName": "overall_fit_score", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "score_justification", "type": "string", "display": true, "removed": false, "required": false, "displayName": "score_justification", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Company", "type": "string", "display": true, "required": false, "displayName": "Company", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Company Industry", "type": "string", "display": true, "required": false, "displayName": "Company Industry", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Company Size", "type": "string", "display": true, "required": false, "displayName": "Company Size", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Company LinkedIn URL", "type": "string", "display": true, "required": false, "displayName": "Company LinkedIn URL", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Company Website", "type": "string", "display": true, "required": false, "displayName": "Company Website", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Current Company Join Date", "type": "string", "display": true, "required": false, "displayName": "Current Company Join Date", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Certifications", "type": "string", "display": true, "required": false, "displayName": "Certifications", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Courses Taken", "type": "string", "display": true, "required": false, "displayName": "Courses Taken", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Email", "type": "string", "display": true, "required": false, "displayName": "Email", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Phone", "type": "string", "display": true, "required": false, "displayName": "Phone", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Connections Count", "type": "string", "display": true, "required": false, "displayName": "Connections Count", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Followers Count", "type": "string", "display": true, "required": false, "displayName": "Followers Count", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Languages Spoken", "type": "string", "display": true, "required": false, "displayName": "Languages Spoken", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Projects", "type": "string", "display": true, "required": false, "displayName": "Projects", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "Date Created", "type": "string", "display": true, "required": false, "displayName": "Date Created", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "defineBelow", "matchingColumns": [], "attemptToConvertTypes": false, "convertFieldsToString": false}, "options": {}, "operation": "append", "sheetName": {"__rl": true, "mode": "id", "value": "={{ $('Create a new sheet').first().json.sheetId }}"}, "documentId": {"__rl": true, "mode": "list", "value": "1M9UUgw1wPZIBSoPiGTvNIgA19ERgOo5KmD9wx__Y8ZY", "cachedResultUrl": "{{ $env.WEBHOOK_URL }}", "cachedResultName": "Candidates"}}, "credentials": {"googleSheetsOAuth2Api": {"id": "6wBRjmD77d71tAqP", "name": "Google Sheets account"}}, "typeVersion": 4.5, "notes": "This googleSheets node performs automated tasks as part of the workflow."}, {"id": "7a56e7d7-31f8-4115-b993-227bd7221c07", "name": "Adds 10 to start - Go to next page", "type": "n8n-nodes-base.code", "position": [4340, -220], "parameters": {"jsCode": "// Get the start value from 'Edit Fields2' node\nconst startValue =$('If desired results not reached').first().json.start;\n\n// Add 10 to the start value\nconst start = startValue + 10;\n\n// Return the new value\nreturn [{ json: { start } }];\n"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}, {"id": "afe22fc0-c9c1-4aab-a11d-d91740f812bb", "name": "Columns to add", "type": "n8n-nodes-base.code", "position": [1980, -180], "parameters": {"jsCode": "return [{\n  json: {\n    \"linkedin_url\": \"\"\n  }\n}];\n"}, "typeVersion": 2, "notes": "This code node performs automated tasks as part of the workflow."}], "active": true, "pinData": {}, "settings": {"executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": null, "timezone": "UTC", "executionTimeout": 3600, "maxExecutions": 1000, "retryOnFail": true, "retryCount": 3, "retryDelay": 1000}, "versionId": "ce389fd9-7697-4e36-8346-6be9414aecf2", "connections": {"5a93c8f2-f55b-4d0e-92f8-0d86147f8d13": {"main": [[{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-fe5c9b42", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-c7800d34", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-20462ae2", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-ea8c1282", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-7e848b35", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-6e26072b", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-67e2c9b6", "type": "main", "index": 0}], [{"node": "error-handler-5a93c8f2-f55b-4d0e-92f8-0d86147f8d13-20110577", "type": "main", "index": 0}]]}, "df0fb397-55d0-41ec-a9df-2c39019ad68e": {"main": [[{"node": "error-handler-df0fb397-55d0-41ec-a9df-2c39019ad68e-a4229c06", "type": "main", "index": 0}]]}, "678a0b65-de67-41f0-ada6-23cef1226228": {"main": [[{"node": "error-handler-678a0b65-de67-41f0-ada6-23cef1226228-0ad75b4b", "type": "main", "index": 0}]]}, "37b4f264-34f0-47bb-9b1b-fa53beafb2a9": {"main": [[{"node": "error-handler-37b4f264-34f0-47bb-9b1b-fa53beafb2a9-2999a04c", "type": "main", "index": 0}]]}, "d218a1e8-2959-4b7c-a84d-f8e0df82c5e7": {"main": [[{"node": "error-handler-d218a1e8-2959-4b7c-a84d-f8e0df82c5e7-afe7d136", "type": "main", "index": 0}]]}}, "description": "Automated workflow: TopSourcer - Finds LinkedIn Profiles using natural language. This workflow integrates 9 different services: stickyNote, httpRequest, wait, code, stopAndError. It contains 24 nodes and follows best practices for error handling and security.", "notes": "Excellent quality workflow: TopSourcer - Finds LinkedIn Profiles using natural language. This workflow has been optimized for production use with comprehensive error handling, security, and documentation."}