Home / Stage 6 of 6
๐Ÿ”— Stage 6

LinkedIn Outreach

For your highest-priority prospects, LinkedIn is more powerful than email. A connection request has higher acceptance rates than cold email open rates โ€” and a connected message skips the spam folder entirely.

What is this?

LinkedIn outreach means sending personalized connection requests to your Tier A and B prospects, then following up with messages once they accept. It's more personal than email, and the accept rate for a well-written connection request is 25โ€“40% โ€” meaning one in three people actively wants to hear from you.

The three-step funnel

1๏ธโƒฃ
Connection request

A short, friendly note. Not a pitch. "Hey Sarah โ€” I've been following your work at PixelCraft and wanted to connect." That's it. The note can't be more than 300 characters.

2๏ธโƒฃ
Acceptance detected

When Sarah accepts, the system detects it overnight. This is the trigger for the next step. Don't jump straight to a pitch โ€” wait.

3๏ธโƒฃ
Value message + Loom

48 hours after acceptance, send a short message with genuine value: a Loom video showing a quick idea for their website, a resource relevant to their industry, or a brief case study. Not a pitch โ€” a gift. Then let the conversation evolve naturally.

25โ€“40%
connection acceptance rate
20/day
safe connection request limit
48h
wait after acceptance before messaging
~$99/mo
Unipile API (LinkedIn automation)
LinkedIn actively fights automation. They block accounts that send too many requests or use scripted browser automation. The safe path is the Unipile API, which connects to LinkedIn through a legitimate integration rather than browser scripting.
1

Set up Unipile

Unipile is an API that lets you interact with LinkedIn programmatically โ€” send connection requests, detect new connections, read and send messages. You connect your LinkedIn account once and then control it via API. It's how enterprise sales teams automate LinkedIn without getting banned.

2

Write your connection request templates

Write 3โ€“5 different versions. Each should be under 300 characters. Mention something specific about them or their company. Never mention what you're selling. Test which versions get the highest accept rates and shift volume toward the winners.

3

Build the send queue script

The script reads your database for Tier A contacts with a LinkedIn URL that haven't been contacted yet. It sends up to 20 connection requests per day via the Unipile API, logs each one, and moves on. Runs every morning via Windows Task Scheduler โ€” no babysitting required.

4

Build the acceptance detection script

Runs every night. Calls the Unipile API to check your LinkedIn connections for new people. Any new connection that matches someone in your funnel gets their status updated to "accepted." This triggers the follow-up message queue.

5

Build the follow-up message script

Runs every morning. Finds contacts who accepted 48+ hours ago and haven't received a follow-up message yet. Sends a Loom video message or personalized value message via the Unipile API. Marks them as messaged in the database.

Loom changes everything. A personalized 2-minute Loom video โ€” showing a quick observation about their website โ€” consistently outperforms text messages. People are not used to it. It signals you actually looked at their work.
Unipile API Python / requests SQLite Windows Task Scheduler Loom

Accounts & costs

Service What it does Cost
Unipile LinkedIn API โ€” connect requests, messages, connection detection ~$99/month
Loom Quick screen recordings for personalized follow-up messages Free tier available
Claude Code Prompt โ€” LinkedIn connection sender
Build a LinkedIn connection request sender using the Unipile API. My Unipile API key is in .env as UNIPILE_API_KEY. My Unipile account_id is in .env as UNIPILE_ACCOUNT_ID. Database: outreach.db Tables: - contacts: has linkedin_url, li_found columns - li_funnel: (id, contact_id, status, request_sent_at, accepted_at, message_sent_at, template_used) Script: li_send_requests.py 1. Query contacts where li_found=1 and not yet in li_funnel table 2. Limit to 20 per run (--limit flag, default 20) 3. For each contact, pick a random template from my templates list (hardcoded) 4. Send via POST to Unipile /api/v1/users/{account_id}/connections with the linkedin profile URL and message 5. Log to li_funnel with status='requested' and request_sent_at=now() Templates (rotate randomly): - "Hi {first_name} โ€” came across {company_name} while researching {city} design studios. Impressive work." - "Hey {first_name}, {company_name}'s branding work caught my eye. Wanted to connect." - "Hi {first_name} โ€” been following some of the studios in {state} and {company_name} stood out. Let's connect." Add --dry-run flag.
Claude Code Prompt โ€” Acceptance detection + follow-up
Build two scripts for the LinkedIn follow-up funnel: Script 1: li_check_accepted.py 1. Call Unipile GET /api/v1/users/{account_id}/connections?limit=200 to get recent connections 2. For each connection, check if the LinkedIn profile URL matches anyone in li_funnel with status='requested' 3. If match found, update li_funnel: status='accepted', accepted_at=now() Script 2: li_send_followup.py 1. Find contacts in li_funnel where status='accepted' and accepted_at was 48+ hours ago and message_sent_at is null 2. Send a follow-up message via Unipile POST /api/v1/users/{account_id}/chats with: Body: "Hey {first_name} โ€” thanks for connecting. I took a quick look at {company_name}'s site and had a thought on [specific thing]. Recorded a 90-second Loom if you're curious: [LOOM_URL]. No pressure at all โ€” just wanted to share." 3. Update li_funnel: message_sent_at=now(), status='messaged' Note: Replace [LOOM_URL] with a placeholder โ€” I'll record Looms manually and update the script.

LinkedIn limits โ€” what's safe

โœ“ 20 connection requests/day. LinkedIn's unofficial safe limit for automated requests. Going above this risks a temporary restriction.
โœ“ Use Unipile, not browser scripting. Browser automation (Selenium, Playwright) is detectable and against LinkedIn's ToS. Unipile uses the mobile API, which is less restricted.
โœ— Don't use connection note for your pitch. The 300-character note is for making a human connection. If you start selling there, accept rates drop to under 10%.
โœ— Don't send a follow-up message immediately after acceptance. It reads as automated. Wait 48 hours minimum.

The full automation schedule

When all six stages are running, your Windows Task Scheduler looks like this:

โ€” Daily tasks โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
8:00am EmailSend.py --limit 50
8:15am EmailFollowup.py --limit 50
8:30am LiSendRequests.py --limit 20
9:00pm LiCheckAccepted.py
9:15pm LiSendFollowup.py
โ€” Weekly tasks โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”โ€”
Sun 11pm ListCollection.py (add new cities/searches)
Mon 2am Enrichment.py (enrich new domains)

Once this is running, your outreach machine works overnight every night. You wake up to accepted connections, email replies, and a growing database โ€” without touching any of the scripts.

You've seen the whole machine.

Every piece was built with Claude Code โ€” no developers, no design agencies, no expensive SaaS subscriptions. A niche, a list, enrichment, contacts, an explorer, an email funnel, and a LinkedIn funnel. Repeat the cycle as your niche evolves.

Outreach Machine Playbook ยท Built with Claude Code ยท Back to overview