AutomationFlowsSocial Media › Send LinkedIn Connection Requests from Google Sheets

Send LinkedIn Connection Requests from Google Sheets

Original n8n title: Send Personalized Linkedin Connection Requests with Google Sheets and Unipile

BySalman Mehboob @salmanmehboob on n8n.io

Send personalized LinkedIn connection requests automatically using n8n, Google Sheets, and Unipile — with built-in safety limits, duplicate prevention, and full invitation tracking.

Cron / scheduled trigger★★★★☆ complexity16 nodesGoogle SheetsHTTP Request
Social Media Trigger: Cron / scheduled Nodes: 16 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #14444 — 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 →

Download .json
{
  "nodes": [
    {
      "id": "09a7c560-e540-4643-ad75-d1af11dcd7ed",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1248,
        304
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "9ab7d97d-4358-4e21-9e2a-d9e70eecbea6",
      "name": "Get Leads",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1456,
        304
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1IM0qnZlhYK7Pbj__xwyPgEprW-5-P3RBnen1xGZjSSk",
          "cachedResultName": "linkedin_connection_request_template"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "406ea666-cf94-46a8-a8e4-caa3ebd4a5c9",
      "name": "Filter",
      "type": "n8n-nodes-base.filter",
      "position": [
        1648,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "d7a7f360-9796-4544-be34-834b20134833",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.connection_request_status }}",
              "rightValue": "UserInvitationSent"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "e499cc36-ed44-42d1-94c5-de8117f99ecc",
      "name": "Limit Connection Request",
      "type": "n8n-nodes-base.limit",
      "position": [
        1824,
        304
      ],
      "parameters": {
        "maxItems": 10
      },
      "typeVersion": 1
    },
    {
      "id": "29311348-574e-4c90-9496-a6deca605334",
      "name": "Get Linkedin Username",
      "type": "n8n-nodes-base.code",
      "position": [
        2080,
        304
      ],
      "parameters": {
        "jsCode": "const linkedinUrl = $input.first().json.linkedin_url;\nconst cleanUrl = linkedinUrl.replace(/\\/$/, '');\nconst username = cleanUrl.split('/in/')[1]?.split('/')[0] || null;\n\nreturn [{\n  json: {\n    username: username\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "4fb137da-fe83-4ad4-a090-7d43992257f1",
      "name": "Data Arrangement",
      "type": "n8n-nodes-base.set",
      "position": [
        2352,
        304
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "85e65f5e-2c0f-49ed-ab1c-c055ae3d6803",
              "name": "username",
              "type": "string",
              "value": "={{ $json.username }}"
            },
            {
              "id": "4010206c-e5a4-41e0-9f3b-4a42509ed118",
              "name": "row_number",
              "type": "number",
              "value": "={{ $('Filter').item.json.row_number }}"
            },
            {
              "id": "2631f30b-9886-4547-809e-be8bba4f0cba",
              "name": "first_name",
              "type": "string",
              "value": "={{ $('Filter').item.json.first_name }}"
            },
            {
              "id": "ba11efc2-09ec-465a-aa03-265a29a98408",
              "name": "connection_note",
              "type": "string",
              "value": "={{ $('Filter').item.json.connection_note }}"
            },
            {
              "id": "5bb53f3e-ee85-437d-8dc1-3d1ba5d92922",
              "name": "unipile_DSN",
              "type": "string",
              "value": "unipile-DSN"
            },
            {
              "id": "16c58cb4-3ce0-48c0-aaba-b7679f6aa30c",
              "name": "unipile_api_key",
              "type": "string",
              "value": "your-api-key-here"
            },
            {
              "id": "07d81241-ded9-4df1-ba96-cd5730f5a960",
              "name": "unipile_account_ID",
              "type": "string",
              "value": "your-unipile-linkedin-account-id-here"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "32de3e00-855b-4738-81be-8869af3000ba",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        2592,
        304
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "5074fbd6-a232-4e95-af39-b48df19384d9",
      "name": "Get Linkedin Provider ID",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2864,
        320
      ],
      "parameters": {
        "url": "=https://{{ $json.unipile_DSN }}/api/v1/users/{{ $json.username }}",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "account_id",
              "value": "={{ $json.unipile_account_ID }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-KEY",
              "value": "={{ $json.unipile_api_key }}"
            },
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2777a601-7dd8-407c-b949-24e741f180fb",
      "name": "Send Connection Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3136,
        320
      ],
      "parameters": {
        "url": "=https://{{ $('Loop Over Items').item.json.unipile_DSN }}/api/v1/users/invite",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "provider_id",
              "value": "={{ $json.provider_id }}"
            },
            {
              "name": "account_id",
              "value": "={{ $('Data Arrangement').item.json.unipile_account_ID }}"
            },
            {
              "name": "message",
              "value": "={{ $('Loop Over Items').item.json.connection_note }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-KEY",
              "value": "={{ $('Loop Over Items').item.json.unipile_api_key }}"
            },
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "634e66be-9e37-4cae-bf99-637538794a9e",
      "name": "Update Connection Request Status and ID",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3344,
        320
      ],
      "parameters": {
        "columns": {
          "value": {
            "row_number": "={{ $('Loop Over Items').item.json.row_number }}",
            "invitation_id": "={{ $json.invitation_id }}",
            "connection_request_status": "={{ $json.object }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1IM0qnZlhYK7Pbj__xwyPgEprW-5-P3RBnen1xGZjSSk",
          "cachedResultName": "linkedin_connection_request_template"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b5588ed4-5802-4b53-92ff-ba0e3206c358",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        3600,
        320
      ],
      "parameters": {
        "unit": "minutes"
      },
      "typeVersion": 1.1
    },
    {
      "id": "6da65c08-5492-42c3-8815-ccc9aab317da",
      "name": "\ud83d\udcd6 Setup Instructions",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        656,
        -320
      ],
      "parameters": {
        "color": 4,
        "width": 500,
        "height": 820,
        "content": "# \ud83d\udd17 LinkedIn Auto Connection Request\n**Automated LinkedIn outreach \u2014 personalized, safe, and trackable.**\n\n### \ud83c\udfaf What This Workflow Does:\n\u2705 Reads leads from Google Sheets\n\u2705 Filters out already-contacted leads\n\u2705 Sends personalized connection requests via LinkedIn\n\u2705 Tracks every invitation back in your sheet\n\u2705 Adds delays to avoid LinkedIn restrictions\n\n### \ud83d\udccb Setup Required:\n\n**1\ufe0f\u20e3 Google Sheet**\nMake sure your sheet has these columns:\n`first_name` | `last_name` | `linkedin_url` | `connection_note` | `connection_request_status` | `invitation_id` | `row_number`\n\n**2\ufe0f\u20e3 Unipile Account**\n- Sign up at: https://unipile.com\n- Connect your LinkedIn account\n- Get your: API Key, DSN, and Account ID\n- Fill these in the **Data Arrangement** node\n\n**3\ufe0f\u20e3 Schedule**\n- Set your preferred run frequency in the Schedule Trigger node\n- Recommended: every 4\u20138 hours for safe outreach\n\n**4\ufe0f\u20e3 Set Your Daily Limit**\n- Open the **Limit Connection Request** node\n- Set max items to 10\u201315 per run (safe range)\n\n### \ud83d\ude80 Quick Start:\n1. Complete setup steps above\n2. Activate the workflow\n3. Add leads to your Google Sheet\n4. Watch connections go out automatically!"
      },
      "typeVersion": 1
    },
    {
      "id": "a99c390c-053c-43bc-98a2-2f79f28a972f",
      "name": "\ud83d\udce5 Lead Collection Path",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1200,
        -144
      ],
      "parameters": {
        "color": 5,
        "width": 760,
        "height": 640,
        "content": "## \ud83d\udce5 LEAD COLLECTION & FILTERING\n\n**Pulls leads from Google Sheets and prepares them for outreach.**\n\n### \ud83d\udd04 Flow:\n1\ufe0f\u20e3 **Schedule Trigger** \u2014 Runs on your set schedule\n2\ufe0f\u20e3 **Get Leads** \u2014 Reads all rows from the 'leads' sheet\n3\ufe0f\u20e3 **Filter** \u2014 Keeps only leads where `connection_request_status` is empty (not yet contacted)\n4\ufe0f\u20e3 **Limit** \u2014 Caps the batch size per run\n\n### \ud83d\udca1 Key Points:\n- Leads already sent a request are automatically skipped\n- Adjust the Limit node to control daily volume\n- Recommended limit: **10\u201315 per run** to stay safe\n\n### \ud83d\udcca Sheet Columns Needed:\n`linkedin_url` | `connection_note` | `connection_request_status`"
      },
      "typeVersion": 1
    },
    {
      "id": "8e4edf7a-df26-4243-833c-ea2782328793",
      "name": "\u2699\ufe0f Data Preparation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2000,
        -224
      ],
      "parameters": {
        "color": 3,
        "width": 540,
        "height": 724,
        "content": "## \u2699\ufe0f DATA PREPARATION\n\n**Extracts the LinkedIn username and arranges all data needed for the API calls.**\n\n### \ud83d\udd04 Flow:\n1\ufe0f\u20e3 **Get LinkedIn Username** \u2014 Parses the username from the LinkedIn URL\n   - Input: `https://linkedin.com/in/john-doe/`\n   - Output: `john-doe`\n\n2\ufe0f\u20e3 **Data Arrangement** \u2014 Bundles all required fields into one clean item:\n   - `username` \u2192 for API lookup\n   - `row_number` \u2192 to update the correct sheet row later\n   - `connection_note` \u2192 personalized message per lead\n   - `unipile_DSN`, `unipile_api_key`, `unipile_account_ID` \u2192 your API credentials\n\n### \u2699\ufe0f Setup \u2014 Fill In the Data Arrangement Node:\n- `unipile-DSN` \u2192 Your Unipile DSN\n- `your-api-key-here` \u2192 Your Unipile API Key\n- `your-unipile-linkedin-account-id-here` \u2192 Your LinkedIn Account ID"
      },
      "typeVersion": 1
    },
    {
      "id": "4cd20a6e-04e1-4351-985f-67cef7dde723",
      "name": "\ud83d\udce8 Connection Request Loop",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        -112
      ],
      "parameters": {
        "color": 6,
        "width": 820,
        "height": 608,
        "content": "## \ud83d\udce8 CONNECTION REQUEST LOOP\n\n**Sends connection requests one by one with a delay between each.**\n\n### \ud83d\udd04 Flow:\n1\ufe0f\u20e3 **Loop Over Items** \u2014 Processes leads one at a time\n2\ufe0f\u20e3 **Get LinkedIn Provider ID** \u2014 Calls Unipile API to resolve the username to LinkedIn's internal `provider_id`\n3\ufe0f\u20e3 **Send Connection Request** \u2014 Posts the invite with your personalized note\n4\ufe0f\u20e3 **Update Status & ID** \u2014 Writes `UserInvitationSent` + `invitation_id` back to Google Sheets\n5\ufe0f\u20e3 **Wait** \u2014 Pauses between requests to mimic human behavior\n\ud83d\udd01 Loop repeats until all leads in the batch are processed\n\n### \u26a0\ufe0f LinkedIn Safety Tips:\n- Set Wait to **3\u20135 minutes** for safest operation\n- Keep daily total under **20 requests**\n- Use a warmed-up LinkedIn account\n- Vary your connection notes per lead"
      },
      "typeVersion": 1
    },
    {
      "id": "6a329601-d7e7-4b78-96fe-f710a8aae766",
      "name": "\u2705 Tracking & Updates",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3408,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 660,
        "content": "## \u2705 TRACKING & STATUS UPDATE\n\n**Keeps your Google Sheet up to date after every request.**\n\n### \ud83d\udcdd What Gets Written Back:\n| Column | Value |\n|---|---|\n| `connection_request_status: ` | `UserInvitationSent` |\n| `invitation_id: ` | Unique ID from Unipile |\n\n### \ud83c\udfaf Why This Matters:\n- The **Filter node** uses `connection_request_status` to skip leads on future runs\n- `invitation_id` can be used for follow-up message workflows\n- Matched by `row_number` \u2014 always updates the exact correct row\n\n### \ud83d\udca1 Build On This:\nYou can extend this workflow to send follow-up messages to accepted connections using the stored `invitation_id`."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter": {
      "main": [
        [
          {
            "node": "Limit Connection Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Leads": {
      "main": [
        [
          {
            "node": "Filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Get Linkedin Provider ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Arrangement": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Linkedin Username": {
      "main": [
        [
          {
            "node": "Data Arrangement",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Connection Request": {
      "main": [
        [
          {
            "node": "Update Connection Request Status and ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Linkedin Provider ID": {
      "main": [
        [
          {
            "node": "Send Connection Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit Connection Request": {
      "main": [
        [
          {
            "node": "Get Linkedin Username",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Connection Request Status and ID": {
      "main": [
        [
          {
            "node": "Wait",
            "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

Send personalized LinkedIn connection requests automatically using n8n, Google Sheets, and Unipile — with built-in safety limits, duplicate prevention, and full invitation tracking.

Source: https://n8n.io/workflows/14444/ — original creator credit. Request a take-down →

More Social Media workflows → · Browse all categories →

Related workflows

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

Social Media

This template is ideal for sales teams, recruiters, business development professionals, and relationship managers who need to monitor changes in their network's LinkedIn profiles. Perfect for agencies

Google Sheets, HTTP Request, Slack
Social Media

Save time - Eliminate manual LinkedIn posting and content scheduling tasks Stay consistent - Automated daily posting keeps your LinkedIn profile active and engaging Keep control - Preview every post b

HTTP Request, Telegram, Google Sheets +2
Social Media

Send Personalized DMs to LinkedIn Profile Visitors. Uses httpRequest, googleSheets. Scheduled trigger; 21 nodes.

HTTP Request, Google Sheets
Social Media

&gt; Set up n8n self-hosted instance using https://tino.vn/vps-n8n?affid=388 &gt; Use the code ==VPSN8N== for up to 39% off.

Google Drive, HTTP Request, Google Sheets
Social Media

&gt; Recommended: Self-hosted via tino.vn/vps-n8n?affid=388 — use code VPSN8N for up to 39% off.

Google Drive, Google Sheets, HTTP Request