Introduction
LinkedIn is the world's most powerful professional networking platform — but using it effectively for job searching is brutally time-consuming. Sending 20 personalized connection requests per day, following up with accepted connections, tracking who responded and who didn't, then crafting value-add messages before making an ask... it easily consumes 2-3 hours daily during an active job search.
What if you could automate the research and drafting — while keeping the human touch that makes LinkedIn outreach actually work? That's exactly what this workflow does. By combining n8n's automation engine with ChatGPT's language abilities and Apify's LinkedIn scraping, you get a system that does the heavy lifting while you focus on the conversations that matter.
This isn't about sending spam or gaming the system. It's about using AI to help you research each person thoroughly and craft a genuinely relevant message — at a scale that would otherwise be impossible manually. The result? Higher acceptance rates, more meaningful conversations, and a significantly shorter time-to-interview.
By the end of this guide, you'll have a fully automated LinkedIn outreach system that runs daily, handles quota management to stay within safe limits, and maintains a complete CRM record of every interaction. Whether you're a job seeker, a recruiter, or a B2B sales professional, this workflow will transform how you use LinkedIn.
⚡ What You'll Build
- ✅ Automated LinkedIn profile research (name, headline, company, recent posts)
- ✅ AI-personalized connection request notes (under 300 characters)
- ✅ Daily quota management to stay under LinkedIn's limits safely
- ✅ Automated follow-up sequences for accepted connections
- ✅ Response monitoring and webhook notifications
- ✅ Google Sheets CRM dashboard for tracking all interactions
Prerequisites
Before building this workflow, gather the following accounts and tools. The total monthly cost is low — most components are free or nearly free:
- n8n account: Cloud or self-hosted. The free cloud tier is sufficient for this workflow — sign up at n8n.io
- OpenAI API key: For ChatGPT message generation. Get yours at platform.openai.com. GPT-3.5-turbo is sufficient here and costs very little (~$1/month for daily use)
- LinkedIn personal account: A regular LinkedIn account — not Sales Navigator. Using your own account is important for authenticity. Avoid using a brand-new account; accounts at least 3-6 months old with a complete profile perform significantly better
- Apify account: For LinkedIn profile scraping. The free tier provides 5 Actor compute units/month — enough for moderate use. Sign up at apify.com
- Google Sheets: Free. You'll use it as both input (target profiles list) and output (CRM tracking)
- Phantombuster account (optional): Alternative to Apify for sending connection requests. Free tier includes 2 hours of execution/day — available at phantombuster.com
Total monthly cost estimate: $0-15 depending on usage volume. At 20 connections/day (the safe limit), you can build a network of 600 targeted connections per month — all with personalized, AI-crafted messages.
Understanding LinkedIn's Limits
Before automating anything on LinkedIn, you must understand the platform's limits and policies. LinkedIn actively detects unusual activity patterns, and exceeding limits can result in temporary restrictions or permanent account bans. Here's what you need to know:
Connection Request Limits
LinkedIn has tightened its connection request limits significantly over the past few years. As of 2025, the safe operating parameters are:
- Safe daily limit: 15-20 connection requests per day for accounts under 6 months old; up to 25-30 for established accounts with high SSI (Social Selling Index)
- Weekly limit: LinkedIn enforces a soft weekly cap of approximately 100 connection requests. Consistently hitting this maximum is a red flag for their detection systems
- Pending requests: If you have more than 500 pending (unanswered) connection requests outstanding, LinkedIn may temporarily restrict new requests. Withdraw old pending requests periodically
InMail vs. Connection Requests
This workflow focuses on connection requests (free) rather than InMail (premium). Connection requests with personalized notes have a meaningful acceptance rate advantage — roughly 35-45% compared to 20-25% for blank requests — and they don't require a premium subscription. InMail is better for contacting people completely outside your network when a connection request would seem out of place.
Warming Up Your Account
If your LinkedIn account is relatively new or has been inactive, warm it up gradually before running this automation:
- Week 1: 5-7 connection requests/day, manually sent
- Week 2: 10-12/day, begin mixing in automated sends
- Week 3+: Ramp to 15-20/day with full automation
During warmup, keep your profile activity high: post once or twice, like 10-15 posts, comment on 3-5 posts daily. This signals genuine human activity to LinkedIn's algorithms and establishes a baseline before automation begins.
What to Absolutely Avoid
Certain behaviors trigger LinkedIn's bot detection systems quickly. Never: send identical messages to multiple people, send more than 30 requests in a day, log in from multiple IP addresses in the same session, or run the automation 24/7 without human-like gaps. This workflow builds in all of these protections by design.
Step-by-Step Workflow Setup
Step 1: LinkedIn Profile Research Scraper
🔍 HTTP Request Node — Apify LinkedIn Scraper
Uses Apify's LinkedIn Profile Scraper actor to pull structured data from target profiles: name, headline, current company, location, mutual connections, and recent post activity. This research data feeds directly into AI message personalization.
Apify's LinkedIn Profile Scraper is a managed actor (cloud function) that handles all the scraping complexity. You call it via n8n's HTTP Request node using Apify's REST API. Here's how to set it up:
First, create a target profiles spreadsheet in Google Sheets with columns: profile_url, name, status (leave blank — workflow fills this). Add the LinkedIn profile URLs of people you want to connect with — sourced from LinkedIn search results, alumni networks, or company pages.
Add a Google Sheets node to read pending profiles (rows where status is empty), then add an HTTP Request node:
- Method: POST
- URL:
https://api.apify.com/v2/acts/curious_coder~linkedin-profile-scraper/runs - Authentication: Bearer Token → your Apify API token
- Body (JSON):
{
"profileUrls": ["{{ $json.profile_url }}"],
"proxyConfiguration": { "useApifyProxy": true },
"maxDelay": 5,
"minDelay": 2
}
The actor returns structured profile data including: fullName, headline, currentCompany, location, connectionsCount, recentPosts (array of last 3 posts with text), sharedConnections count, and skills list.
Add a Wait node set to 30 seconds after triggering the scraper — Apify runs are asynchronous, so you need to wait for the run to complete before fetching results. Then add another HTTP Request node to retrieve the run results:
GET https://api.apify.com/v2/acts/curious_coder~linkedin-profile-scraper/runs/last/dataset/items
Header: Authorization: Bearer YOUR_APIFY_TOKEN
Step 2: Target Audience Configuration
⚙️ Code Node — Define Ideal Connection Criteria
A central configuration object that defines who you're targeting and why. This informs both the LinkedIn search strategy and the AI message generator, ensuring every message is contextually relevant to your goals.
Add a Code node early in your workflow to define your targeting criteria. This serves as context that the AI uses when generating messages:
// ============================================
// TARGET AUDIENCE CONFIGURATION
// ============================================
const targetConfig = {
// Who you're looking for
target_titles: [
"Engineering Manager",
"Senior Software Engineer",
"VP of Engineering",
"Head of AI/ML",
"CTO"
],
target_companies: [
"Stripe", "Figma", "Notion", "Linear", "Vercel"
],
target_industries: [
"Software / SaaS",
"AI / Machine Learning",
"Developer Tools"
],
target_locations: ["San Francisco", "New York", "Remote"],
// About you (to help AI write relevant messages)
my_role: "Senior Software Engineer with 6 years in AI/ML",
my_goal: "exploring senior IC and EM roles at high-growth startups",
my_value_prop: "I've built production ML systems at scale and love sharing learnings",
// Connection request settings
daily_limit: 18,
include_note: true,
note_max_chars: 295 // LinkedIn max is 300, buffer for safety
};
return [{ json: targetConfig }];
Keeping your my_value_prop honest and specific is crucial. The AI uses this to craft messages that feel genuine rather than templated. If you're a job seeker, your value prop might be a recent project. If you're in sales, it could be a relevant case study or insight you can share.
Step 3: AI Message Personalization
🤖 OpenAI Node — Personalized Connection Request Writer
The core of the workflow. ChatGPT analyzes each profile's data — headline, company, recent posts, mutual connections — and writes a hyper-personalized connection note that feels human and relevant. The strict character limit forces concise, high-impact messages.
Add an OpenAI node configured as follows:
- Model: gpt-3.5-turbo (sufficient quality, very fast and cheap)
- Max Tokens: 150
- Temperature: 0.8 (higher = more varied, natural-feeling output)
System prompt:
User message (dynamic):
After generating the message, add a Code node to validate the character count and truncate gracefully if needed — GPT occasionally exceeds the limit despite instructions:
const message = $input.first().json.message.content.trim();
const limit = $('Target Config').first().json.note_max_chars;
// Truncate at word boundary if over limit
let finalMessage = message;
if (message.length > limit) {
finalMessage = message.slice(0, limit).replace(/\s\S+$/, '...');
}
return [{ json: {
...$input.first().json,
connection_note: finalMessage,
note_length: finalMessage.length
}}];
Step 4: Queue Management
📋 Code Node — Daily Quota Management
Enforces your daily connection request limit and schedules sends at human-like random intervals throughout the day. This is the safety layer that keeps your account in good standing with LinkedIn's detection systems.
Add a Code node to manage daily quotas:
const config = $('Target Config').first().json;
const allProfiles = $input.all();
// Check today's send count from Google Sheets tracking
// (You'll wire this to read the Sheets CRM node)
const todayCount = $('Today Count').first().json.sent_today || 0;
const remaining = Math.max(0, config.daily_limit - todayCount);
if (remaining === 0) {
console.log('Daily limit reached. No requests will be sent today.');
return [];
}
// Take only up to remaining quota
const batch = allProfiles.slice(0, remaining);
// Add randomized delay offsets (human-like: 3-12 minutes between sends)
return batch.map((profile, index) => ({
json: {
...profile.json,
send_delay_minutes: Math.floor(Math.random() * 9) + 3 + (index * 8),
batch_position: index + 1,
batch_total: batch.length
}
}));
The randomized delay is critical. Sending exactly one request every 5 minutes looks mechanical. Random gaps between 3 and 12 minutes, distributed across a normal working-hours window (9 AM to 5 PM), closely mimics human behavior and dramatically reduces detection risk.
Connect a Schedule Trigger at the start of this sub-workflow to run at 9 AM daily. The queue manager then staggers sends throughout the morning, all completing before noon — leaving the afternoon clear for manual engagement on LinkedIn.
Step 5: Connection Request Sending
🚀 HTTP Request Node — Send via Phantombuster
Executes the actual connection request through Phantombuster's LinkedIn Connection Request Sender phantom. Phantombuster handles the browser automation and session management, keeping your credentials secure and off n8n's servers.
Setting up Phantombuster requires a few one-time steps:
- Create a Phantombuster account and navigate to the Phantom Store
- Find and launch the LinkedIn Connection Request Sender phantom
- In Phantom settings, add your LinkedIn session cookie (
li_at) — find this in Chrome DevTools → Application → Cookies → linkedin.com - Configure the phantom with: spreadsheet URL as input source, message field mapped to your AI-generated note, daily limit set to 1 (n8n controls the batching)
- Note the Phantom ID from the URL for use in your API calls
Add an HTTP Request node in n8n to launch each send:
- Method: POST
- URL:
https://api.phantombuster.com/api/v2/agents/launch - Header — X-Phantombuster-Key: your Phantombuster API key
- Body (JSON):
{
"id": "YOUR_PHANTOM_ID",
"argument": {
"profileUrl": "{{ $json.profile_url }}",
"message": "{{ $json.connection_note }}",
"sessionCookie": "{{ $env.LINKEDIN_SESSION_COOKIE }}"
}
}
Store your LinkedIn session cookie as an n8n environment variable (LINKEDIN_SESSION_COOKIE) rather than hardcoding it. Session cookies expire after ~30-90 days, so you'll need to refresh this periodically. Set a reminder in your calendar to update it monthly.
Error handling: Add an IF node after the launch call to check the HTTP status code. If it's not 200, route to an error handler that marks the profile as "failed" in Google Sheets and sends you a Slack notification. This prevents silently-dropped requests from polluting your data.
Step 6: Response Monitoring
📡 Webhook + Polling — Monitor Accepted Connections
Detects when a connection request has been accepted so the follow-up sequence can be triggered. Uses a combination of scheduled polling and n8n webhooks for near-real-time response tracking.
LinkedIn doesn't offer a native webhook for new connections, so you need to poll for them. Add a second Schedule Trigger that runs every 4 hours and executes this monitoring sub-workflow:
- Add an HTTP Request node calling Apify's LinkedIn Connections Scraper to get your current connections list (it returns names, profile URLs, and connection dates)
- Add a Google Sheets node to read your pending outreach list (profiles where status = "sent")
- Add a Code node to cross-reference: find any profiles in your pending list that now appear in your connections list
- For each newly accepted connection, update their row status to "connected" in Google Sheets and add a
connected_attimestamp - Pass newly accepted connections to the follow-up sequence trigger
// Cross-reference accepted connections
const myConnections = $('Fetch Connections').all().map(c => c.json.profileUrl);
const pendingOutreach = $('Read Sheets').all().filter(p => p.json.status === 'sent');
const newlyAccepted = pendingOutreach.filter(p =>
myConnections.some(url => url.includes(p.json.profile_url.split('/in/')[1]))
);
return newlyAccepted.map(p => ({
json: { ...p.json, status: 'connected', connected_at: new Date().toISOString() }
}));
Step 7: AI Follow-Up Sequences
💬 OpenAI Node — 3-Touch Follow-Up Sequences
Once a connection is accepted, a timed follow-up sequence begins. Three messages go out at strategic intervals: a warm thank-you, a genuine value add, and a soft ask. All are AI-personalized using the profile data collected in Step 1.
Add three separate OpenAI nodes, each triggered by different delays using n8n's Wait node:
Message 1 — Day 1 Thank You (send immediately after acceptance):
Message 2 — Day 5 Value Add (use 5-day Wait node before this step):
Message 3 — Day 10 Soft Ask (use another 5-day Wait node before this step):
The 5-day gaps between messages are intentional. Reaching out too quickly feels automated; too slowly and they've forgotten you. The Day 1 → Day 5 → Day 10 cadence consistently outperforms both faster and slower approaches in terms of response rate.
For sending follow-up messages, use Phantombuster's LinkedIn Message Sender phantom (same setup as connections, different phantom) or the LinkedIn Sales Navigator Message Sender if you have a premium account. Call it via HTTP Request exactly as in Step 5.
Step 8: Google Sheets CRM Tracking
📊 Google Sheets Node — Full CRM Dashboard
Every interaction — sent request, accepted connection, message sent, reply received, meeting booked — gets logged to a Google Sheet with timestamps. This gives you a complete picture of your outreach pipeline and helps you optimize over time.
Create a Google Sheet named "LinkedIn Outreach CRM" with these columns:
| Column | Description | Auto-filled By |
|---|---|---|
| profile_url | LinkedIn profile URL | Input / Step 1 |
| full_name | Contact's full name | Step 1 Scraper |
| headline | Current job title + company | Step 1 Scraper |
| company | Current employer | Step 1 Scraper |
| location | Geographic location | Step 1 Scraper |
| connection_note | AI-generated request message | Step 3 |
| status | pending / sent / connected / replied / meeting | Steps 5-8 |
| sent_at | Timestamp of request send | Step 5 |
| connected_at | Timestamp of acceptance | Step 6 |
| msg1_sent | Day 1 message sent timestamp | Step 7 |
| msg2_sent | Day 5 message sent timestamp | Step 7 |
| msg3_sent | Day 10 message sent timestamp | Step 7 |
| last_reply | Most recent reply received date | Step 6 monitor |
| next_action | What to do next (auto-suggested) | Step 7 |
| notes | Manual notes (fill in yourself) | You |
In n8n, add Google Sheets nodes at each key stage to update the relevant row. Use the profile URL as the primary key to look up and update existing rows rather than appending duplicates. The "Append or Update" operation in n8n's Google Sheets node handles this elegantly — specify profile_url as the matching column.
Once you have data flowing in, build a simple dashboard tab in the same spreadsheet: use COUNTIF formulas to show your acceptance rate (connected/sent), reply rate (replied/connected), and conversion rate (meeting/sent). Reviewing this weekly tells you which types of profiles respond best to your outreach and which message templates perform strongest.
Use Cases
This workflow is highly versatile. While it's designed with job seekers in mind, the same infrastructure serves several other use cases with minor configuration changes:
Job Seeker Networking
The primary use case. Target hiring managers, senior engineers at your dream companies, and recruiters at target firms. The Day 5 value-add message is particularly powerful for job seekers — share a relevant technical article, a side project, or an industry insight that demonstrates your expertise without explicitly asking for a job. Relationships built this way often lead to referrals, which dramatically improve application success rates compared to cold applications.
B2B Sales Prospecting
Replace the job-seeker framing with your sales value proposition. Target decision-makers at companies matching your ICP (Ideal Customer Profile). The three-touch sequence translates perfectly to a sales context: awareness → value → ask. Track pipeline progression with custom status values like "qualified", "demo_scheduled", "closed_won".
Recruiter Candidate Outreach
Flip the workflow — instead of job seekers targeting employers, recruiters can use it to reach passive candidates. The AI personalization is particularly valuable here: candidates are far more likely to respond to a message referencing their specific skills and projects than a generic "I have an exciting opportunity" pitch. Use the profile data to reference specific achievements from their LinkedIn experience section.
Building Thought Leadership
Content creators and consultants can use this to grow their LinkedIn following by connecting with their target audience: potential clients, collaborators, or peers in adjacent fields. The value-add message is excellent for this use case — share your own content, not external articles, which starts building your thought leadership positioning from the first interaction.
Safety Tips
Running LinkedIn automation safely requires respecting both technical and social limits. Here are the most important practices to follow:
- Never exceed 20 connection requests per day. Even if LinkedIn's stated limit is higher, staying at 20 or below provides a safe margin and prevents the accumulation of pending requests that can trigger restrictions
- Add human-like delays between actions. Every send in this workflow includes a randomized 3-12 minute delay. Never batch sends in bursts with sub-minute gaps — this is the fastest way to trigger bot detection
- Withdraw pending requests older than 30 days. Periodically run a cleanup that withdraws connection requests that haven't been accepted within 30 days. This keeps your pending count manageable and signals human-like behavior to LinkedIn
- Keep your profile complete and active. An automation running on a sparse, inactive profile is a red flag. Post content, like and comment organically at least 3x/week
- Don't run automation from multiple IP addresses in the same day. If you use a VPS for n8n, make sure Phantombuster is also configured with a consistent residential proxy in your actual location
- Monitor your Social Selling Index (SSI). LinkedIn's SSI score at linkedin.com/sales/ssi is a proxy for account health. A score above 60 gives you more flexibility; below 40 and you should slow your automation significantly
- Never automate LinkedIn reactions, comments, or endorsements. These are much more heavily monitored than connection requests and carry a higher ban risk