npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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

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 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

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 universalNameusername 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

v1.3.3 Features

  • 🔄 Consistent Username Field - Company search results now use username instead of universalName for 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 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

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 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 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-api

Quick 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/billgates

Company 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 15

Company 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 available

COMPANY_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 available

Troubleshooting

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

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