AutomationFlowsMarketing & Ads › Comet Leadgen Pipeline with Scrap.io

Comet Leadgen Pipeline with Scrap.io

Original n8n title: Comet Leadgen Pipeline

Comet LeadGen Pipeline. Uses httpRequest. Scheduled trigger; 12 nodes.

Cron / scheduled trigger★★★★☆ complexity12 nodesHTTP Request
Marketing & Ads Trigger: Cron / scheduled Nodes: 12 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "name": "Comet LeadGen Pipeline",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 19 * * 2,3,4"
            }
          ]
        }
      },
      "name": "Cron \u2014 Tu/We/Th 19:00 UTC",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        240,
        300
      ],
      "id": "node-cron-01"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "comet-trigger",
        "responseMode": "responseNode",
        "options": {}
      },
      "name": "Webhook Trigger (manual)",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        240,
        460
      ],
      "id": "node-webhook-01"
    },
    {
      "parameters": {
        "functionCode": "// Step 1 \u2014 Merge trigger, validate payload\nconst data = $input.item.json;\nconst icp = data.icp || 'usa_dental_medspa';\nconst dryRun = data.dry_run || false;\n\nconst validIcps = ['usa_dental_medspa', 'pl_logistyka_wielkopolska'];\nif (!validIcps.includes(icp)) {\n  throw new Error(`Unknown ICP: ${icp}. Valid: ${validIcps.join(', ')}`);\n}\n\nreturn [{\n  json: {\n    project_id: `CP-${new Date().toISOString().replace(/[^0-9]/g, '').slice(0,14)}`,\n    icp,\n    dry_run: dryRun,\n    started_at: new Date().toISOString(),\n    status: 'AWAITING_SCRAPE'\n  }\n}];"
      },
      "name": "Step 1 \u2014 Init Pipeline",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        460,
        380
      ],
      "id": "node-step1"
    },
    {
      "parameters": {
        "url": "={{$env['SCRAPIO_BASE_URL']}}/companies/search",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env['SCRAPIO_API_KEY']}}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "industry",
              "value": "={{$json.icp === 'usa_dental_medspa' ? 'dental healthcare medspa' : 'logistics transportation'}}"
            },
            {
              "name": "location",
              "value": "={{$json.icp === 'usa_dental_medspa' ? 'USA' : 'Wielkopolska Poland'}}"
            },
            {
              "name": "limit",
              "value": "=50"
            }
          ]
        },
        "options": {
          "timeout": 30000
        }
      },
      "name": "Step 2 \u2014 Scrap.io Search",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        680,
        380
      ],
      "id": "node-step2"
    },
    {
      "parameters": {
        "functionCode": "// Step 3 \u2014 Build search params + filter Scrap.io results\nconst state = $('Step 1 \u2014 Init Pipeline').item.json;\nconst scrapio = $input.item.json;\n\nconst companies = scrapio.companies || [];\n\n// Map do naszego formatu\nconst rawLeads = companies.map((c, i) => ({\n  id: `${state.project_id}-${String(i+1).padStart(3,'0')}`,\n  name: c.name || '',\n  address: c.address || '',\n  phone: c.phone || null,\n  website: c.website || null,\n  email: c.email || null,\n  employee_count: c.employee_count || null,\n  linkedin_url: c.linkedin_url || null,\n  source: 'scrapio'\n})).filter(l => l.name);\n\nreturn [{\n  json: {\n    ...state,\n    raw_leads: rawLeads,\n    raw_leads_count: rawLeads.length,\n    status: 'AWAITING_LLM_PARSE'\n  }\n}];"
      },
      "name": "Step 3 \u2014 Build Search Params",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        900,
        380
      ],
      "id": "node-step3"
    },
    {
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env['OPENAI_API_KEY']}}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "gpt-4o-mini"
            },
            {
              "name": "messages",
              "value": "=[{\"role\":\"system\",\"content\":\"You are a B2B lead qualifier. Return valid JSON only.\"},{\"role\":\"user\",\"content\":\"Qualify these leads and score A/B/C fit for AI automation services. Return JSON array with fields: name, fit_score, confidence (0-100), approach_angle, hours_saved_monthly, disqualify (bool). Leads: \" + JSON.stringify($json.raw_leads.slice(0,10))}]"
            },
            {
              "name": "response_format",
              "value": "={\"type\":\"json_object\"}"
            },
            {
              "name": "temperature",
              "value": "=0.1"
            }
          ]
        },
        "options": {
          "timeout": 60000
        }
      },
      "name": "Step 5 \u2014 LLM Parser (OpenAI)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        1120,
        380
      ],
      "id": "node-step5-llm"
    },
    {
      "parameters": {
        "functionCode": "// Step 5b \u2014 Parse LLM response + triage\nconst state = $('Step 3 \u2014 Build Search Params').item.json;\nconst llmResp = $input.item.json;\n\nlet parsedLeads = [];\ntry {\n  const content = llmResp.choices[0].message.content;\n  const data = JSON.parse(content);\n  parsedLeads = data.leads || data.results || [];\n} catch(e) {\n  console.error('LLM parse error:', e.message);\n}\n\n// Triage\nconst enrichedLeads = parsedLeads.filter(l => \n  !l.disqualify && \n  ['A','B'].includes(l.fit_score) && \n  (l.confidence || 0) >= 40\n);\n\nconst failedLeads = parsedLeads.filter(l => l.disqualify);\n\nreturn [{\n  json: {\n    ...state,\n    parsed_leads: parsedLeads,\n    enriched_leads: enrichedLeads,\n    failed_leads: failedLeads,\n    enriched_count: enrichedLeads.length,\n    status: 'AWAITING_CRM_EXPORT'\n  }\n}];"
      },
      "name": "Step 6 \u2014 Triage + Exception Check",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1340,
        380
      ],
      "id": "node-step6"
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{$json.enriched_count}}",
              "operation": "larger",
              "value2": 0
            }
          ]
        }
      },
      "name": "Has Qualified Leads?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1560,
        380
      ],
      "id": "node-if-leads"
    },
    {
      "parameters": {
        "url": "https://api.hubapi.com/crm/v3/objects/contacts/batch/create",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env['HUBSPOT_API_KEY']}}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "inputs",
              "value": "={{$json.enriched_leads.map(l => ({properties: {company: l.name, address: l._address || '', phone: l._phone || '', lead_fit_score: l.fit_score, approach_angle: l.approach_angle || '', hs_lead_status: 'NEW'}}))}}"
            }
          ]
        },
        "options": {
          "timeout": 30000
        }
      },
      "name": "Step 7a \u2014 HubSpot Export",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        1780,
        300
      ],
      "id": "node-step7-hubspot"
    },
    {
      "parameters": {
        "functionCode": "// Step 8 \u2014 Generate final report + Slack notification\nconst state = $('Step 6 \u2014 Triage + Exception Check').item.json;\nconst hubspotResult = $input.item.json;\n\nconst exportedCount = (hubspotResult.results || []).length;\nconst topLeads = (state.enriched_leads || []).slice(0,3).map(l => l.name).join(', ');\n\nconst report = {\n  project_id: state.project_id,\n  icp: state.icp,\n  status: 'DONE',\n  raw_count: state.raw_leads_count,\n  parsed_count: (state.parsed_leads || []).length,\n  exported_count: exportedCount,\n  top_leads: topLeads,\n  generated_at: new Date().toISOString()\n};\n\nreturn [{json: report}];"
      },
      "name": "Step 8 \u2014 Report Generator",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        2000,
        300
      ],
      "id": "node-step8"
    },
    {
      "parameters": {
        "url": "={{$env['SLACK_WEBHOOK_URL']}}",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "=\u2705 *Comet Pipeline {{$json.project_id}}* \u2014 DONE\n\ud83d\udcca Scraped: {{$json.raw_count}} | Exported: {{$json.exported_count}}\n\ud83c\udfc6 Top: {{$json.top_leads}}\n\ud83d\udccc ICP: `{{$json.icp}}`"
            }
          ]
        },
        "options": {}
      },
      "name": "Slack Notification",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        2220,
        300
      ],
      "id": "node-slack"
    },
    {
      "parameters": {
        "functionCode": "// No qualified leads \u2014 log and stop\nconst state = $json;\nconsole.log('No qualified leads for project:', state.project_id);\nreturn [{json: {...state, status: 'NO_LEADS'}}];"
      },
      "name": "Log \u2014 No Leads",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1780,
        480
      ],
      "id": "node-no-leads"
    }
  ],
  "connections": {
    "Cron \u2014 Tu/We/Th 19:00 UTC": {
      "main": [
        [
          {
            "node": "Step 1 \u2014 Init Pipeline",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger (manual)": {
      "main": [
        [
          {
            "node": "Step 1 \u2014 Init Pipeline",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 1 \u2014 Init Pipeline": {
      "main": [
        [
          {
            "node": "Step 2 \u2014 Scrap.io Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 2 \u2014 Scrap.io Search": {
      "main": [
        [
          {
            "node": "Step 3 \u2014 Build Search Params",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 3 \u2014 Build Search Params": {
      "main": [
        [
          {
            "node": "Step 5 \u2014 LLM Parser (OpenAI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 5 \u2014 LLM Parser (OpenAI)": {
      "main": [
        [
          {
            "node": "Step 6 \u2014 Triage + Exception Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 6 \u2014 Triage + Exception Check": {
      "main": [
        [
          {
            "node": "Has Qualified Leads?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Qualified Leads?": {
      "main": [
        [
          {
            "node": "Step 7a \u2014 HubSpot Export",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log \u2014 No Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 7a \u2014 HubSpot Export": {
      "main": [
        [
          {
            "node": "Step 8 \u2014 Report Generator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Step 8 \u2014 Report Generator": {
      "main": [
        [
          {
            "node": "Slack Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "errorWorkflow": "",
    "timezone": "UTC"
  },
  "tags": [
    "leadgen",
    "comet",
    "b2b",
    "n8n"
  ],
  "versionId": "1.0.0"
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Comet LeadGen Pipeline. Uses httpRequest. Scheduled trigger; 12 nodes.

Source: https://github.com/Gruszkoland/leadgen-comet/blob/master/n8n_workflows/comet_leadgen_pipeline.json — original creator credit. Request a take-down →

More Marketing & Ads workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Marketing & Ads

Workflow A — WhatsApp Lead Intake & Qualification. Uses postgres, httpRequest, errorTrigger. Scheduled trigger; 67 nodes.

Postgres, HTTP Request, Error Trigger
Marketing & Ads

Build authentic Reddit presence and generate qualified leads through AI-powered community engagement that provides genuine value without spam or promotion.

HTTP Request, Reddit
Marketing & Ads

This workflow runs on scheduled weekly and monthly triggers to generate unified marketing performance reports. It processes multiple websites by collecting analytics data, paid ads performance, and CR

Gmail, Google Sheets, Google Analytics +3
Marketing & Ads

Fetch Multiple Google Analytics GA4 metrics daily, post to Discord, update previous day’s entry as GA data finalizes over seven days. Automates daily traffic reporting Maintains single message per day

Google Analytics, Discord, HTTP Request
Marketing & Ads

WABA Message Journey Flow Documentation This document outlines the automated workflow for sending WhatsApp messages to contacts, triggered hourly and managed through disposition and message count logi

Supabase, HTTP Request