Research synthesized from 10 parallel agents covering library landscape, Aimfox internals, anti-detection, session management, official APIs, unibox patterns, open-source starting points, pricing, and deploy infra.
There is no maintained "Telethon for LinkedIn." The canonical Voyager wrapper — tomquirk/linkedin-api — was pulled private in 2024-25; the best Node equivalent has been abandoned since 2021; the entire modern outreach industry (HeyReach, Lemlist, Salesflow…) quietly runs on Unipile, a paid REST API at ~$5/account/mo.
We're not going that route. Decision locked: self-host Voyager via the nsandman fork + StaffSpy + linkedin-messaging, wrap behind a single LinkedInProvider adapter so we can swap to Unipile as a hot-swap escape hatch if Voyager drift gets unmanageable.
Architecture: Aimfox/Expandi-class pure cloud — server worker per account, dedicated ISP residential proxy per account, Chrome extension exists only to harvest the user's li_at cookie + fingerprint snapshot at link time. ICP: solo-first at $49/seat (Aimfox/Dripify lane). Agency, multichannel, white-label features deferred to v2.
| # | Component | Purpose | Stack |
|---|---|---|---|
| 1 | extension/ | One-job MV3: read li_at + JSESSIONID + x-li-track + UA, POST to backend over TLS at link time. No DOM automation. | Vanilla JS, MV3 |
| 2 | backend/providers/linkedin.py | LinkedInProvider interface: login, send_invite, withdraw_invite, send_message, fetch_inbox, fetch_conversation, get_profile, search_people. | nsandman + StaffSpy + linkedin-messaging |
| 3 | backend/workers/account_worker.py | One process per LinkedIn account. Owns session, asserts proxy, enforces rate limits. Concurrency=1 per account. | Python asyncio |
| 4 | backend/scheduler.py | Per-account jittered cadence, ramp curve, weekly caps, message uniqueness gate. Auto-throttle on negative signals. | Arq + Redis |
| 5 | backend/realtime.py | SSE long-poll on /realtime/connect per account → normalize → write conversations/messages → publish to UI WebSocket. | httpx-sse |
| 6 | backend/health.py | Probes /voyager/api/me every 15min. State machine HEALTHY→WARMING→THROTTLED→CHALLENGED→BANNED. | asyncio |
| 7 | backend/proxies.py | Provisions and binds one ISP residential IP per account at link time. Country-matched to account location. | IPRoyal API |
| 8 | backend/db/ | workspaces · users · linkedin_accounts (cookies encrypted) · campaigns · steps · leads · campaign_leads · conversations · messages · lead_events · notes | Postgres 16 + SQLAlchemy 2 |
| 9 | frontend/ | Three surfaces: Accounts (link/health), Campaigns (sequences), Unibox (cross-account inbox + lead sidebar + AI draft). | Next.js 15 + shadcn/ui |
| Library | Lang | Approach | Status | Verdict |
|---|---|---|---|---|
| nsandman/linkedin-api | Python | Voyager HTTP fork | 🟢 active | Our pick — modern successor to tomquirk. |
| StaffSpy | Python | Voyager + Playwright | 🟢 active | Pair for people search + CAPTCHA fallback. |
| linkedin-messaging | Python | Voyager messaging only | 🟡 stale but stable | Battle-tested in mautrix-linkedin bridge. Use for inbox. |
| tomquirk/linkedin-api | Python | Voyager HTTP | 🔴 repo private | PyPI v2.3.1 still installable, but you can't PR or inspect. Don't depend. |
| eilonmore/linkedin-private-api | Node/TS | Voyager HTTP | 🔴 abandoned 2021 | 4 years stale. Endpoint drift guaranteed. |
| Unipile | Hosted | REST SaaS | 🟢 production-grade | Escape-hatch behind Provider interface. ~$5/account/mo if Voyager drift becomes unmanageable. |
| joeyism/linkedin_scraper | Python | Playwright | 🟢 active 4.1k★ | Scrape-only, no messaging. Not useful for outreach. |
| linkedin-developers/* (official) | Py/TS | OAuth REST | official | No connection, no DM, no search. Useless for outreach. Industry-wide. |
| Tool | Architecture | Entry $/mo | Segment | Our angle |
|---|---|---|---|---|
| Aimfox | Cloud + cookie-handoff ext + dedicated proxy | $49/seat | Solo | Direct lane match — undercut on AI inbox + Slack pings. |
| Heyreach | Cloud, shared infra (caught a 2026 ban wave) | $79 → $999 bundle | Agency | Their bundle assumes 50 seats; underserved 5-20 seat agencies = v2 white-space. |
| Expandi | Cloud, dedicated IP, premium safety rep | $99/account | Premium solo | Beat on price; match on safety. |
| Dripify | Cloud, simple UX | $39 annual | Solo | Beat on AI + multichannel. |
| Waalaxy | Hybrid free-extension + cloud | €25 Pro | EU solo | Detection risk; not direct competition. |
| Linked Helper | Desktop Electron app | $15-45 | Bargain | Different model (must be running). |
| Lemlist | Cloud, email-first, LI side-feature | $79 → $109 | Multichannel | v2 target with native email. |
| Salesflow / Skylead / Zopto | Cloud + dedicated IP, managed-service flavored | $99 → $297/seat | Enterprise | Out of scope v1. |
workspaces(id, name, plan)
users(id, workspace_id, email, role)
linkedin_accounts(id, workspace_id, owner_user_id, li_member_urn,
li_at_enc, jsessionid_enc, x_li_track, user_agent,
proxy_id, status, daily_limits jsonb, last_seen_at)
campaigns(id, workspace_id, linkedin_account_id, name, goal, status,
settings jsonb, created_at)
steps(id, campaign_id, position, type, -- invite | message | wait | condition
template_text, delay_hours, branch_on jsonb)
leads(id, workspace_id, li_urn, full_name, headline, company,
status, sub_status, owner_user_id, enriched jsonb,
first_touched_at, last_activity_at)
campaign_leads(campaign_id, lead_id, current_step_id, state, next_run_at,
PRIMARY KEY(campaign_id, lead_id))
conversations(id, workspace_id, lead_id, linkedin_account_id, channel,
li_conversation_urn, assigned_user_id, last_message_at,
unread_count, sentiment, tags text[])
messages(id, conversation_id, li_event_urn UNIQUE, direction,
sender_urn, body, attachments jsonb, sent_at, delivered_at,
read_at, is_ai_draft bool)
lead_events(id, lead_id, type, payload jsonb, occurred_at) -- append-only
notes(id, lead_id, author_user_id, body, mentions uuid[], created_at)
proxies(id, provider, ip, country, assigned_to_account_id, status)
Every outreach tool — Aimfox, Dripify, Expandi, Waalaxy, Heyreach, Lemlist — runs on Voyager calls that violate LinkedIn's ToS §8.2. There is no official endpoint at any price for connection requests or member-to-member DMs. The 9th Circuit's hiQ ruling protects scraping logged-out public pages, not logged-in automation.
Positioning: "LinkedIn co-pilot that acts on the user's behalf within their own session" — never "LinkedIn API integration", never "LinkedIn-partnered". ToS should state: (i) user authorizes software to act in their session, (ii) user owns compliance with LinkedIn ToS, (iii) no representation that automation is permitted, (iv) liability disclaimer for restrictions.
Diversification: roadmap to native email + X by year-end so a HeyReach-style ban event isn't an extinction risk.