Private LinkedIn Sales Navigator client with automatic cookie management
npm install linkedin-secret-saucebash
pnpm docs
Opens TypeDoc documentation in your browser
`
Installation
`bash
pnpm add linkedin-secret-sauce
or
npm install linkedin-secret-sauce
`
Requirements: Node.js >= 18 (uses native fetch)
---
Module 1: LinkedIn API
Server-side LinkedIn Sales Navigator client with automatic cookie management, retries, caching, and parsing.
$3
`typescript
import {
initializeLinkedInClient,
getProfileByVanity,
searchSalesLeads,
getCompanyById,
typeahead,
} from 'linkedin-secret-sauce';
// Initialize once at app startup
initializeLinkedInClient({
cosiallApiUrl: process.env.COSIALL_API_URL!,
cosiallApiKey: process.env.COSIALL_API_KEY!,
proxyString: process.env.LINKEDIN_PROXY_STRING, // optional
logLevel: 'info',
});
// Fetch a profile
const profile = await getProfileByVanity('john-doe');
console.log(profile.firstName, profile.lastName);
console.log(profile.positions); // Work history
// Search Sales Navigator
const results = await searchSalesLeads('cto fintech', { start: 0, count: 25 });
for (const lead of results.items) {
console.log(lead.fullName, lead.title, lead.company);
}
// Get company details
const company = await getCompanyById(1234567);
console.log(company.name, company.industry, company.employeeCount);
`
$3
| Variable | Required | Description |
|----------|----------|-------------|
| COSIALL_API_URL | Yes | Base URL of your Cosiall cookie service |
| COSIALL_API_KEY | Yes | API key for Cosiall |
| LINKEDIN_PROXY_STRING | No | Proxy in format host:port or host:port:user:pass |
| LOG_LEVEL | No | debug, info, warn, error (default: info) |
$3
`typescript
initializeLinkedInClient({
// Required
cosiallApiUrl: process.env.COSIALL_API_URL!,
cosiallApiKey: process.env.COSIALL_API_KEY!,
// Optional networking
proxyString: process.env.LINKEDIN_PROXY_STRING,
logLevel: 'info',
// Cache TTLs (milliseconds)
profileCacheTtl: 15 60 1000, // 15 minutes (default)
searchCacheTtl: 15 60 1000, // 15 minutes (default)
companyCacheTtl: 10 60 1000, // 10 minutes (default)
typeaheadCacheTtl: 60 60 1000, // 1 hour (default)
// Cookie pool & rotation
cookieRefreshInterval: 15 60 1000, // 15 minutes (default)
accountCooldownMs: 5000, // 5 seconds (default)
maxFailuresBeforeCooldown: 3, // default
// Retry behavior
maxRetries: 2, // default
retryDelayMs: 700, // default
// Request tracking
maxRequestHistory: 500, // default
// Initialization options
eagerInitialization: true, // Load cookies immediately (default)
fallbackWithoutProxyOnError: false, // Retry without proxy on 502/503/504
// Error handling
onInitializationError: (error) => {
console.error('Failed to initialize:', error);
},
// Sentry integration (optional)
sentryClient: Sentry,
});
`
$3
#### Profiles
`typescript
// By vanity URL (username)
const profile = await getProfileByVanity('john-doe');
// By URN or key
const profile = await getProfileByUrn('urn:li:fsd_profile:ACwAAAbCdEf');
const profile = await getProfileByUrn('ACwAAAbCdEf'); // Just the key works too
// Batch fetch (parallel with concurrency control)
const profiles = await getProfilesBatch(['john-doe', 'jane-smith'], 5);
`
#### Search
`typescript
// Basic keyword search
const results = await searchSalesLeads('software engineer');
// With pagination
const results = await searchSalesLeads('cto fintech', {
start: 0, // Offset
count: 25 // Results per page (max 100)
});
// Access results
results.items.forEach(lead => {
console.log(lead.fullName);
console.log(lead.title);
console.log(lead.company);
console.log(lead.linkedInUrl);
console.log(lead.fsdKey); // Use for detailed profile fetch
});
`
#### Companies
`typescript
// Resolve company name to ID
const { companyId } = await resolveCompanyUniversalName('microsoft');
// Get company details
const company = await getCompanyById(companyId);
console.log(company.name, company.website, company.employeeCount);
// From LinkedIn URL
const company = await getCompanyByUrl('https://linkedin.com/company/microsoft/');
`
#### Typeahead (Autocomplete)
`typescript
// Geographic locations
const locations = await typeahead({
type: 'BING_GEO',
query: 'new york',
count: 10
});
// Industries, titles, companies also supported
`
$3
`typescript
import { LinkedInClientError } from 'linkedin-secret-sauce';
try {
const profile = await getProfileByVanity('nonexistent');
} catch (error) {
if (error instanceof LinkedInClientError) {
switch (error.code) {
case 'NOT_FOUND':
console.log('Profile does not exist');
break;
case 'RATE_LIMITED':
console.log('Too many requests, wait before retrying');
break;
case 'AUTH_ERROR':
console.log('Cookie/session expired');
break;
}
}
}
`
$3
`typescript
import {
getSnapshot,
getRequestHistory,
getAccountsSummary
} from 'linkedin-secret-sauce';
// Performance metrics
const metrics = getSnapshot();
console.log(metrics.profileFetches);
console.log(metrics.httpSuccess);
console.log(metrics.cacheHits);
// Request log (bounded, for debugging)
const history = getRequestHistory();
// Account health status
const accounts = getAccountsSummary();
accounts.forEach(acc => {
console.log(${acc.accountId}: healthy=${acc.healthy});
});
`
---
Module 2: Email Enrichment
Find and verify business emails using multiple providers with waterfall/parallel strategies.
$3
| Provider | Type | Cost | Best For |
|----------|------|------|----------|
| LDD | Database | FREE | LinkedIn profile emails (~500M profiles) |
| SmartProspect | Database | FREE* | SmartLead's prospect database |
| Cosiall | Profile Lookup | FREE | Emails from LinkedIn profiles |
| TryKitt.ai | AI Finder | FREE** | AI-powered email finding with catch-all verification |
| Construct | Pattern + MX | FREE | Pattern guessing with MX verification |
| BounceBan | Verification | FREE/$0.003 | Catch-all specialist (85-95% accuracy) |
| Hunter | Email Finder | $0.015/email | Domain search, email finder |
| Snov.io | Email Finder | $0.02/email | Finding emails by name+domain |
*SmartProspect is FREE if you have a SmartLead subscription
**TryKitt.ai is FREE for individuals with unlimited searches
$3
The enrichment client uses an optimized 3-phase strategy:
`
PHASE 1 - Free Lookups (Parallel):
├── LDD → Real verified emails from LinkedIn Data Dump
├── SmartProspect → Real verified emails from SmartLead
├── Cosiall → Emails from LinkedIn profiles
└── TryKitt.ai → AI-powered email finding
PHASE 2 - Pattern + Verification (if Phase 1 < 80% confidence):
├── Construct → Pattern guessing + MX check
└── BounceBan → Catch-all verification (FREE single)
PHASE 3 - Paid Finders (only if Phase 2 inconclusive):
├── Hunter → $0.015/email
└── Snov.io → $0.02/email
`
$3
Lookup emails directly from LinkedIn profiles without the full enrichment pipeline:
`typescript
import { fetchProfileEmailsFromCosiall } from 'linkedin-secret-sauce';
// Lookup by ObjectURN (most precise)
const result = await fetchProfileEmailsFromCosiall({
objectUrn: 'urn:li:member:129147375'
});
// Or by LinkedIn URL
const result = await fetchProfileEmailsFromCosiall({
linkedInUrl: 'https://www.linkedin.com/in/john-doe/'
});
// Or by vanity name
const result = await fetchProfileEmailsFromCosiall({
vanity: 'john-doe'
});
console.log(result.emails); // ['john@company.com', 'john.doe@gmail.com']
`
$3
`typescript
import { createEnrichmentClient } from 'linkedin-secret-sauce';
const enrichment = createEnrichmentClient({
providers: {
// FREE providers - all run automatically in parallel
ldd: {
apiUrl: process.env.LDD_API_URL,
apiToken: process.env.LDD_API_TOKEN
},
smartprospect: {
email: process.env.SMARTLEAD_EMAIL,
password: process.env.SMARTLEAD_PASSWORD,
},
// cosiall: enabled by default (uses global Cosiall config)
// construct: enabled by default (pattern guessing)
// TryKitt.ai - FREE for individuals (AI email finder)
trykitt: {
apiKey: process.env.TRYKITT_API_KEY,
},
// BounceBan - FREE single verification, catch-all specialist
bounceban: {
apiKey: process.env.BOUNCEBAN_API_KEY,
useDeepVerify: true, // +15-25% accuracy for pattern-guessed emails
useWaterfall: true, // Recommended - includes free retries
},
// PAID providers - only used if FREE providers don't find email
hunter: { apiKey: process.env.HUNTER_API_KEY },
snovio: {
clientId: process.env.SNOVIO_CLIENT_ID,
clientSecret: process.env.SNOVIO_CLIENT_SECRET
},
},
// Optional: cost tracking callback
onCost: (provider, cost) => {
console.log(${provider} cost: $${cost});
},
});
// Find business email for a person
const result = await enrichment.enrich({
firstName: 'John',
lastName: 'Doe',
company: 'Acme Corp',
domain: 'acme.com', // Optional but improves accuracy
linkedinUrl: 'linkedin.com/in/johndoe', // For LDD lookups
});
console.log(result.business_email); // john.doe@acme.com
console.log(result.business_email_source); // 'hunter'
console.log(result.business_email_verified); // true
console.log(result.business_email_confidence); // 95
`
$3
`typescript
// Process multiple candidates efficiently
const candidates = [
{ firstName: 'John', lastName: 'Doe', domain: 'acme.com' },
{ firstName: 'Jane', lastName: 'Smith', domain: 'techcorp.io' },
// ... hundreds more
];
const results = await enrichment.enrichBatch(candidates, {
batchSize: 50, // Process 50 at a time
delayMs: 200, // Delay between batches (rate limiting)
});
results.forEach(r => {
console.log(${r.candidate.firstName}: ${r.business_email});
});
`
$3
`typescript
// Get emails from ALL providers instead of stopping at first match
const result = await enrichment.enrichAll({
firstName: 'John',
lastName: 'Doe',
domain: 'acme.com',
});
// Returns all found emails sorted by confidence
result.emails.forEach(email => {
console.log(email.email); // john.doe@acme.com
console.log(email.source); // 'hunter'
console.log(email.confidence); // 95
console.log(email.verified); // true
console.log(email.type); // 'business' | 'personal' | 'role' | 'disposable'
console.log(email.isCatchAll); // false
});
console.log(result.totalCost); // Total $ spent across providers
console.log(result.providersQueried); // ['ldd', 'smartprospect', 'construct', 'hunter']
// Batch version
const allResults = await enrichment.enrichAllBatch(candidates, {
batchSize: 50,
delayMs: 200,
});
`
$3
AI-powered email finding with enterprise identity server catch-all verification:
`typescript
import {
findEmailWithTryKitt,
verifyEmailWithTryKitt
} from 'linkedin-secret-sauce';
// Find email by name + domain
const result = await findEmailWithTryKitt(
'John Doe',
'acme.com',
{ apiKey: process.env.TRYKITT_API_KEY },
'https://linkedin.com/in/johndoe' // Optional - improves accuracy
);
if (result?.status === 'completed') {
console.log(result.result.email); // john.doe@acme.com
console.log(result.result.confidence_score); // 0.95
console.log(result.result.is_catchall); // false
}
// Verify an existing email
const verification = await verifyEmailWithTryKitt(
'john@acme.com',
{ apiKey: process.env.TRYKITT_API_KEY }
);
console.log(verification?.valid); // true
console.log(verification?.deliverable); // true
console.log(verification?.isCatchAll); // false
console.log(verification?.isDisposable); // false
`
$3
Specializes in catch-all email verification with 85-95% accuracy without sending emails:
`typescript
import {
verifyEmailWithBounceBan,
verifyEmailsBatchWithBounceBan,
checkCatchAllWithBounceBan
} from 'linkedin-secret-sauce';
// Verify a single email (FREE)
const result = await verifyEmailWithBounceBan(
'john@catchall-domain.com',
{
apiKey: process.env.BOUNCEBAN_API_KEY,
useDeepVerify: true, // Better for pattern-guessed emails
useWaterfall: true, // Recommended - includes 30-min free retry window
}
);
console.log(result.result); // 'deliverable' | 'risky' | 'undeliverable' | 'unknown'
console.log(result.score); // 0-100
console.log(result.is_accept_all); // true (catch-all domain)
console.log(result.is_disposable); // false
console.log(result.is_role); // false (not info@, support@, etc.)
console.log(result.smtp_provider); // 'google' | 'microsoft' | etc.
// Check if domain is catch-all
const isCatchAll = await checkCatchAllWithBounceBan(
'example.com',
{ apiKey: process.env.BOUNCEBAN_API_KEY }
);
// Batch verification (for >5 emails, uses bulk API at $0.003/email)
const batchResults = await verifyEmailsBatchWithBounceBan(
['john@example.com', 'jane@example.com'],
{ apiKey: process.env.BOUNCEBAN_API_KEY }
);
batchResults.forEach((result, email) => {
console.log(${email}: ${result?.result});
});
`
$3
`typescript
import {
findEmailsWithSnovio,
verifyEmailWithSnovio
} from 'linkedin-secret-sauce';
// Find emails for a person
const emails = await findEmailsWithSnovio(
'John',
'Doe',
'acme.com',
{
clientId: process.env.SNOVIO_CLIENT_ID,
clientSecret: process.env.SNOVIO_CLIENT_SECRET
}
);
emails?.emails.forEach(e => {
console.log(e.email, e.emailStatus);
});
`
$3
`typescript
import {
isValidEmailSyntax,
isPersonalEmail,
isBusinessEmail,
isDisposableEmail,
isRoleAccount,
PERSONAL_DOMAINS,
DISPOSABLE_DOMAINS,
} from 'linkedin-secret-sauce';
// Syntax validation
isValidEmailSyntax('john@example.com'); // true
isValidEmailSyntax('invalid'); // false
// Domain classification
isPersonalEmail('john@gmail.com'); // true (Gmail, Yahoo, etc.)
isBusinessEmail('john@acme.com'); // true (not personal domain)
isDisposableEmail('x@mailinator.com'); // true (temp email service)
// Role account detection (info@, support@, etc.)
isRoleAccount('info@company.com'); // true
isRoleAccount('john@company.com'); // false
// Access domain lists directly
console.log(PERSONAL_DOMAINS.has('gmail.com')); // true
console.log(DISPOSABLE_DOMAINS.size); // 500+ domains
`
$3
`typescript
const enrichment = createEnrichmentClient({
providers: { / ... / },
options: {
// Custom order - cheaper/faster providers first
providerOrder: [
'ldd', // FREE - your database
'smartprospect', // FREE - SmartLead
'cosiall', // FREE - LinkedIn profiles
'trykitt', // FREE - AI finder
'construct', // FREE - pattern matching
'bounceban', // FREE single / $0.003 bulk
'hunter', // $0.015
'snovio', // $0.02
],
// Stop after finding one verified email
stopOnFirstVerified: true,
// Minimum confidence to accept
minConfidence: 70,
// Maximum cost per email
maxCostPerEmail: 0.05,
},
});
`
$3
`typescript
const enrichment = createEnrichmentClient({
providers: { / ... / },
// Optional cache implementation
cache: {
async get(key: string) {
return redis.get(key);
},
async set(key: string, value: any, ttlMs?: number) {
await redis.set(key, value, 'PX', ttlMs || 86400000);
},
},
});
`
---
Contact Matching (LinkedIn <-> SmartProspect)
Match LinkedIn profiles to SmartProspect contacts for unified email lookup:
`typescript
import {
getEmailsForLinkedInContact,
getEmailsForLinkedInContactsBatch,
salesLeadToContact,
createLinkedInEnricher,
} from 'linkedin-secret-sauce';
// Convert a Sales Navigator lead to contact format
const contact = salesLeadToContact(lead);
// Get emails for a LinkedIn contact (recommended unified approach)
const result = await getEmailsForLinkedInContact(
{
firstName: 'John',
lastName: 'Doe',
company: 'Acme Corp',
linkedinUrl: 'https://linkedin.com/in/johndoe',
},
{
// SmartProspect config
smartprospect: {
email: process.env.SMARTLEAD_EMAIL,
password: process.env.SMARTLEAD_PASSWORD,
},
// TryKitt.ai (FREE)
trykitt: { apiKey: process.env.TRYKITT_API_KEY },
// BounceBan (FREE single)
bounceban: {
apiKey: process.env.BOUNCEBAN_API_KEY,
useDeepVerify: true,
},
}
);
console.log(result.emails); // Array of found emails with metadata
console.log(result.bestEmail); // Highest confidence business email
console.log(result.providersQueried); // Which providers were called
console.log(result.matchedContact); // SmartProspect match if found
// Batch version
const results = await getEmailsForLinkedInContactsBatch(
contacts,
config,
{ concurrency: 5 }
);
`
---
Combined Workflow Example
Here's a complete example: Search LinkedIn -> Enrich with emails:
`typescript
import {
initializeLinkedInClient,
searchSalesLeads,
getProfileByUrn,
createEnrichmentClient,
} from 'linkedin-secret-sauce';
// Initialize LinkedIn client
initializeLinkedInClient({
cosiallApiUrl: process.env.COSIALL_API_URL!,
cosiallApiKey: process.env.COSIALL_API_KEY!,
});
// Initialize enrichment client with new providers
const enrichment = createEnrichmentClient({
providers: {
construct: {},
trykitt: { apiKey: process.env.TRYKITT_API_KEY },
bounceban: { apiKey: process.env.BOUNCEBAN_API_KEY, useDeepVerify: true },
hunter: { apiKey: process.env.HUNTER_API_KEY },
},
});
async function findLeadsWithEmails(query: string, limit: number) {
// Step 1: Search LinkedIn
const searchResults = await searchSalesLeads(query, { count: limit });
const enrichedLeads = [];
for (const lead of searchResults.items) {
// Step 2: Get full profile (optional, for more data)
const profile = lead.fsdKey
? await getProfileByUrn(lead.fsdKey)
: null;
// Step 3: Find business email
const emailResult = await enrichment.enrich({
firstName: lead.firstName,
lastName: lead.lastName,
company: lead.company,
domain: profile?.company?.domain, // If available
linkedinUrl: lead.linkedInUrl,
});
enrichedLeads.push({
name: lead.fullName,
title: lead.title,
company: lead.company,
linkedIn: lead.linkedInUrl,
email: emailResult.business_email,
emailVerified: emailResult.business_email_verified,
});
}
return enrichedLeads;
}
// Usage
const leads = await findLeadsWithEmails('cto fintech san francisco', 50);
console.table(leads);
`
---
Environment Variables
Complete reference of all environment variables:
`bash
Required - LinkedIn API
COSIALL_API_URL=https://...
COSIALL_API_KEY=...
Optional - LinkedIn networking
LINKEDIN_PROXY_STRING=host:port:user:pass
LOG_LEVEL=debug|info|warn|error
Email Enrichment - FREE providers
LDD_API_URL=https://...
LDD_API_TOKEN=...
SMARTLEAD_EMAIL=...
SMARTLEAD_PASSWORD=...
TRYKITT_API_KEY=... # FREE for individuals
BOUNCEBAN_API_KEY=... # FREE single, $0.003 bulk
Email Enrichment - PAID providers
HUNTER_API_KEY=... # $0.015/email
SNOVIO_CLIENT_ID=... # $0.02/email
SNOVIO_CLIENT_SECRET=...
`
---
Development
`bash
Install dependencies
pnpm install
Build
pnpm build
Run tests
pnpm test
Type check
pnpm typecheck
Lint
pnpm lint
Generate documentation
pnpm docs
`
$3
`bash
pnpm dev:playground
or simply
pnpm dev
`
Opens a React UI at http://localhost:5173 to test the API interactively.
Playground Features:
| Tab | Features |
|-----|----------|
| LinkedIn | Sales Navigator search, profile lookup, company tools, typeahead |
| SmartLead | Contact search, email lookup, bulk operations |
| LDD | Profile lookup by username/ID |
| Cosiall | Profile email lookup (objectUrn, URL, vanity) |
| Email Tools | Validation, provider testing (side-by-side), contact matching debugger |
| System | Account management, real-time logs (SSE), metrics, cost analytics |
---
Publishing
`bash
Bump version
npm version patch # or minor/major
Publish to npm
npm publish
Push tags
git push --follow-tags
`
---
License
UNLICENSED - Private package
Support
- Issues: GitHub Issues
- Docs: Run pnpm docs` to generate TypeDoc locally