AX/T/02 — AX/TUTORIALS
Published: May 4, 2026 · 22 min read

How to automate LinkedIn lead sourcing

Account warming, search-based scraping, AI enrichment, push to HubSpot/Pipedrive. Without bans and with real results.

AdvancedPlaywrightTypeScriptOpenAI / Anthropic APIPostgreSQLHubSpot API

LinkedIn is the hardest automation target in 2026. They have advanced behavioral detection, IP fingerprinting, per-action rate limits, and aggressive account banning. But a good pipeline built here beats every other B2B source.

This tutorial shows the long-term approach — not a script that runs once, fetches 1000 profiles, and the account lands a permanent ban in 48h. We show infrastructure for 6-12 months of stable sourcing.

WARNING: Violation of LinkedIn ToS. You do this at your own risk. Use only for public business data (consistent with hiQ vs LinkedIn precedent). Personal data, messages, profile photos = do not touch.

What you need
  • Node.js 20+ or Python 3.11+
  • Playwright experience (browser contexts, network interception)
  • Dedicated residential proxy (one IP per account, mandatory)
  • 1-3 LinkedIn accounts — preferably bought or warmed (never your main corporate one)
  • OpenAI / Anthropic API key for AI scoring
  • CRM (HubSpot, Pipedrive, or own database)
Steps
  1. 01

    Account warming — 28-day plan

    Fresh account = instant ban on automation. Warming is baseline.

    • Week 1: profile setup (photo, headline, 3-5 jobs history), 5-10 connection requests/day to real people in industry, like 10-20 posts daily
    • Week 2: 15-20 connection requests/day, comments on posts (2-3 daily), 1 own post
    • Week 3: 20-30 connections, 5+ comments, own posts 2-3/week, DMs to new connections
    • Week 4: stabilization at safe max — can start automation

    Warming is MANUAL — there is no point automating warming, that defeats the purpose. 15 min/day for 28 days.

  2. 02

    Browser fingerprint per account

    LinkedIn tracks browser fingerprint. Each account MUST have a stable, unique fingerprint throughout its lifetime. We use playwright-extra + custom fingerprint:

    // fingerprint/store.ts
    import { chromium } from 'playwright-extra';
    import stealth from 'puppeteer-extra-plugin-stealth';
    chromium.use(stealth());
    
    const accountFingerprints = {
      'account-01': {
        userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...',
        viewport: { width: 1440, height: 900 },
        locale: 'en-US',
        timezoneId: 'Europe/London',
        proxy: 'http://user:pass@proxy-uk-1.smartproxy.com:7000',
      },
      // ... per account
    };
    
    export async function launchForAccount(accountId) {
      const fp = accountFingerprints[accountId];
      const browser = await chromium.launch({
        headless: true,
        proxy: { server: fp.proxy },
      });
      const context = await browser.newContext({
        userAgent: fp.userAgent,
        viewport: fp.viewport,
        locale: fp.locale,
        timezoneId: fp.timezoneId,
        storageState: `./storage/${accountId}.json`, // persistent cookies
      });
      return { browser, context };
    }

    storageState holds session cookies — so you do not log in every time (logging in is a trigger).

  3. 03

    Search-based scraping with rate limits

    Sales Navigator search → profile list. Each action has a rate limit. Safe thresholds (2026):

    • Profile views: 80-100/day (300+ = ban warning)
    • Connection requests: 80-100/week (changed since 2023, was 100/day)
    • Search queries: 30-50/day
    • Message sends: 30-50/day to connections
    const RATE_LIMITS = {
      profileViews: { max: 80, window: '24h' },
      connectionRequests: { max: 80, window: '7d' },
      searchQueries: { max: 30, window: '24h' },
    };
    
    async function checkAndScrapeProfile(accountId, profileUrl) {
      if (!await canPerform(accountId, 'profileViews')) {
        logger.info({ accountId, action: 'skip', reason: 'rate_limit' });
        return null;
      }
      // ... actual scrape
      await recordAction(accountId, 'profileViews');
    }

    Distribute work across multiple accounts — 3 accounts × 80 profiles/day = 240/day without risk.

  4. 04

    Human-like behavior — random pauses, scroll, mouse

    A bot doing page.goto → page.locator.click → next every 2 seconds = obvious bot. We simulate a human:

    async function humanVisit(page, url) {
      await page.goto(url, { waitUntil: 'domcontentloaded' });
      await sleep(rand(1500, 3500)); // initial pause
      // Random scroll
      for (let i = 0; i < rand(2, 5); i++) {
        await page.evaluate(`window.scrollBy(0, ${rand(200, 600)})`);
        await sleep(rand(800, 2000));
      }
      // Random mouse movement
      await page.mouse.move(rand(100, 1300), rand(100, 700), { steps: rand(10, 25) });
      await sleep(rand(500, 1500));
      // Reading time on profile
      await sleep(rand(8000, 25000)); // 8-25s per profile
    }

    Per profile: 15-40 seconds (vs <2s for a naive bot). 80 profiles daily takes 30-60 min wall-clock. That is OK — automation does not have to be instant.

  5. 05

    AI scoring and CRM push

    Raw profiles = noise. Filter through LLM to keep only ICP fit:

    import Anthropic from '@anthropic-ai/sdk';
    const client = new Anthropic();
    
    async function scoreProfile(profile) {
      const response = await client.messages.create({
        model: 'claude-3-5-sonnet-latest',
        max_tokens: 256,
        messages: [{
          role: 'user',
          content: `ICP: B2B SaaS, 50-200 employees, founder/CRO/Head of Sales.
            Profile:
            ${JSON.stringify(profile)}
            
            Score 0-10 for ICP fit. Reply with JSON: {"score": N, "reason": "..."}`,
        }],
      });
      return JSON.parse(response.content[0].text);
    }

    Push to HubSpot only score >=7. HubSpot API:

    async function pushToHubSpot(profile, score) {
      await fetch('https://api.hubapi.com/crm/v3/objects/contacts', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.HUBSPOT_TOKEN}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          properties: {
            firstname: profile.firstName,
            lastname: profile.lastName,
            company: profile.company,
            jobtitle: profile.title,
            linkedin_url: profile.url,
            ax_score: score.score,
            ax_score_reason: score.reason,
            lifecyclestage: 'lead',
          }
        }),
      });
    }
