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
npm install @lineai/linkedin-apiProfessional LinkedIn API client with TypeScript support, entity classes, and developer-friendly features. Implements the LinkedIn Data API from RapidAPI.
| 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 |
javascript
import LinkedInAPI, {
LOCATIONS,
INDUSTRIES,
COMPANY_SIZES,
LinkedInError,
RateLimitError,
NotFoundError
} from '@lineai/linkedin-api';
`$3
| 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
PostSearchItemAuthorData interface 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$3
- ๐ง 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$3
- ๐ฆ Package Optimization - Removed examples directory to reduce package size and improve build compatibility
- ๐ ๏ธ Build Fixes - Resolved Next.js build issues by streamlining package structure$3
- ๐งน Dependency Cleanup - Removed Prettier and related ESLint configurations to reduce package size and simplify development
- โก Streamlined Build - Cleaner development environment with fewer dependencies$3
- ๐ ๏ธ Data Transformation Fix - Fixed company search universalName โ username transformation to occur in CompanySearchResult.items getter for better data consistency
- ๐ฏ Unified API Experience - Both api.getProfile(item.username) and api.getCompany(item.username) now use the same field name$3
- ๐ Consistent Username Field - Company search results now use username instead of universalName for consistency with profile search$3
- ๐๏ธ 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$3
- ๐ง Fixed Location Parameter Types - LocationId is now correctly typed as number instead of string
- โ
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$3
- โจ 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 get methods 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 informationFeatures
- ๐ 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
`bash
npm install @lineai/linkedin-api
or
yarn add @lineai/linkedin-api
`Quick Start
`javascript
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
$3
`javascript
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/billgates
`$3
`javascript
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);
`$3
`javascript
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());
`$3
`javascript
// 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;
}
}
`$3
`javascript
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);
}
}
`$3
`typescript
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);
}
`$3
`javascript
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:
$3
`javascript
// 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 15
`$3
`javascript
// 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
});
`$3
`javascript
// 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
});
`$3
`javascript
// 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
$3
`javascript
// 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
$3
`javascript
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 |$3
#### 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 |$3
All search result classes support pagination:
| Method | Returns | Description |
|--------|---------|-------------|
|
hasNextPage() | boolean | More results available |
| getNextPage() | Promise | Fetch next page |
| items | SearchItem[] | Current page items |
| total | number | Total result count |$3
Search results return raw data objects. Use the API methods to get full details:
`javascript
// 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 |`javascript
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
$3
`javascript
LOCATIONS.US.NEW_YORK // 105080838 (number)
LOCATIONS.US.SAN_FRANCISCO // 102277331 (number)
LOCATIONS.US.LOS_ANGELES // 102448103 (number)
// ... more cities available
`$3
`javascript
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)
`$3
`javascript
INDUSTRIES.TECHNOLOGY // 96
INDUSTRIES.FINANCIAL_SERVICES // 43
INDUSTRIES.HEALTHCARE // 14
INDUSTRIES.RETAIL // 27
// ... more industries available
`Troubleshooting
$3
| 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 |
$3
| 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 |$3
`javascript
// 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);
}
`$3
LinkedIn profile searches may show high total counts but return 0 items due to private profiles. The package automatically handles this:
`javascript
// 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:
`javascript
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:
`javascript
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 |See API_IMPLEMENTATION_GUIDE.md for implementation details and patterns.
MIT