AutomationFlowsWeb Scraping › Competitor Poach - Auto Daily Limit

Competitor Poach - Auto Daily Limit

Competitor Poach - Auto Daily Limit. Uses formTrigger, httpRequest. Event-driven trigger; 21 nodes.

Event trigger★★★★☆ complexity21 nodesForm TriggerHTTP Request
Web Scraping Trigger: Event Nodes: 21 Complexity: ★★★★☆ Added:

This workflow follows the Form Trigger → 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 →

Download .json
{
  "name": "Competitor Poach - Auto Daily Limit",
  "nodes": [
    {
      "parameters": {
        "formTitle": "Competitor Post Scraper",
        "formDescription": "Scrape competitor post and auto-send connections (max 8/day, auto-continues next day)",
        "formFields": {
          "values": [
            {
              "fieldLabel": "LinkedIn Post URL",
              "placeholder": "https://www.linkedin.com/feed/update/...",
              "requiredField": true
            },
            {
              "fieldLabel": "Competitor Name",
              "placeholder": "e.g., Salesforce",
              "requiredField": true
            },
            {
              "fieldLabel": "Send Message?",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Yes"
                  },
                  {
                    "option": "No"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Message Template",
              "fieldType": "textarea",
              "placeholder": "Hi {firstName}, saw your comment. Would love to connect!"
            }
          ]
        },
        "options": {}
      },
      "id": "trigger",
      "name": "Form Trigger",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        0,
        264
      ],
      "typeVersion": 2.2
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "daily-limit",
              "name": "dailyLimit",
              "value": 10,
              "type": "number"
            },
            {
              "id": "post-url",
              "name": "postUrl",
              "value": "={{ $json['LinkedIn Post URL'] }}",
              "type": "string"
            },
            {
              "id": "competitor",
              "name": "competitor",
              "value": "={{ $json['Competitor Name'].toLowerCase() }}",
              "type": "string"
            },
            {
              "id": "send-msg",
              "name": "sendMessage",
              "value": "={{ $json['Send Message?'] === 'Yes' }}",
              "type": "boolean"
            },
            {
              "id": "template",
              "name": "template",
              "value": "={{ $json['Message Template'] || 'Hi {firstName}, saw your comment. Would love to connect!' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "set-config",
      "name": "Set Config",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        264
      ],
      "typeVersion": 3.4
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.connectsafely.ai/linkedin/posts/comments/all",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ postUrl: $json.postUrl, maxComments: 500 }) }}",
        "options": {}
      },
      "id": "get-comments",
      "name": "Get Comments",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        448,
        264
      ],
      "typeVersion": 4.2,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        },
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const config = $('Set Config').first().json;\nconst response = $input.first().json;\n\n// Handle array wrapper - API returns [{success, comments, ...}]\nconst comments = Array.isArray(response) ? response[0]?.comments : response.comments;\n\nif (!comments || comments.length === 0) {\n  return [{ json: { error: 'No comments found', profiles: [] } }];\n}\n\n// Deduplicate by profileUrl\nconst seen = new Set();\nconst profiles = [];\n\nfor (const c of comments) {\n  if (c.profileUrl && !seen.has(c.profileUrl)) {\n    seen.add(c.profileUrl);\n    profiles.push({\n      profileId: c.publicIdentifier,\n      name: c.authorName || '',\n      profileUrl: c.profileUrl,\n      competitor: config.competitor,\n      sendMessage: config.sendMessage,\n      template: config.template,\n      dailyLimit: config.dailyLimit\n    });\n  }\n}\n\nreturn profiles.map(p => ({ json: p }));"
      },
      "id": "extract-profiles",
      "name": "Extract Profiles",
      "type": "n8n-nodes-base.code",
      "position": [
        672,
        264
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "loop",
      "name": "Loop",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        896,
        264
      ],
      "typeVersion": 3
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first().json;\nconst staticData = $getWorkflowStaticData('global');\nconst today = new Date().toISOString().split('T')[0];\nconst DAILY_LIMIT = item.dailyLimit || 8;\n\n// Reset if new day\nif (staticData.date !== today) {\n  staticData.date = today;\n  staticData.count = 0;\n}\n\nconst count = staticData.count || 0;\nconst limitReached = count >= DAILY_LIMIT;\n\nreturn [{ json: { ...item, limitReached, sentToday: count, dailyLimit: DAILY_LIMIT } }];"
      },
      "id": "check-limit",
      "name": "Check Limit",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        264
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.limitReached }}",
              "rightValue": true
            }
          ]
        },
        "options": {}
      },
      "id": "if-limit",
      "name": "Limit Reached?",
      "type": "n8n-nodes-base.if",
      "position": [
        1344,
        264
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "jsCode": "// Calculate seconds until midnight\nconst now = new Date();\nconst tomorrow = new Date(now);\ntomorrow.setDate(tomorrow.getDate() + 1);\ntomorrow.setHours(0, 0, 5, 0); // 12:00:05 AM\n\nconst secondsUntilTomorrow = Math.ceil((tomorrow - now) / 1000);\n\n// Reset counter for new day\nconst staticData = $getWorkflowStaticData('global');\nconst newDate = tomorrow.toISOString().split('T')[0];\nstaticData.date = newDate;\nstaticData.count = 0;\n\nreturn [{ json: { ...$input.first().json, waitSeconds: secondsUntilTomorrow, resumeAt: tomorrow.toISOString(), limitReached: false, sentToday: 0 } }];"
      },
      "id": "wait-tomorrow",
      "name": "Wait Until Tomorrow",
      "type": "n8n-nodes-base.code",
      "position": [
        1568,
        192
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "amount": "={{ $json.waitSeconds }}"
      },
      "id": "wait-node",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        1792,
        192
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.connectsafely.ai/linkedin/profile",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ profileId: $json.profileId }) }}",
        "options": {}
      },
      "id": "get-profile",
      "name": "Get Profile",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2016,
        264
      ],
      "typeVersion": 4.2,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        },
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const profile = $input.first().json;\nconst item = $('Loop').first().json;\n\n// Skip if competitor employee\nconst company = (profile.currentCompany || profile.headline || '').toLowerCase();\nif (company.includes(item.competitor)) {\n  return [{ json: { skip: true, reason: 'competitor_employee', profileId: item.profileId, name: item.name } }];\n}\n\n// Build message if enabled\nlet message = null;\nif (item.sendMessage && item.template) {\n  const firstName = profile.firstName || item.name?.split(' ')[0] || 'there';\n  message = item.template.replace('{firstName}', firstName);\n  if (message.length > 300) message = message.slice(0, 297) + '...';\n}\n\nreturn [{ json: { skip: false, profileId: item.profileId, name: item.name, firstName: profile.firstName, message } }];"
      },
      "id": "prepare-send",
      "name": "Prepare & Filter",
      "type": "n8n-nodes-base.code",
      "position": [
        2240,
        264
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "not-skip",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.skip }}",
              "rightValue": false
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-skip",
      "name": "Should Send?",
      "type": "n8n-nodes-base.if",
      "position": [
        2464,
        264
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.connectsafely.ai/linkedin/connect",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ (() => { const b = {profileId: $json.profileId}; if($json.message) b.customMessage = $json.message; return JSON.stringify(b); })() }}",
        "options": {}
      },
      "id": "send-connection",
      "name": "Send Connection",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2688,
        192
      ],
      "typeVersion": 4.2,
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        },
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const res = $input.first().json;\nconst item = $('Loop').first().json;\nconst sent = res.success === true;\n\n// Increment daily counter if sent successfully\nif (sent) {\n  const staticData = $getWorkflowStaticData('global');\n  staticData.count = (staticData.count || 0) + 1;\n}\n\nreturn [{ json: { profileId: item.profileId, name: item.name, sent, error: res.error || null } }];"
      },
      "id": "log-increment",
      "name": "Log & Increment",
      "type": "n8n-nodes-base.code",
      "position": [
        2912,
        192
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "amount": 3
      },
      "id": "wait-3s",
      "name": "Wait 3s",
      "type": "n8n-nodes-base.wait",
      "position": [
        3152,
        400
      ],
      "typeVersion": 1.1
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "results",
        "options": {}
      },
      "id": "aggregate",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1136,
        -64
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "jsCode": "const results = $input.first().json.results || [];\nconst sent = results.filter(r => r.sent).length;\nconst skipped = results.filter(r => r.skip).length;\nconst staticData = $getWorkflowStaticData('global');\n\nreturn [{ json: {\n  status: 'completed',\n  total: results.length,\n  sent,\n  skipped,\n  failed: results.length - sent - skipped,\n  dailyCountNow: staticData.count || 0,\n  results\n} }];"
      },
      "id": "summary",
      "name": "Summary",
      "type": "n8n-nodes-base.code",
      "position": [
        1344,
        -64
      ],
      "typeVersion": 2
    },
    {
      "parameters": {
        "content": "## Competitor Poach - Auto Daily Limit\n\nAutomatically scrape LinkedIn post commenters and send connection requests with built-in daily limits and auto-scheduling.\n\n### Who is this for?\n- Sales teams targeting competitor audiences\n- Recruiters finding engaged professionals\n- Marketers building targeted networks\n\n### How it works\n1. Submit a LinkedIn post URL via the form\n2. Extracts all commenters (up to 500)\n3. Loops through each profile one-by-one\n4. Checks daily limit before each send\n5. If limit reached \u2192 waits until midnight \u2192 resumes\n6. Filters out competitor employees\n7. Sends personalized connection requests\n8. Returns summary when complete\n\n### Setup steps\n1. Add ConnectSafely.ai API credentials (Header Auth)\n2. Configure `dailyLimit` in Set Config node (default: 8)\n3. Activate the workflow\n4. Access form at: `/webhook/competitor-poach`\n\n### Customization\n- Change `dailyLimit` in Set Config node\n- Modify message template in the form\n- Adjust `Wait 3s` for different rate limiting\n\n### Requirements\n- ConnectSafely.ai account with API access\n- n8n credentials: Header Auth type",
        "height": 854,
        "width": 594,
        "color": 2
      },
      "id": "sticky-overview",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -688,
        -160
      ]
    },
    {
      "parameters": {
        "content": "## 1. Setup & Data Collection",
        "height": 262,
        "width": 702,
        "color": 5
      },
      "id": "sticky-section1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -64,
        176
      ]
    },
    {
      "parameters": {
        "content": "## 2. Processing Loop with Daily Limit",
        "height": 300,
        "width": 2350,
        "color": 7
      },
      "id": "sticky-section2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        777,
        124
      ]
    },
    {
      "parameters": {
        "content": "## 3. Results & Response",
        "height": 278,
        "width": 650,
        "color": 4
      },
      "id": "sticky-section3",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1072,
        -176
      ]
    }
  ],
  "connections": {
    "Form Trigger": {
      "main": [
        [
          {
            "node": "Set Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Config": {
      "main": [
        [
          {
            "node": "Get Comments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Comments": {
      "main": [
        [
          {
            "node": "Extract Profiles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Profiles": {
      "main": [
        [
          {
            "node": "Loop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Limit": {
      "main": [
        [
          {
            "node": "Limit Reached?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit Reached?": {
      "main": [
        [
          {
            "node": "Wait Until Tomorrow",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Until Tomorrow": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Get Profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Profile": {
      "main": [
        [
          {
            "node": "Prepare & Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare & Filter": {
      "main": [
        [
          {
            "node": "Should Send?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should Send?": {
      "main": [
        [
          {
            "node": "Send Connection",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 3s",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Connection": {
      "main": [
        [
          {
            "node": "Log & Increment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log & Increment": {
      "main": [
        [
          {
            "node": "Wait 3s",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3s": {
      "main": [
        [
          {
            "node": "Loop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summary": {
      "main": [
        []
      ]
    },
    "Loop": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check Limit",
            "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.

Pro

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

About this workflow

Competitor Poach - Auto Daily Limit. Uses formTrigger, httpRequest. Event-driven trigger; 21 nodes.

Source: https://gist.github.com/connectsafely/a5acae381fdfa159bce028cc9de2d920 — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

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

Web Scraping

This workflow allows you to import any workflow from a file or another n8n instance and map the credentials easily. A multi-form setup guides you through the entire process At the beginning you have t

Execute Command, Read Write File, HTTP Request +3
Web Scraping

[n8n] Advanced URL Parsing and Shortening Workflow - Switchy.io Integration. Uses splitInBatches, stickyNote, httpRequest, html. Event-driven trigger; 56 nodes.

HTTP Request, GitHub, Stop And Error +1
Web Scraping

[](https://youtu.be/c7yCZhmMjtI)

HTTP Request, GitHub, Stop And Error +1
Web Scraping

N8n recently introduced folders and it has been a big improvement on workflow management on top of the tags.

HTTP Request, n8n, Form Trigger +1
Web Scraping

This workflow automates the creation of press releases for music artists releasing a new single. Upload your MP3, fill in basic info, and receive a publication-ready press release saved as a Google Do

Form Trigger, HTTP Request, Google Docs