What it costs to run

Run cost for 3 accounts, 240 profiles/day (7200/month):

  • Residential proxy (3 dedicated IPs, ~5GB/month): $40-60/month
  • VPS (Hetzner CX32, 4vCPU/8GB — multiple Playwright contexts need RAM): €10/month
  • Anthropic API (Claude Sonnet, ~200 tokens per profile in/out): ~$15-25/month for 7200 scorings
  • HubSpot: $0 (free tier up to 1k contacts) or plan depending
  • LinkedIn accounts: $20-50/account one-time (bought warmed) or time (28-day warming)

Total: ~$70-100/month operating + $60-150 one-time for accounts.

Common pitfalls
  • No account warming — fresh accounts banned in 24-48h. No exceptions.
  • Shared IP between accounts — instant trigger. 1 IP per account, always.
  • Logging in every time — use storageState for persistent cookies.
  • Constant 5-second pauses — bot pattern. Randomize 1.5-3.5s, 8-25s on profile.
  • No success rate monitoring — when LinkedIn starts serving "you must be a robot" pages, the scraper returns garbage not errors. Monitor success metrics.
  • GDPR violation — LinkedIn profiles = personal data. Lawful basis (Art. 6) MUST be considered. Consult a lawyer.
Build yourself or hire?

The above gives 240 ICP-scored leads/day at safe operation. Scaling to 1000+/day requires: 12+ accounts (with separate IPs), distributed queue, ML-driven action distribution (instead of hard limits, predict-driven), dedicated infrastructure per geo.

If you need this at scale without 6-month R&D — write us. We do LinkedIn sourcing for 4 clients currently, an 8-person HR agency ran 4× their normal capacity with us.

Frequently asked questions
Does LinkedIn ban accounts using automation?
Yes, aggressively. Fresh accounts get permanent ban in 24-48h. Warmed accounts with dedicated IP + human-like behavior last 6-12 months. Without warming + without IP rotation = banned immediately. This is NOT a "maybe it will work" option.
How many leads per day can I safely fetch?
Per account: 80-100 profile views + 30-50 search queries + 30-50 messages. With 3 accounts = 240 ICP-scored leads/day. Above 100 profile views per account you get "you are being too active" warnings. Above 300 — temporary restriction. Above 1000 — permanent ban.
Is LinkedIn automation compliant with ToS?
No. LinkedIn ToS forbids scraping, automation, use of unauthorized tools. The hiQ vs LinkedIn 2022 precedent confirms the right to scrape PUBLIC profile data but does not protect from account termination. You do this at your own risk.
What about GDPR when scraping LinkedIn?
LinkedIn profiles are personal data. GDPR Art. 6 requires lawful basis — most commonly "legitimate interest" (B2B sales). Required: legitimate interest assessment (LIA), opt-out mechanism, data minimization, retention policy. Consult a lawyer BEFORE launching.