Home Blog 🔍 n8n Job Search 💼 LinkedIn Automation 📄 AI Resume Tailor 📊 Make.com Tracker 🔔 Zapier Job Alerts 🤖 AI Agents Guide 🛠️ Best AI Tools 💬 ChatGPT Tips 📊 n8n Guide 🎯 Prompts 🔗 LangChain
n8n LinkedIn ChatGPT

Automate LinkedIn Job Search & Outreach with AI

Build an 8-step n8n + ChatGPT system that researches profiles, writes hyper-personalized connection requests, sends follow-up sequences, and tracks everything in Google Sheets — automatically.

👤 Akash 📅 March 14, 2025 ⏱ 18 min read 🔄 Updated 2025

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

1

🔍 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

2

⚙️ 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

3

🤖 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:

You are an expert LinkedIn networker who writes highly personalized connection request notes. Your messages are concise, genuine, warm, and reference specific details about the recipient's work or background. You never sound like a template or a bot. Rules: - Maximum 295 characters including spaces (LinkedIn limit is 300) - Do NOT start with "Hi [Name]," — LinkedIn shows the name separately - Reference ONE specific detail from their profile (recent post, current role, company, shared interest) - State briefly who you are and why connecting makes sense - End with something light and conversational — no hard ask - Never mention you "found them on LinkedIn" — it's obvious - No emojis unless they use them in their headline

User message (dynamic):

Write a LinkedIn connection note for this person. THEIR PROFILE: Name: {{ $json.fullName }} Headline: {{ $json.headline }} Current Company: {{ $json.currentCompany }} Location: {{ $json.location }} Recent Post (if any): {{ $json.recentPosts?.[0]?.text?.slice(0, 200) || 'No recent posts' }} Mutual Connections: {{ $json.sharedConnections || 0 }} MY CONTEXT: Role: {{ $('Target Config').first().json.my_role }} Goal: {{ $('Target Config').first().json.my_goal }} Value: {{ $('Target Config').first().json.my_value_prop }} Write a single connection note under 295 characters. Output ONLY the message text, nothing else.

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

4

📋 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

5

🚀 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:

  1. Create a Phantombuster account and navigate to the Phantom Store
  2. Find and launch the LinkedIn Connection Request Sender phantom
  3. In Phantom settings, add your LinkedIn session cookie (li_at) — find this in Chrome DevTools → Application → Cookies → linkedin.com
  4. 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)
  5. 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

6

📡 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:

  1. 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)
  2. Add a Google Sheets node to read your pending outreach list (profiles where status = "sent")
  3. Add a Code node to cross-reference: find any profiles in your pending list that now appear in your connections list
  4. For each newly accepted connection, update their row status to "connected" in Google Sheets and add a connected_at timestamp
  5. 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

7

💬 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):

Write a brief LinkedIn message to send right after a connection request was accepted. THEIR PROFILE: Name: {{ $json.fullName }} Role: {{ $json.headline }} Company: {{ $json.currentCompany }} MY CONTEXT: {{ $('Target Config').first().json.my_role }} Instructions: - Under 200 characters - Warm but not over-the-top grateful - Reference something specific about their work (use their headline or company) - Optional: mention a shared interest or experience if relevant from their profile - No explicit ask — just establish the connection warmly - Natural conversational tone

Message 2 — Day 5 Value Add (use 5-day Wait node before this step):

Write a LinkedIn follow-up message that provides genuine value to this connection. THEIR PROFILE: Name: {{ $json.fullName }} Role: {{ $json.headline }} Company: {{ $json.currentCompany }} Recent Post: {{ $json.recentPosts?.[0]?.text?.slice(0, 200) || 'N/A' }} MY CONTEXT: {{ $('Target Config').first().json.my_role }} MY VALUE PROP: {{ $('Target Config').first().json.my_value_prop }} Instructions: - Under 300 characters - Share something useful: a relevant article, insight, tool, or resource they'd actually care about - Make the value directly relevant to their role or recent post topic - Conversational, not formal — like a message from a peer - No explicit ask of any kind in this message

Message 3 — Day 10 Soft Ask (use another 5-day Wait node before this step):

Write a LinkedIn message making a soft, low-pressure ask of this connection. THEIR PROFILE: Name: {{ $json.fullName }} Role: {{ $json.headline }} Company: {{ $json.currentCompany }} MY CONTEXT: {{ $('Target Config').first().json.my_role }} MY GOAL: {{ $('Target Config').first().json.my_goal }} Instructions: - Under 350 characters - The ask should be very low-friction: a 15-minute virtual coffee, a quick question about their experience, or asking if they know of relevant opportunities - Frame it around their expertise and what you could learn from them — NOT what they can do for you - Acknowledge you've been enjoying connecting first - Easy for them to decline gracefully if busy — remove all pressure

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

8

📊 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_urlLinkedIn profile URLInput / Step 1
full_nameContact's full nameStep 1 Scraper
headlineCurrent job title + companyStep 1 Scraper
companyCurrent employerStep 1 Scraper
locationGeographic locationStep 1 Scraper
connection_noteAI-generated request messageStep 3
statuspending / sent / connected / replied / meetingSteps 5-8
sent_atTimestamp of request sendStep 5
connected_atTimestamp of acceptanceStep 6
msg1_sentDay 1 message sent timestampStep 7
msg2_sentDay 5 message sent timestampStep 7
msg3_sentDay 10 message sent timestampStep 7
last_replyMost recent reply received dateStep 6 monitor
next_actionWhat to do next (auto-suggested)Step 7
notesManual 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

Frequently Asked Questions

Is LinkedIn automation against their Terms of Service? +
Technically, any form of automated access to LinkedIn violates Section 8.2 of their User Agreement. However, LinkedIn's enforcement focuses primarily on large-scale scraping and spam — accounts sending a modest volume of personalized, human-quality messages at safe rates are very rarely actioned. Millions of professionals use tools like Phantombuster and Dux-Soup daily. That said, you should read the ToS yourself and make an informed decision about your own risk tolerance. Self-hosted n8n gives you better privacy than cloud-based LinkedIn automation SaaS tools.
What connection request acceptance rate should I expect? +
With AI-personalized notes referencing specific profile details, you should realistically expect a 35-50% acceptance rate for well-targeted profiles in your industry. By contrast, blank connection requests (no note) typically achieve 15-25%. Requests to people with 500+ connections and those outside your 2nd-degree network generally have lower acceptance rates. Track your acceptance rate weekly in the Google Sheets CRM — if it drops below 25%, your targeting criteria may be off, or your messages may not be specific enough.
Can I personalize messages at real scale with this? +
Yes — that's the core value proposition. At 20 connections/day with GPT-3.5-turbo, you can generate and send 20 genuinely personalized messages for roughly $0.05-0.10 in API costs. Each message is unique, references the recipient's specific role, company, or recent post, and reads like it was written by a thoughtful human. The key is feeding the AI high-quality profile data (which is why the Apify scraper step matters so much) — garbage in, garbage out applies here just as in any AI system.
How do I measure the ROI of this outreach? +
Track these metrics in your Google Sheets CRM: Send Rate (connections sent/day), Acceptance Rate (accepted/sent), Reply Rate (replies/accepted), Meeting Rate (meetings booked/replied), and ultimately Outcome Rate (job offers, deals closed, goals achieved / meetings). For job seekers, a healthy benchmark is: 40% acceptance → 25% reply → 30% meeting conversion. At 20 sends/day, that's roughly 2-3 meaningful conversations per week from scratch — a volume that would take 3-4 hours daily to achieve manually. The time savings alone justify the setup cost within the first week.