@lineai/linkedin-api
v1.3.11
Published
Professional LinkedIn API client with TypeScript support, entity classes, and developer-friendly features. Perfect for AI coders, recruiting, lead generation, market research, and content analysis. Includes comprehensive JSDoc, helper constants (LOCATIONS
Maintainers
Readme
@lineai/linkedin-api
Professional LinkedIn API client with TypeScript support, entity classes, and developer-friendly features. Implements the LinkedIn Data API from RapidAPI.
Quick Reference
| Operation | Method | Purpose | Key Parameters | Returns |
|-----------|--------|---------|----------------|---------|
| Profile Search | searchProfiles(params) | Find profiles by criteria | keywords, geo, company, minItems | ProfileSearchResult |
| Profile Get | getProfile(username) | Full profile data | username | LinkedInProfile |
| Company Search | searchCompanies(params) | Find companies by criteria | keywords, industries, companySizes, minItems | CompanySearchResult |
| Company Get | getCompany(username) | Full company data | username | LinkedInCompany |
| Post Search | searchPosts(params) | Find posts by criteria | keywords, author, datePosted, minItems | PostSearchResult |
| Post Get | getPost(postId) | Full post data | postId | LinkedInPost |
Essential Imports
import LinkedInAPI, {
LOCATIONS,
INDUSTRIES,
COMPANY_SIZES,
LinkedInError,
RateLimitError,
NotFoundError
} from '@lineai/linkedin-api';Core Entity Methods
| Entity | Key Methods | Returns |
|--------|-------------|---------|
| LinkedInProfile | getFullName(), getHeadline(), getCurrentPosition(), getLinkedInUrl() | Formatted strings/objects |
| LinkedInCompany | getName(), getFollowerCount(), getEmployeeCount(), getLinkedInUrl() | Company data |
| LinkedInPost | getText(), getLikeCount(), getCommentCount(), getAuthor() | Post metrics |
🆕 What's New in v1.3.8
- 🔍 Improved TypeScript Types - Added
PostSearchItemAuthorDatainterface for better type safety when working with post search results - 🎯 Enhanced IntelliSense - Post search author data now has proper autocompletion and type checking
- 🛠️ Type Safety Fix - Eliminates type mismatches between post search results and full post data author schemas
v1.3.7 Features
- 🔧 Next.js Build Fix - Fixed webpack compilation error by replacing process.env.NODE_ENV manipulation with instance-based debug mode
- ⚡ Better Debug Mode - Debug logging now uses instance property instead of environment variable modification
v1.3.6 Features
- 📦 Package Optimization - Removed examples directory to reduce package size and improve build compatibility
- 🛠️ Build Fixes - Resolved Next.js build issues by streamlining package structure
v1.3.5 Features
- 🧹 Dependency Cleanup - Removed Prettier and related ESLint configurations to reduce package size and simplify development
- ⚡ Streamlined Build - Cleaner development environment with fewer dependencies
v1.3.4 Features
- 🛠️ Data Transformation Fix - Fixed company search
universalName→usernametransformation to occur in CompanySearchResult.items getter for better data consistency - 🎯 Unified API Experience - Both
api.getProfile(item.username)andapi.getCompany(item.username)now use the same field name
v1.3.3 Features
- 🔄 Consistent Username Field - Company search results now use
usernameinstead ofuniversalNamefor consistency with profile search
v1.3.2 Features
- 🗑️ Simplified Search Results - Removed ProfileSearchItem, CompanySearchItem, and PostSearchItem wrapper classes for direct data access
- 🎯 Direct Data Access - Search results now return raw data objects with helper methods for cleaner, simpler usage
v1.3.1 Features
- 🔧 Fixed Location Parameter Types - LocationId is now correctly typed as
numberinstead ofstring - ✅ API Compatibility - Location constants (LOCATIONS) now properly work with LinkedIn API requirements
- 🎯 Cross-Method Consistency - Both profile search (GET) and company search (POST) handle location arrays correctly
v1.3.0 Features
- ✨ Enhanced Type Safety - Complete TypeScript response type definitions prevent runtime errors
- 🔒 Immutable Parameters - Search parameters are never mutated, ensuring predictable behavior
- 🏎️ Improved Caching - Now works for all
getmethods with 100% cache hit performance - 🛠️ Better Error Handling - More specific error types with better debugging information
- 🎯 Unified API - Consistent pagination and parameter handling across all search methods
- 🔍 Enhanced Debugging - Improved debug logs with accurate response structure information
Features
- 🚀 Easy to use - Simple, intuitive API with high-level methods
- 📘 TypeScript ready - Full type definitions included
- 🎯 Entity classes - Clean data access without dealing with raw JSON
- 🔄 Seamless data loading - Load full profiles/companies/posts from search results
- 🛡️ Safe defaults - Never worry about null/undefined values
- ⚡ Built-in caching - Reduce API calls automatically (v1.3.0+)
- 🎛️ Pagination helpers - Easy navigation through search results
- 🏷️ Helper constants - No more magic strings for locations, industries, etc.
- 🔒 Parameter immutability - Original search params never modified (v1.3.0+)
Installation
npm install @lineai/linkedin-api
# or
yarn add @lineai/linkedin-apiQuick Start
import { LinkedInAPI } from '@lineai/linkedin-api';
const api = new LinkedInAPI('your-rapidapi-key');
// Get a profile
const profile = await api.getProfile('satyanadella');
console.log(profile.getFullName()); // "Satya Nadella"
console.log(profile.getCurrentPosition()?.title); // "CEO"Usage Examples
Profile Operations
import { LinkedInAPI, LOCATIONS } from '@lineai/linkedin-api';
const api = new LinkedInAPI('your-rapidapi-key');
// Search for profiles
const searchResults = await api.searchProfiles({
keywords: 'software engineer',
geo: [LOCATIONS.US.SAN_FRANCISCO, LOCATIONS.US.NEW_YORK], // Auto-joined
company: 'Google'
});
console.log(`Found ${searchResults.total} profiles`);
// Access search results
searchResults.items.forEach(item => {
console.log(item.getFullName());
console.log(item.getHeadline());
console.log(item.getLinkedInUrl());
});
// Get full profile using username from search result
const firstResult = searchResults.items[0];
const fullProfile = await api.getProfile(firstResult.username);
// Access full profile data
console.log(fullProfile.getFullName());
console.log(fullProfile.getCurrentPosition()?.companyName);
console.log(fullProfile.getEducation()[0]?.schoolName);
console.log(fullProfile.getSkills().length);
console.log(fullProfile.getProfilePictureUrl('large'));
// Direct profile access
const profile = await api.getProfile('billgates');
console.log(profile.getLinkedInUrl()); // https://linkedin.com/in/billgatesCompany Operations
import { LinkedInAPI, COMPANY_SIZES, INDUSTRIES, LOCATIONS } from '@lineai/linkedin-api';
const api = new LinkedInAPI('your-rapidapi-key');
// Search companies
const companies = await api.searchCompanies({
keyword: 'artificial intelligence',
locations: [LOCATIONS.US.ALL],
industries: [INDUSTRIES.TECHNOLOGY],
companySizes: [COMPANY_SIZES.LARGE, COMPANY_SIZES.XLARGE], // 201-1000 employees
hasJobs: true
});
// Access company data
const company = companies.items[0];
console.log(company.getName());
console.log(company.getTagline());
// Get full company details using username from search result
const fullCompany = await api.getCompany(company.username);
console.log(fullCompany.getEmployeeCount());
console.log(fullCompany.getHeadquarters()?.city);
console.log(fullCompany.getSpecialties());
console.log(fullCompany.getFollowerCount());
console.log(fullCompany.getWebsite());
// Direct company access
const microsoft = await api.getCompany('microsoft');
console.log(microsoft.getName()); // "Microsoft"
console.log(microsoft.getEmployeeCount());
console.log(microsoft.getAllLocations().length);Post Operations
const api = new LinkedInAPI('your-rapidapi-key');
// Search posts
const posts = await api.searchPosts({
keyword: 'machine learning',
sortBy: 'date_posted',
fromCompany: [1441] // Google's company ID
});
console.log(`Found ${posts.total} posts`);
// Access post data
const post = posts.items[0];
console.log(post.getText());
console.log(post.getAuthorName());
console.log(post.getPostedAt());
// Get full post details using URN from search result
const fullPost = await api.getPost(post.urn);
console.log(fullPost.getTotalEngagement());
console.log(fullPost.getLikeCount());
console.log(fullPost.getCommentsCount());
console.log(fullPost.hasVideo());
console.log(fullPost.getArticle()?.title);
// Direct post access
const specificPost = await api.getPost('7219434359085252608');
console.log(specificPost.getAuthor().firstName);
console.log(specificPost.getTotalEngagement());Pagination
// Handle pagination easily
const results = await api.searchProfiles({ keywords: 'CEO' });
console.log(`Page 1: ${results.items.length} items`);
if (results.hasNextPage()) {
const page2 = await results.getNextPage();
console.log(`Page 2: ${page2.items.length} items`);
}
// Process all pages
let currentPage = results;
let pageNum = 1;
while (currentPage.items.length > 0) {
console.log(`Processing page ${pageNum}: ${currentPage.items.length} items`);
// Process items
for (const item of currentPage.items) {
console.log(item.getFullName());
}
if (currentPage.hasNextPage()) {
currentPage = await currentPage.getNextPage();
pageNum++;
} else {
break;
}
}Error Handling
import { LinkedInAPI, NotFoundError, RateLimitError } from '@lineai/linkedin-api';
const api = new LinkedInAPI('your-rapidapi-key');
try {
const profile = await api.getProfile('invalid-username-xyz');
} catch (error) {
if (error instanceof NotFoundError) {
console.error('Profile not found');
} else if (error instanceof RateLimitError) {
console.error(`Rate limited. Retry after: ${error.retryAfter}`);
} else {
console.error('Unexpected error:', error.message);
}
}TypeScript Support
import {
LinkedInAPI,
LinkedInProfile,
LinkedInCompany,
ProfileSearchParams,
LOCATIONS,
LocationId
} from '@lineai/linkedin-api';
const api = new LinkedInAPI('your-key');
// Full type safety
const searchParams: ProfileSearchParams = {
keywords: 'engineer',
geo: [LOCATIONS.US.SEATTLE] as LocationId[]
};
const results = await api.searchProfiles(searchParams);
// TypeScript knows all available methods
const profile: LinkedInProfile = await api.getProfile('username');
const position = profile.getCurrentPosition(); // Type: Position | null
if (position) {
console.log(position.title); // TypeScript knows all Position properties
console.log(position.companyName);
}Helper Constants
import { LOCATIONS, COMPANY_SIZES, INDUSTRIES } from '@lineai/linkedin-api';
// Use location constants instead of numeric IDs
const usProfiles = await api.searchProfiles({
geo: [LOCATIONS.US.ALL]
});
const techCompanies = await api.searchCompanies({
locations: [
LOCATIONS.US.SAN_FRANCISCO,
LOCATIONS.US.SEATTLE,
LOCATIONS.US.AUSTIN
],
industries: [INDUSTRIES.TECHNOLOGY],
companySizes: COMPANY_SIZES.ALL // All company sizes
});🆕 Smart Retry with minItems (v1.2.2)
All search methods now support intelligent retry logic to ensure you get the results you need:
Profile Search with minItems
// Get at least 15 profiles - will retry across different pages until found
const profiles = await api.searchProfiles({
keywords: 'software engineer',
geo: [LOCATIONS.US.SAN_FRANCISCO],
minItems: 15, // Minimum results required
maxRetries: 5 // Maximum retry attempts
});
console.log(`Found ${profiles.items.length} profiles`); // At least 15Company Search with minItems
// Get substantial company dataset
const companies = await api.searchCompanies({
keywords: 'fintech startup',
industries: [INDUSTRIES.FINANCIAL_SERVICES],
minItems: 20, // Keep retrying until 20+ companies found
maxRetries: 6
});Post Search with minItems
// Collect many posts for content analysis
const posts = await api.searchPosts({
keywords: 'artificial intelligence',
datePosted: 'past-week',
minItems: 25, // Need at least 25 posts
maxRetries: 8
});Disable Retry Logic
// For exact pagination control, disable retries
const exactPage = await api.searchProfiles({
keywords: 'manager',
start: '20',
minItems: 0 // Disable retries - return exact page results
});Why use minItems?
- LinkedIn has many private profiles/companies that appear in counts but don't return data
- Results can be sparse across different pages
- Perfect for data collection, analysis, or when you need substantial sample sizes
- Automatically handles pagination gaps and empty result pages
Advanced Usage
// Access raw data when needed
const profile = await api.getProfile('username');
console.log(profile.raw); // Original API response
// Use the low-level endpoint method
const response = await api.callEndpoint('get_profile_posts', {
username: 'satyanadella',
start: '0'
});
// Cache management
api.clearCache(); // Clear all cached data
api.enableCache = false; // Disable caching
// Batch operations
const usernames = ['billgates', 'satyanadella', 'sundarpichai'];
const profiles = await Promise.all(
usernames.map(username => api.getProfile(username))
);
profiles.forEach(profile => {
console.log(`${profile.getFullName()} - ${profile.getHeadline()}`);
});API Reference
Main Class: LinkedInAPI
const api = new LinkedInAPI(rapidApiKey, rapidApiHost?)| Method | Parameters | Returns | Description |
|--------|------------|---------|-------------|
| searchProfiles(params) | {keywords?, geo?, company?, start?, minItems?, maxRetries?} | ProfileSearchResult | Search LinkedIn profiles with smart retry |
| getProfile(username) | string | LinkedInProfile | Get full profile data |
| searchCompanies(params) | {keywords?, industries?, companySizes?, minItems?, maxRetries?} | CompanySearchResult | Search LinkedIn companies with smart retry |
| getCompany(username) | string | LinkedInCompany | Get full company data |
| searchPosts(params) | {keywords?, author?, datePosted?, minItems?, maxRetries?} | PostSearchResult | Search LinkedIn posts with smart retry |
| getPost(postId) | string | LinkedInPost | Get full post data |
| clearCache() | - | void | Clear all cached data |
| validateParameters(endpoint, params) | string, object | ValidationResult | Validate parameters |
Entity Classes
LinkedInProfile
| Method | Returns | Description |
|--------|---------|-------------|
| getFullName() | string | Combined first + last name |
| getHeadline() | string | Professional headline |
| getCurrentPosition() | Position \| null | Current job position |
| getAllPositions() | Position[] | All work experience |
| getEducation() | Education[] | Education history |
| getSkills() | Skill[] | Skills list |
| getLinkedInUrl() | string | Full LinkedIn profile URL |
| getLocation() | Location | Geographic location |
| getProfilePictureUrl(size?) | string | Profile picture URL |
LinkedInCompany
| Method | Returns | Description |
|--------|---------|-------------|
| getName() | string | Company name |
| getFollowerCount() | number | LinkedIn followers |
| getEmployeeCount() | number | Employee count |
| getLinkedInUrl() | string | Full LinkedIn company URL |
| getDescription() | string | Company description |
| getSpecialties() | string[] | Company specialties |
| getIndustries() | string[] | Industry categories |
| getWebsite() | string | Company website |
LinkedInPost
| Method | Returns | Description |
|--------|---------|-------------|
| getText() | string | Post content text |
| getLikeCount() | number | Number of likes |
| getCommentCount() | number | Number of comments |
| getShareCount() | number | Number of shares |
| getAuthor() | Author | Post author info |
| getPostedAt() | Date | Post publication date |
| getTotalEngagement() | number | Total engagement count |
| hasMedia() | boolean | Contains images/videos |
Search Results Classes
All search result classes support pagination:
| Method | Returns | Description |
|--------|---------|-------------|
| hasNextPage() | boolean | More results available |
| getNextPage() | Promise<SearchResult> | Fetch next page |
| items | SearchItem[] | Current page items |
| total | number | Total result count |
Search Item Data
Search results return raw data objects. Use the API methods to get full details:
// Get full data from search results
const profiles = await api.searchProfiles({ keywords: 'engineer' });
const fullProfile = await api.getProfile(profiles.items[0].username);
const companies = await api.searchCompanies({ keyword: 'tech' });
const fullCompany = await api.getCompany(companies.items[0].username);
const posts = await api.searchPosts({ keyword: 'AI' });
const fullPost = await api.getPost(posts.items[0].urn);Error Types
| Error | When Thrown | Properties | Example Handling |
|-------|-------------|------------|------------------|
| LinkedInError | Base error class | message, code | Generic error handling |
| RateLimitError | Rate limit exceeded | retryAfter (Date) | Wait until retry time |
| NotFoundError | Resource not found/private | message | Handle missing data |
| NetworkError | Connection issues | originalError | Retry logic |
try {
const profile = await api.getProfile('username');
} catch (error) {
if (error instanceof RateLimitError) {
console.log(`Retry after: ${error.retryAfter}`);
} else if (error instanceof NotFoundError) {
console.log('Profile not found or private');
}
}Helper Constants
LOCATIONS
LOCATIONS.US.NEW_YORK // 105080838 (number)
LOCATIONS.US.SAN_FRANCISCO // 102277331 (number)
LOCATIONS.US.LOS_ANGELES // 102448103 (number)
// ... more cities availableCOMPANY_SIZES
COMPANY_SIZES.SELF_EMPLOYED // 'A' (Self-employed)
COMPANY_SIZES.TINY // 'B' (1-10 employees)
COMPANY_SIZES.SMALL // 'C' (11-50 employees)
COMPANY_SIZES.MEDIUM // 'D' (51-200 employees)
COMPANY_SIZES.LARGE // 'E' (201-500 employees)
COMPANY_SIZES.XLARGE // 'F' (501-1000 employees)
COMPANY_SIZES.ENTERPRISE // 'G' (1001-5000 employees)
COMPANY_SIZES.MASSIVE // 'H' (5001-10000 employees)
COMPANY_SIZES.MEGA // 'I' (10000+ employees)INDUSTRIES
INDUSTRIES.TECHNOLOGY // 96
INDUSTRIES.FINANCIAL_SERVICES // 43
INDUSTRIES.HEALTHCARE // 14
INDUSTRIES.RETAIL // 27
// ... more industries availableTroubleshooting
Common Issues & Solutions
| Problem | Cause | Solution | |---------|-------|----------| | "Invalid username" | Username format incorrect | Use LinkedIn username (from URL) not display name | | "Rate limit exceeded" | Too many requests | Implement delays, use caching, handle RateLimitError | | "Profile not found" | Private profile or invalid username | Handle NotFoundError gracefully | | Empty search results | Overly specific criteria | Broaden search parameters | | Profile search returns 0 items | Private profiles in result set | Enable auto-retry (default) or use debug mode | | Slow performance | Not using caching | Enable caching (default) or clear periodically | | Memory issues | Large dataset processing | Process in batches, clear cache regularly |
Best Practices
| Practice | Why | Example |
|----------|-----|---------|
| Use helper constants | Avoid magic strings | geo: [LOCATIONS.US.NYC] not geo: ['105080838'] |
| Handle errors specifically | Better error recovery | Check for RateLimitError, NotFoundError |
| Use entity methods | Safe data access | profile.getFullName() not profile.data.firstName |
| Implement delays | Avoid rate limits | await new Promise(r => setTimeout(r, 1000)) |
| Load full data smartly | Performance optimization | Search first, then load full data only when needed |
Parameter Validation
// Validate before making requests
const validation = api.validateParameters('search_people', {
keywords: 'engineer'
});
if (!validation.valid) {
console.log('Missing:', validation.missingRequired);
console.log('Extra:', validation.extraParams);
}Profile Search & Private Profiles
LinkedIn profile searches may show high total counts but return 0 items due to private profiles. The package automatically handles this:
// Automatic retry (default behavior)
const results = await api.searchProfiles({ keywords: 'engineer' });
// Will try different start positions automatically
// Debug what's happening
api.setDebugMode(true);
const debugResults = await api.searchProfiles({ keywords: 'ceo' });
// Logs: "Profile search attempt 1, start=0: itemsCount=0"
// "Profile search attempt 2, start=10: itemsCount=2" ✅
// Manual control
const exactResults = await api.searchProfiles({
keywords: 'manager',
autoRetryForPrivateProfiles: false, // Get exact page
start: '20'
});Configuration
The API client supports several configuration options:
const api = new LinkedInAPI('your-key');
// Disable caching
api.enableCache = false;
// Adjust cache timeout (default: 5 minutes)
api.cacheTimeout = 10 * 60 * 1000; // 10 minutes
// Clear cache
api.clearCache();
// Enable debug mode (helpful for troubleshooting)
api.setDebugMode(true);Rate Limiting
The API includes rate limit information in responses. Handle rate limits gracefully:
try {
const profile = await api.getProfile('username');
} catch (error) {
if (error instanceof RateLimitError) {
console.log(`Rate limited until: ${error.retryAfter}`);
// Implement retry logic
}
}Additional Resources
- AI_USAGE_GUIDE.md - Comprehensive guide optimized for AI coders with advanced patterns
- API_IMPLEMENTATION_GUIDE.md - Implementation details and development patterns
- LinkedIn Data API Documentation - Original RapidAPI documentation
Use Cases
| Scenario | Methods Needed | Pattern |
|----------|----------------|---------|
| Recruiting | searchProfiles(), getProfile() | Search → Filter → Load Full Data |
| Lead Generation | searchCompanies(), getCompany() | Search by Industry → Get Details |
| Market Research | searchCompanies(), searchProfiles() | Industry Analysis + Employee Insights |
| Content Analysis | searchPosts(), getPost() | Keyword Search → Engagement Analysis |
| Competitive Intelligence | getCompany(), searchProfiles() | Company Data + Employee Profiles |
Contributing
See API_IMPLEMENTATION_GUIDE.md for implementation details and patterns.
License
MIT
