NodeJS Unofficial LinkedIn API
This `linkedin-private-api` package offers an unofficial, TypeScript-written wrapper for interacting with the LinkedIn platform programmatically. It operates by simulating browser requests, allowing automation of tasks without requiring official OAuth tokens; instead, it authenticates using direct user credentials (username and password). The current stable version is 1.1.2, with recent minor updates (v1.1.1, for example, introduced job searching and invitation notes). Its release cadence appears infrequent, suggesting a more conservative development or maintenance-oriented approach. A key differentiator is its ability to bypass official API restrictions, enabling direct automation of tasks such as searching profiles and jobs, sending connection invitations, and managing messages. This functionality is well-suited for specialized automation not covered by the public LinkedIn API, though users must be aware of the inherent risks associated with using unofficial access methods, including potential account action by LinkedIn.
Common errors
-
Login failed: Invalid credentials
cause The provided username or password was incorrect, or LinkedIn may have flagged the login attempt due to unusual activity or location.fixDouble-check credentials. Try logging in manually via a browser to ensure the account is active and not locked. Verify `process.env.USERNAME` and `process.env.PASSWORD` are correctly set. -
Cannot read properties of undefined (reading 'jobPosting') (or similar for other entity properties)
cause A scroller returned no results, or the internal structure of LinkedIn's API response for a specific entity changed, leading to incorrect parsing by the library.fixAlways check if scroller results or specific properties exist before accessing them (e.g., `if (someReactJobHit?.hitInfo?.jobPosting)`). Update the library to the latest version, or check the GitHub issues for known breaking changes if LinkedIn's API structure has been modified. -
Too many requests (HTTP 429) / You have been temporarily blocked
cause LinkedIn's rate limiting system detected excessive requests from your IP address or account within a short period.fixImplement asynchronous delays (e.g., `await new Promise(resolve => setTimeout(resolve, delayMs))`) between API calls, increasing `delayMs` gradually if errors persist. Use a proxy rotation service for high-volume operations.
Warnings
- breaking Use of this unofficial API violates LinkedIn's Terms of Service and may lead to account suspension or termination. LinkedIn frequently updates its website, which can cause this library to break without notice.
- gotcha This library performs actions through simulated browser requests. Aggressive usage (high request rates) can trigger LinkedIn's rate limiting or bot detection mechanisms, leading to temporary blocks or CAPTCHAs.
- gotcha Authentication relies on direct username/password submission, which is inherently less secure than OAuth tokens. Storing these credentials, even in environment variables, requires robust security practices to prevent leakage.
Install
-
npm install linkedin-private-api -
yarn add linkedin-private-api -
pnpm add linkedin-private-api
Imports
- Client
const Client = require('linkedin-private-api')import { Client } from 'linkedin-private-api' - Scroller
import Scroller from 'linkedin-private-api'
import { Scroller } from 'linkedin-private-api' - JobSearchHit
import { JobSearchHit } from 'linkedin-private-api'import type { JobSearchHit } from 'linkedin-private-api'
Quickstart
import { Client } from 'linkedin-private-api';
const username = process.env.USERNAME as string;
const password = process.env.PASSWORD as string;
(async () => {
// Login
const client = new Client();
await client.login.userPass({ username, password });
// Search for React development jobs in Israel
const jobsScroller = await client.search.searchJobs({
keywords: 'React',
filters: { location: 'Israel' },
limit: 20,
skip: 5,
});
const [someReactJobHit] = await jobsScroller.scrollNext();
const jobCompanyName = someReactJobHit.hitInfo.jobPosting.companyDetails.company.name;
// Fetch the job's company
const companiesScroller = await client.search.searchCompanies({ keywords: jobCompanyName });
const [{ company: jobCompany }] = await companiesScroller.scrollNext();
// Search for profiles and send an invitation
const peopleScroller = await client.search.searchPeople({
keywords: 'Bill Gates'
});
const [{ profile: billGates }] = await peopleScroller.scrollNext();
await client.invitation.sendInvitation({
profileId: billGates.profileId,
trackingId: billGates.trackingId,
});
// Search in my connections
const ownConnectionsScroller = await client.search.searchOwnConnections({ keywords: 'Bill Gates', limit: 1 });
const connections = await ownConnectionsScroller.scrollNext();
// Get conversation
const [billConversation] = await client.conversation.getConversations({
recipients: billGates.profileId
}).scrollNext();
const conversationMessages = await client.message.getMessages({
conversationId: billConversation.conversationId
}).scrollNext();
// Send a message
const sentMessage = await client.message.sendMessage({
profileId: billGates.profileId,
text: 'Hey Bill!',
});
})();