UNPKG

@lineai/linkedin-api

Version:

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

1,936 lines (1,809 loc) 80.7 kB
import axios from "axios"; /** * LinkedIn Data API - Real Endpoints Documentation * Base URL: https://linkedin-data-api.p.rapidapi.com * * Headers Required: * - X-RapidAPI-Key: Your RapidAPI key * - X-RapidAPI-Host: linkedin-data-api.p.rapidapi.com */ export const ENDPOINTS = { // BASE URL: https://linkedin-data-api.p.rapidapi.com // PROFILE APIS - Complete documentation based on actual RapidAPI documentation get_profile_data: { path: "/", method: "GET", description: "Get comprehensive LinkedIn profile data", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, // get_profile_data_by_url: { // path: "/get-profile-data-by-url", // method: "GET", // description: "Get profile data using LinkedIn profile URL", // parameters: { // required: ["url"], // optional: [], // }, // example_params: { // url: "https://www.linkedin.com/in/jaywestlin/", // }, // }, search_people: { path: "/search-people", method: "GET", description: "Search for LinkedIn profiles with various filters", parameters: { required: [], optional: [ "keywords", "start", "geo", "schoolId", "firstName", "lastName", "keywordSchool", "keywordTitle", "company", ], }, example_params: { keywords: "max", start: "0", geo: "103644278,101165590", schoolId: "", firstName: "", lastName: "", keywordSchool: "", keywordTitle: "", company: "", }, notes: { start: "Could be one of these: 0, 10, 20, 30, etc.", geo: "Follow the location ID guide link in the documentation", company: "Company name", }, }, search_people_by_url: { path: "/search-people-by-url", method: "POST", description: "Search people using LinkedIn search URL parameters", parameters: { required: [], optional: [], }, body_params: { required: ["url"], optional: [], }, example_body: { url: "https://www.linkedin.com/search/results/people/?currentCompany=%5B%221035%22%5D&geoUrn=%5B%22103644278%22%5D&keywords=max&origin=FACETED_SEARCH&sid=%3AB5", }, content_type: "application/json", }, get_profile_recent_activity_time: { path: "/get-profile-recent-activity-time", method: "GET", description: "Get recent activity timestamp for a profile", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, get_profile_posts: { path: "/get-profile-posts", method: "GET", description: "Get posts from a LinkedIn profile", parameters: { required: ["username"], optional: ["start", "paginationToken", "postedAt"], }, example_params: { username: "jaywestlin", start: "", // "use this param to get posts in next results page: 0 for page 1, 50 for page 2 100 for page 3, etc." paginationToken: "", // "required when fetching the next results page. The token from the previous call must be used." postedAt: "", // "filters posts after fetching them from LinkedIn and returns posts that are newer than the given date. Example value: 2024-01-01 00:00" }, }, get_profile_post_and_comments: { path: "/get-profile-post-and-comments", method: "GET", description: "Get specific post and its comments", parameters: { required: ["urn"], optional: [], }, example_params: { urn: "7181285108586211378", }, }, get_profile_post_comment: { path: "/get-profile-posts-comments", method: "GET", description: "Get comments for a specific post", parameters: { required: ["urn", "sort"], optional: ["page", "paginationToken"], }, example_params: { urn: "7169084130104737792", sort: "mostRelevant", // mostRelevant, mostRecent page: "1", paginationToken: "", // required when fetching next results page }, }, get_profiles_comments: { path: "/get-profiles-comments", method: "GET", description: "Get all comments made by a profile", parameters: { required: ["username"], // optional: ["start", "count"], }, example_params: { username: "jaywestlin", // start: "0", // count: "10", }, }, get_profile_connection_and_follower_count: { path: "/get-profile-connection-and-follower-count", method: "GET", description: "Get profile connection and follower counts", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, get_profile_data_and_connection_and_follower_count: { path: "/data-connection-count", method: "GET", description: "Get profile data along with connection and follower counts", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, get_received_recommendations: { path: "/get-received-recommendations", method: "GET", description: "Get recommendations received by a profile", parameters: { required: ["username"], optional: ["start"], }, example_params: { username: "jaywestlin", start: "0", }, }, get_given_recommendations: { path: "/get-given-recommendations", method: "GET", description: "Get recommendations given by a profile", parameters: { required: ["username"], optional: ["start"], }, example_params: { username: "jaywestlin", start: "0", }, }, get_profile_likes: { path: "/get-profile-likes", method: "GET", description: "Get reactions on profile posts", parameters: { required: ["username"], optional: ["start", "paginationToken"], }, example_params: { username: "jaywestlin", start: "", paginationToken: "", }, }, about_this_profile: { path: "/about-this-profile", method: "GET", description: "Get profile about section", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, get_profile_data_connection_count_posts: { path: "/profile-data-connection-count-posts", method: "GET", description: "Get comprehensive profile data with connections and posts", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, profile_data_and_recommendations: { path: "/all-profile-data", method: "GET", description: "Get profile data with recommendations", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, get_similar_profiles: { path: "/similar-profiles", method: "GET", description: "Get profiles similar to the specified profile", parameters: { required: ["url"], optional: [], }, example_params: { url: "https://www.linkedin.com/in/jaywestlin/", }, }, // get_profile_positions_with_skills: { // path: "/get-profile-positions-with-skills", // method: "GET", // description: "Get profile work positions with associated skills", // parameters: { // required: ["username"], // optional: [], // }, // example_params: { // username: "jaywestlin", // }, // }, get_profile_top_position: { path: "/profile/positions/top", method: "GET", description: "Get the top/current position of a profile", parameters: { required: ["username"], optional: [], }, example_params: { username: "jaywestlin", }, }, // get_profile_company_interest: { // path: "/profiles/interests/companies", // method: "POST", // description: "Get companies a profile is interested in", // parameters: { // required: [], // optional: [], // }, // body_params: { // required: ["username", "page"], // optional: [], // }, // example_body: { // username: "jaywestlin", // page: 1, // }, // content_type: "application/json", // }, // get_profile_top_voice_interests: { // path: "/profiles/interests/top-voices", // method: "POST", // description: "Get top voice interests for a profile", // parameters: { // required: [], // optional: [], // }, // body_params: { // required: ["username", "page"], // optional: [], // }, // example_body: { // username: "jaywestlin", // page: 1, // }, // content_type: "application/json", // }, get_profile_group_interests: { path: "/profiles/interests/groups", method: "POST", description: "Get group interests for a profile", parameters: { required: [], optional: [], }, body_params: { required: ["username", "page"], optional: [], }, example_body: { username: "jaywestlin", page: 1, }, content_type: "application/json", }, get_profile_school_interests: { path: "/profiles/interests/schools", method: "POST", description: "Get school interests for a profile", parameters: { required: [], optional: [], }, body_params: { required: ["username", "page"], optional: [], }, example_body: { username: "jaywestlin", page: 1, }, content_type: "application/json", }, get_profile_newsletter_interests: { path: "/profiles/interests/newsletters", method: "POST", description: "Get newsletter interests for a profile", parameters: { required: [], optional: [], }, body_params: { required: ["username", "page"], optional: [], }, example_body: { username: "jaywestlin", page: 1, }, content_type: "application/json", }, get_company_details: { path: "/get-company-details", method: "GET", description: "Get detailed information about a LinkedIn company", parameters: { required: ["username"], optional: [], }, example_params: { username: "google", // include_employees: true, // include_posts: false, // include_jobs: true, }, }, get_company_details_by_id: { path: "/get-company-details-by-id", method: "GET", description: "Get company details using LinkedIn company ID", parameters: { required: ["id"], optional: [], }, example_params: { company_id: "1441", // include_insights: true, // include_updates: false, }, }, search_companies: { path: "/companies/search", method: "POST", description: "Search for LinkedIn companies with filters", parameters: { required: ["keyword"], optional: ["locations", "industries", "companySizes", "hasJobs", "page"], }, body_params: { required: [], optional: [ "keyword", "locations", "companySizes", "hasJobs", "industries", "page", ], }, example_params: { keyword: "", locations: [103644278], industries: [96, 4], companySizes: ["D", "E", "F", "G"], hasJobs: true, page: 1, }, notes: `Possible company sizes B: 1-10 employees C: 11-50 employees D: 51-200 employees E: 201-500 employees F: 501-1000 employees G: 1001-5000 employees H: 5001-10,000 employees I: 10,001+ employees`, }, get_company_jobs: { path: "/company-jobs", method: "POST", description: "Get job postings from a specific company", body_params: { required: ["companyIds"], optional: ["page", "sort"], }, example_params: { companyIds: [5383240, 2382910], page: 1, sort: "mostRecent", }, }, get_company_by_domain: { path: "/get-company-by-domain", method: "GET", description: "Get company information using company domain/website", parameters: { required: ["domain"], }, example_params: { domain: "apple.com", }, }, // get_company_insights: { // path: "/get-company-insights", // method: "GET", // description: "Get premium company insights and analytics (premium feature)", // parameters: { // required: ["username"], // }, // example_params: { // username: "amazon", // }, // }, get_company_employees_count: { path: "/get-company-employees-count", method: "POST", description: "Get employee count and statistics for a company", parameters: { required: ["companyId"], optional: ["locations"], }, example_params: { companyId: "1441", // Google company ID locations: [], // Optional array of location IDs to filter employees by location }, }, get_company_jobs_count: { path: "/get-company-jobs-count", method: "GET", description: "Get the total number of active job postings for a company", parameters: { required: ["companyId"], }, example_params: { companyId: "1441", // Google company ID }, }, get_company_pages_people_also_viewed: { path: "/get-company-pages-people-also-viewed", method: "GET", description: "Get companies that people also viewed when looking at this company", parameters: { required: ["username"], }, example_params: { username: "google", }, }, get_company_posts: { path: "/get-company-posts", method: "GET", description: "Get posts published by a company", parameters: { required: ["username"], optional: ["start", "paginationToken"], }, example_params: { username: "google", start: 0, paginationToken: "", // options, Use this for pagination, e.g., "AQH1..." }, }, get_company_post_comments: { path: "/get-company-post-comments", method: "GET", description: "Get comments on a specific company post", parameters: { required: ["urn"], optional: ["sort", "page"], }, example_params: { urn: "7179144327430844416", sort: "mostRelevant", page: 50, }, }, // search_jobs: { // path: "/search-jobs", // method: "GET", // description: "Search for LinkedIn job postings with comprehensive filters", // parameters: { // required: ["keywords"], // optional: ["locationId", "companyIds", "datePosted", "salary", "jobType", "experienceLevel", "titleIds", "functionIds", "start", "industryIds", "onsiteRemote", "sort"], // }, // example_params: { // keywords: "golang", // locationId: "92000000", // Default: 92000000, follow link to find location id // companyIds: "", // follow link to find company id // datePosted: "anyTime", // anyTime, pastMonth, pastWeek, past24Hours // salary: "", // 40k+, 60k+, 80k+, 100k+, 120k+, 140k+, 160k+, 180k+, 200k+ (example: 80k+) // jobType: "", // fullTime, partTime, contract, internship (example: contract) // experienceLevel: "", // internship, associate, director, entryLevel, midSeniorLevel, executive (example: executive) // titleIds: "", // follow link to find title id by title // functionIds: "", // follow link to find function id // start: "", // 0, 25, 50, 75, 100, etc. Maximum start is 975 // industryIds: "", // follow link to find industry id // onsiteRemote: "", // onSite, remote, hybrid (example: remote) // sort: "mostRelevant", // mostRelevant, mostRecent // }, // }, // get_job_details: { // path: "/get-job-details", // method: "GET", // description: "Get detailed information about a specific job posting", // parameters: { // required: ["jobId"], // optional: [], // }, // example_params: { // jobId: "3472345678", // }, // }, // // search_jobs_v2: { // path: "/search-jobs-v2", // method: "GET", // description: "Enhanced job search with additional filters and improved results", // parameters: { // required: ["keywords"], // optional: [], // Need to check specific params for V2 // }, // example_params: { // keywords: "data", // }, // }, // // get_hiring_team: { // path: "/get-hiring-team", // method: "GET", // description: "Get information about the hiring team for a specific job", // parameters: { // required: ["jobId"], // optional: [], // }, // example_params: { // jobId: "3472345678", // }, // }, // // get_profiles_posted_jobs: { // path: "/get-profiles-posted-jobs", // method: "GET", // description: "Get jobs posted by a specific LinkedIn profile/recruiter", // parameters: { // required: ["username"], // optional: [], // }, // example_params: { // username: "jaywestlin", // }, // }, search_posts: { path: "/search-posts", method: "POST", description: "Search for LinkedIn posts with advanced filters", parameters: { required: [], optional: [], }, body_params: { required: ["keyword"], optional: [ "sortBy", "datePosted", "page", "contentType", "fromMember", "mentionsMember", "fromCompany", "mentionsOrganization", "authorIndustry", "authorCompany", "authorTitle", ], }, example_body: { keyword: "microsoft", sortBy: "date_posted", // date_posted, relevance datePosted: "", // date filter page: 1, contentType: "", // post type filter fromMember: [ "ACoAAAEkwwAB9KEc2TrQgOLEQ-vzRyZeCDyc6DQ", "ACoAAANuWM8BtmA18VYdgqPtIWt6GhBCTDXToV4", ], mentionsMember: ["ACoAAAEkwwAB9KEc2TrQgOLEQ-vzRyZeCDyc6DQ"], fromCompany: [1441, 1035], mentionsOrganization: [1441, 1035], authorIndustry: [96, 4], authorCompany: [1035], authorTitle: "", }, content_type: "application/json", }, search_post_by_hashtag: { path: "/search-posts-by-hashtag", method: "POST", description: "Search for posts that contain specific hashtags", parameters: { required: [], optional: [], }, body_params: { required: ["hashtag"], optional: ["sortBy", "start", "paginationToken"], }, example_body: { hashtag: "golang", sortBy: "REV_CHRON", // RELEVANCE, REV_CHRON start: "0", // for pagination: 0, 50, 100, 150, 200, 250, etc. paginationToken: "", // required when fetching next results page }, content_type: "application/json", }, get_post_by_id: { path: "/get-post", method: "GET", description: "Get detailed information about a specific LinkedIn post", parameters: { required: ["postId"], optional: [], }, example_params: { postId: "7134567890123456789", }, }, get_post_by_url: { path: "/get-post", method: "GET", description: "Get detailed information about a specific LinkedIn post", parameters: { required: ["url"], optional: [], }, example_params: { url: "https://www.linkedin.com/feed/update/urn:li:activity:7219434359085252608/", }, }, get_post_reposts: { path: "/get-post-reposts", method: "POST", description: "Get all reposts/shares of a specific LinkedIn post", parameters: { required: [], optional: [], }, body_params: { required: ["urn"], optional: ["page", "paginationToken"], }, example_body: { urn: "7245786832909557760", page: 1, paginationToken: "", }, content_type: "application/json", }, get_post_reactions: { path: "/get-post-reactions", method: "POST", description: "Get reactions (likes, appreciation, empathy) for a specific post", body_params: { required: ["url"], optional: [ "page", "reactionType", // LIKE, APPRECIATION, EMPATHY, INTEREST, PRAISE, ENTERTAINMENT ], }, example_body: { url: "7134567890123456789", page: 1, // for pagination: 0, 50, 100, etc. reactionType: "like", // like, love, celebrate, support, insightful, funny }, content_type: "application/json", }, // get_user_articles: { // path: "/get-user-articles", // method: "GET", // description: "Get all articles published by a specific LinkedIn user", // parameters: { // optional: ["url", "username"], // }, // example_params: { // url: "https://www.linkedin.com/in/williamhgates", // username: "williamhgates", // }, // }, // get_article: { // path: "/get-article", // method: "GET", // description: "Get detailed information about a specific LinkedIn article", // parameters: { // required: ["url"], // }, // example_params: { // url: "https://www.linkedin.com/pulse/hidden-costs-unreliable-electricity-bill-gates/", // }, // }, // get_article_comments: { // path: "/get-article-comments", // method: "GET", // description: "Get comments on a specific LinkedIn article", // parameters: { // required: ["url"], // optional: ["page", "sort"], // }, // example_params: { // url: "https://www.linkedin.com/pulse/2024-corporate-climate-pivot-bill-gates-u89mc/?trackingId=V85mkekwT9KruOXln2gzIg%3D%3D", // page: 0, // sort: "REVERSE_CHRONOLOGICAL", // }, // }, // get_article_reactions: { // path: "/get-article-reactions", // method: "GET", // description: "Get reactions (likes, comments, shares) for a specific article", // parameters: { // required: ["url"], // optional: ["page"], // }, // example_params: { // url: "https://www.linkedin.com/pulse/2024-corporate-climate-pivot-bill-gates-u89mc/?trackingId=V85mkekwT9KruOXln2gzIg%3D%3D", // page: "like", // }, // }, search_locations: { path: "/search-locations", method: "GET", description: "Search for LinkedIn locations with filters", parameters: { required: ["keyword"], }, example_params: { keyword: "berlin", }, }, }; // Helper Constants export const LOCATIONS = { US: { ALL: 103644278, NEW_YORK: 105080838, SAN_FRANCISCO: 102277331, LOS_ANGELES: 102448103, CHICAGO: 103112676, BOSTON: 102380872, SEATTLE: 104116203, AUSTIN: 104472866, DENVER: 103736294, MIAMI: 105149290, }, }; export const COMPANY_SIZES = { SELF_EMPLOYED: "A", TINY: "B", SMALL: "C", MEDIUM: "D", LARGE: "E", XLARGE: "F", ENTERPRISE: "G", MASSIVE: "H", MEGA: "I", ALL: ["A", "B", "C", "D", "E", "F", "G", "H", "I"], }; export const INDUSTRIES = { TECHNOLOGY: 96, FINANCIAL_SERVICES: 43, HEALTHCARE: 14, RETAIL: 27, MANUFACTURING: 12, EDUCATION: 69, CONSTRUCTION: 48, REAL_ESTATE: 44, ENTERTAINMENT: 28, CONSULTING: 9, }; // Default values export const DEFAULT_PROFILE_PICTURE = "https://cdn.linkedin.com/default-profile.png"; // Error Classes /** * Base error class for all LinkedIn API related errors * @class LinkedInError * @extends Error * @example * try { * await api.getProfile('invalid-user'); * } catch (error) { * if (error instanceof LinkedInError) { * console.log('LinkedIn API Error:', error.message); * console.log('Error Code:', error.code); * } * } */ export class LinkedInError extends Error { constructor(message, code) { super(message); this.code = code; this.name = "LinkedInError"; } } /** * Error thrown when API rate limits are exceeded * @class RateLimitError * @extends LinkedInError * @example * try { * await api.searchProfiles({ keywords: 'test' }); * } catch (error) { * if (error instanceof RateLimitError) { * console.log('Rate limited. Retry after:', error.retryAfter); * } * } */ export class RateLimitError extends LinkedInError { constructor(message, headers) { super(message, "RATE_LIMIT"); this.retryAfter = headers["x-ratelimit-reset"] ? new Date(headers["x-ratelimit-reset"] * 1000) : null; } } /** * Error thrown when requested resource is not found or inaccessible * @class NotFoundError * @extends LinkedInError * @example * try { * await api.getProfile('nonexistent-user'); * } catch (error) { * if (error instanceof NotFoundError) { * console.log('Profile not found or private'); * } * } */ export class NotFoundError extends LinkedInError { constructor(message) { super(message, "NOT_FOUND"); } } /** * Error thrown when network requests fail * @class NetworkError * @extends LinkedInError * @example * try { * await api.getProfile('user'); * } catch (error) { * if (error instanceof NetworkError) { * console.log('Network error:', error.originalError.message); * } * } */ export class NetworkError extends LinkedInError { constructor(message, originalError) { super(message, "NETWORK_ERROR"); this.originalError = originalError; } } // Helper Functions /** * Validate if a username meets LinkedIn's format requirements * @param {string} username - The username to validate * @returns {boolean} True if the username is valid * @example * isValidUsername('john-doe'); // true * isValidUsername('john@doe'); // false */ export function isValidUsername(username) { return /^[a-zA-Z0-9-]{3,100}$/.test(username); } /** * Extract username from a LinkedIn profile URL * @param {string} url - LinkedIn profile URL * @returns {string|null} The extracted username or null if not found * @example * extractUsername('https://linkedin.com/in/john-doe'); // 'john-doe' * extractUsername('https://example.com'); // null */ export function extractUsername(url) { const match = url.match(/linkedin\.com\/in\/([a-zA-Z0-9-]+)/); return match?.[1] || null; } // Entity Classes /** * LinkedIn Profile entity with helper methods for accessing profile data * @class LinkedInProfile * @example * const profile = await api.getProfile('satyanadella'); * console.log(profile.getFullName()); // "Satya Nadella" * console.log(profile.getCurrentPosition()?.title); // "CEO" */ export class LinkedInProfile { /** * Create a LinkedInProfile instance * @param {ProfileData} data - Raw profile data from LinkedIn API */ constructor(data) { this.data = data; } /** * Get raw profile data for advanced use cases * @returns {ProfileData} Original API response data */ get raw() { return this.data; } /** * Get profile ID * @returns {number} Profile ID */ getId() { return this.data.id; } /** * Get profile URN (Uniform Resource Name) * @returns {string} Profile URN */ getUrn() { return this.data.urn; } /** * Get LinkedIn username * @returns {string} Username from profile URL */ getUsername() { return this.data.username; } /** * Get first name * @returns {string} First name or empty string if not available */ getFirstName() { return this.data.firstName || ""; } /** * Get last name * @returns {string} Last name or empty string if not available */ getLastName() { return this.data.lastName || ""; } /** * Get full display name * @returns {string} Combined first and last name, or 'Unknown' if neither available * @example * profile.getFullName(); // "Satya Nadella" */ getFullName() { return `${this.getFirstName()} ${this.getLastName()}`.trim() || "Unknown"; } /** * Get professional headline * @returns {string} Professional headline or empty string * @example * profile.getHeadline(); // "CEO at Microsoft" */ getHeadline() { return this.data.headline || ""; } /** * Get profile summary/about section * @returns {string} Profile summary or empty string */ getSummary() { return this.data.summary || ""; } /** * Get LinkedIn profile URL * @returns {string} Full LinkedIn profile URL * @example * profile.getLinkedInUrl(); // "https://linkedin.com/in/satyanadella" */ getLinkedInUrl() { return `https://linkedin.com/in/${this.data.username}`; } /** * Check if profile is marked as Top Voice * @returns {boolean} True if profile has Top Voice status */ isTopVoice() { return this.data.isTopVoice || false; } /** * Check if profile is a LinkedIn Creator * @returns {boolean} True if profile has Creator status */ isCreator() { return this.data.isCreator || false; } /** * Check if profile has Premium membership * @returns {boolean} True if profile has Premium status */ isPremium() { return this.data.isPremium || false; } /** * Get profile picture URL * @returns {string} Profile picture URL or default avatar if none available * @example * profile.getProfilePictureUrl('large'); // High resolution image */ getProfilePictureUrl() { const url = this.data.profilePicture; if (!url) return DEFAULT_PROFILE_PICTURE; return url; } /** * Get background/cover image URL * @returns {string|null} Background image URL or null if not available */ getBackgroundImageUrl() { const images = this.data.backgroundImage; if (!images || images.length === 0) return null; return images[images.length - 1].url; } /** * Get location information * @returns {Object} Location object with country, city, full, and countryCode * @example * const location = profile.getLocation(); * console.log(location.city); // "Seattle" * console.log(location.country); // "United States" */ getLocation() { return ( this.data.geo || { country: "", city: "", full: "", countryCode: "", } ); } /** * Get current/most recent position * @returns {Object|null} Position object or null if no positions available * @example * const position = profile.getCurrentPosition(); * if (position) { * console.log(position.title); // "CEO" * console.log(position.companyName); // "Microsoft" * } */ getCurrentPosition() { return this.data.position?.[0] || null; } /** * Get all work positions/experience * @returns {Array} Array of position objects, empty array if none * @example * const positions = profile.getAllPositions(); * positions.forEach(pos => console.log(pos.title, 'at', pos.companyName)); */ getAllPositions() { return this.data.position || []; } /** * Get education history * @returns {Array} Array of education objects, empty array if none * @example * const education = profile.getEducation(); * education.forEach(edu => console.log(edu.degree, 'from', edu.schoolName)); */ getEducation() { return this.data.educations || []; } /** * Get all skills * @returns {Array} Array of skill objects, empty array if none * @example * const skills = profile.getSkills(); * skills.forEach(skill => console.log(skill.name, skill.endorsementsCount)); */ getSkills() { return this.data.skills || []; } /** * Get skills that have endorsements * @returns {Array} Array of skill objects with endorsements > 0 * @example * const endorsedSkills = profile.getSkillsWithEndorsements(); * console.log(`${endorsedSkills.length} skills with endorsements`); */ getSkillsWithEndorsements() { return this.getSkills().filter((skill) => skill.endorsementsCount > 0); } } /** * LinkedIn Company entity with helper methods for accessing company data * @class LinkedInCompany * @example * const company = await api.getCompany('microsoft'); * console.log(company.getName()); // "Microsoft" * console.log(company.getEmployeeCount()); // 221000 */ export class LinkedInCompany { /** * Create a LinkedInCompany instance * @param {CompanyData} data - Raw company data from LinkedIn API */ constructor(data) { this.data = data; } /** * Get raw company data for advanced use cases * @returns {CompanyData} Original API response data */ get raw() { return this.data; } /** * Get company ID * @returns {string} Company ID */ getId() { return this.data.id; } /** * Get company name (falls back to universal name if main name unavailable) * @returns {string} Company name or 'Unknown Company' if neither available * @example * company.getName(); // "Microsoft" */ getName() { return this.data.name || this.data.universalName || "Unknown Company"; } /** * Get universal company name (URL-friendly identifier) * @returns {string} Universal name or empty string * @example * company.getUniversalName(); // "microsoft" */ getUniversalName() { return this.data.universalName || ""; } /** * Get LinkedIn company page URL * @returns {string} Full LinkedIn company URL * @example * company.getLinkedInUrl(); // "https://linkedin.com/company/microsoft" */ getLinkedInUrl() { return this.data.linkedinUrl || ""; } /** * Get company tagline/slogan * @returns {string} Company tagline or empty string * @example * company.getTagline(); // "Empowering every person and organization on the planet to achieve more" */ getTagline() { return this.data.tagline || ""; } /** * Get company description * @returns {string} Company description or empty string */ getDescription() { return this.data.description || ""; } /** * Get company website URL * @returns {string} Website URL or empty string * @example * company.getWebsite(); // "https://microsoft.com" */ getWebsite() { return this.data.website || ""; } /** * Get company phone number * @returns {string} Phone number or empty string */ getPhone() { return this.data.phone || ""; } /** * Get total employee count * @returns {number} Number of employees, 0 if not available * @example * company.getEmployeeCount(); // 221000 */ getEmployeeCount() { return this.data.staffCount || 0; } /** * Get LinkedIn follower count * @returns {number} Number of followers, 0 if not available * @example * company.getFollowerCount(); // 15000000 */ getFollowerCount() { return this.data.followerCount || 0; } /** * Get employee count range (e.g., "10001+") * @returns {string} Staff count range or empty string * @example * company.getStaffCountRange(); // "10001+" */ getStaffCountRange() { return this.data.staffCountRange || ""; } /** * Get company logo URL * @param {string} [size='medium'] - Logo size ('small', 'medium', 'large') * @returns {string} Logo URL or empty string if not available * @example * company.getLogoUrl('large'); // High resolution logo */ getLogoUrl(size = "medium") { const sizeMap = { small: 100, medium: 200, large: 400 }; const targetSize = sizeMap[size] || 200; const logo = this.data.logos?.find((l) => l.width === targetSize); return logo?.url || this.data.Images?.logo || ""; } /** * Get company cover/background image URL * @returns {string|null} Cover image URL or null if not available */ getCoverImageUrl() { return this.data.Images?.cover || null; } /** * Get headquarters location * @returns {Object|null} Headquarters location object or null if not available * @example * const hq = company.getHeadquarters(); * if (hq) { * console.log(hq.city); // "Redmond" * console.log(hq.country); // "US" * } */ getHeadquarters() { return this.data.locations?.find((loc) => loc.headquarter) || null; } /** * Get all company locations/offices * @returns {Array} Array of location objects, empty array if none * @example * const locations = company.getAllLocations(); * locations.forEach(loc => console.log(loc.city, loc.country)); */ getAllLocations() { return this.data.locations || []; } /** * Get company industries * @returns {Array} Array of industry strings, empty array if none * @example * company.getIndustries(); // ["Software Development", "Computer Software"] */ getIndustries() { return this.data.industries || []; } /** * Get company specialties/focus areas * @returns {Array} Array of specialty strings, empty array if none * @example * company.getSpecialties(); // ["cloud computing", "productivity software", "business applications"] */ getSpecialties() { return this.data.specialities || []; } /** * Get company founding year * @returns {number|null} Founding year or null if not available * @example * company.getFounded(); // 1975 */ getFounded() { return this.data.founded || null; } /** * Check if company page is verified by LinkedIn * @returns {boolean} True if verified */ isVerified() { return this.data.pageVerification?.verified || false; } /** * Check if company page is claimed (controlled by the company) * @returns {boolean} True if claimed */ isClaimed() { return !this.data.isClaimable; } } /** * LinkedIn Post entity with helper methods for accessing post data and engagement metrics * @class LinkedInPost * @example * const post = await api.getPost('7219434359085252608'); * console.log(post.getText()); // Post content * console.log(post.getTotalEngagement()); // 1523 */ export class LinkedInPost { /** * Create a LinkedInPost instance * @param {PostData} data - Raw post data from LinkedIn API */ constructor(data) { this.data = data; } /** * Get raw post data for advanced use cases * @returns {PostData} Original API response data */ get raw() { return this.data; } /** * Get post URN (Uniform Resource Name) * @returns {string} Post URN identifier */ getUrn() { return this.data.urn; } /** * Get post text content * @returns {string} Post text or empty string if not available * @example * post.getText(); // "Excited to announce our new product launch!" */ getText() { return this.data.text || ""; } /** * Get LinkedIn post URL * @returns {string} Full LinkedIn post URL or empty string * @example * post.getLinkedInUrl(); // "https://www.linkedin.com/posts/..." */ getLinkedInUrl() { return this.data.shareUrl || ""; } /** * Get shareable post URL (same as getLinkedInUrl) * @returns {string} Post share URL or empty string */ getShareUrl() { return this.data.shareUrl || ""; } /** * Get post creation date as Date object * @returns {Date} Date when post was created * @example * const date = post.getPostedAt(); * console.log(date.toLocaleDateString()); // "7/17/2024" */ getPostedAt() { return new Date(this.data.postedDateTimestamp); } /** * Get post creation timestamp * @returns {number} Unix timestamp in milliseconds */ getPostedDateTimestamp() { return this.data.postedDateTimestamp; } /** * Get total engagement (reactions + comments + reposts) * @returns {number} Combined engagement count * @example * post.getTotalEngagement(); // 1523 (sum of all interactions) */ getTotalEngagement() { return ( (this.data.totalReactionCount || 0) + (this.data.commentsCount || 0) + (this.data.repostsCount || 0) ); } /** * Get total reaction count (all types of reactions) * @returns {number} Total reactions, 0 if not available * @example * post.getTotalReactionCount(); // 107 */ getTotalReactionCount() { return this.data.totalReactionCount || 0; } /** * Get like count specifically * @returns {number} Number of likes, 0 if not available * @example * post.getLikeCount(); // 89 */ getLikeCount() { return this.data.likeCount || 0; } /** * Get comments count * @returns {number} Number of comments, 0 if not available * @example * post.getCommentsCount(); // 23 */ getCommentsCount() { return this.data.commentsCount || 0; } /** * Get reposts/shares count * @returns {number} Number of reposts, 0 if not available * @example * post.getRepostsCount(); // 15 */ getRepostsCount() { return this.data.repostsCount || 0; } /** * Get post author information * @returns {Object} Author object with name, headline, etc. * @example * const author = post.getAuthor(); * console.log(author.firstName, author.lastName); // "John Doe" */ getAuthor() { return this.data.author || {}; } /** * Get company information if posted by a company * @returns {Object} Company object or empty object if not a company post */ getCompany() { return this.data.company || {}; } /** * Check if post contains video content * @returns {boolean} True if post has video * @example * if (post.hasVideo()) { * const video = post.getVideo(); * console.log(video.duration); // Video length in seconds * } */ hasVideo() { return !!this.data.video?.url; } /** * Check if post contains article content * @returns {boolean} True if post has article * @example * if (post.hasArticle()) { * const article = post.getArticle(); * console.log(article.title); // Article title * } */ hasArticle() { return !!this.data.article?.title; } /** * Check if post contains document attachments * @returns {boolean} True if post has documents */ hasDocument() { return !!this.data.document; } /** * Check if post is a poll * @returns {boolean} True if post is a poll */ isPoll() { return !!this.data.poll; } /** * Check if this post is a repost/share of another post * @returns {boolean} True if this is a repost */ isRepost() { return !!this.data.reposted || !!this.data.resharedPost; } /** * Check if post is a brand partnership * @returns {boolean} True if marked as brand partnership */ isBrandPartnership() { return this.data.isBrandPartnership || false; } /** * Get video content details * @returns {Object|null} Video object with URL, duration, etc. or null if no video * @example * const video = post.getVideo(); * if (video) { * console.log(video.url); // Video URL * console.log(video.duration); // Duration in seconds * } */ getVideo() { return this.data.video || null; } /** * Get article content details * @returns {Object|null} Article object with title, link, etc. or null if no article * @example * const article = post.getArticle(); * if (article) { * console.log(article.title); // Article title * console.log(article.link); // Article URL * } */ getArticle() { return this.data.article || null; } } // Search Item Classes removed in v1.3.2 - Search results now return raw data objects directly // Search Result Classes /** * Profile search results with pagination support * @class ProfileSearchResult * @example * const results = await api.searchProfiles({ keywords: 'engineer' }); * console.log(`Found ${results.total} profiles`); * results.items.forEach(item => console.log(item.getFullName())); * * if (results.hasNextPage()) { * const page2 = await results.getNextPage(); * } */ export class ProfileSearchResult { /** * Create a ProfileSearchResult instance * @param {ProfileSearchData} data - Search result data from API * @param {LinkedInAPI} api - API instance for pagination * @param {ProfileSearchParams} searchParams - Original search parameters */ constructor(data, api, searchParams) { this._data = data; this.api = api; this.searchParams = { ...searchParams }; // Defensive copy to prevent external mutation } /** * Get the data object (for advanced use cases) * @returns {ProfileSearchData} Search result data */ get data() { return { ...this._data }; // Return copy to prevent mutation } /** * Get search parameters used for this search * @returns {ProfileSearchParams} Search parameters */ get params() { return { ...this.searchParams }; // Return copy to prevent mutation } /** * Get total number of profiles found * @returns {number} Total profile count * @example * console.log(`Found ${results.total} profiles`); // "Found 1000 profiles" */ get total() { return this._data.total || 0; } /** * Get array of profile search items * @returns {ProfileSearchItemData[]} Array of profile search data objects * @example * results.items.forEach(item => { * console.log(item.fullName, '-', item.headline); * }); */ get items() { // Use flattened data structure const items = this._data.items || []; if (!Array.isArray(items)) { console.warn( "ProfileSearchResult data does not contain items array:", this._data, ); return []; } return items; // Return raw data objects directly in v1.3.2+ } /** * Check if more results are available * @returns {boolean} True if more pages available * @example * if (results.hasNextPage()) { * console.log('More results available'); * } */ hasNextPage() { return this.items.length > 0 && this.items.length < this.total; } /** * Get the next page of search results * @returns {Promise<ProfileSearchResult>} Next page of results * @throws {Error} When no more pages available * @example * const page2 = await results.getNextPage(); * console.log(`Page 2 has ${page2.items.length} items`); */ async getNextPage() { if (!this.hasNextPage()) { throw new Error("No more pages available"); } const nextPage = (this.searchParams.page || 1) + 1; return this.api.searchProfiles({ ...this.searchParams, page: nextPage, }); } } /** * Company search results with pagination support * @class CompanySearchResult */ export class CompanySearchResult { /** * Create a CompanySearchResult instance * @param {CompanySearchData} data - Search result data from API * @param {LinkedInAPI} api - API instance for pagination * @param {CompanySearchParams} searchParams - Original search parameters */ constructor(data, api, searchParams) { this._data = data; this.api = api; this.searchParams = { ...searchParams }; // Defensive copy to prevent external mutation } /** * Get the data object (for advanced use cases) * @returns {CompanySearchData} Search result data */ get data() { return { ...this._data }; // Return copy to prevent mutation } /** * Get search parameters used for this search * @returns {CompanySearchParams} Search parameters */ get params() { return { ...this.searchParams }; // Return copy to prevent mutation } /** * Get total number of companies found * @returns {number} Total company count */ get total() { return this._data.total || 0; } /** * Get array of company search items * @returns {CompanySearchItemData[]} Array of company search data objects */ get items() { // Use flattened data structure const items = this._data.items || []; if (!Array.isArray(items)) { console.warn( "CompanySearchResult data does not contain items array:", this._data, ); return []; } // Transform universalName to username for consistency with profiles (v1.3.3+) return items.map(item => ({ ...item, username: item.universalName, universalName: undefined, })); } /** * Check if more results are available * @returns {boolean} True if more pages available */ hasNextPage() { return this.items.length > 0; } /** * Get the next page of search results * @returns {Promise<CompanySearchResult>} Next page of results * @throws {Error} When no more pages available */ async getNextPage() { if (!this.hasNextPage()) { throw new Error("No more pages available"); } const nextPage = (this.searchParams.page || 1) + 1; return this.api.searchCompanies({ ...this.searchParams, page: nextPage, }); } } /** * Post search results with pagination support * @class PostSearchResult */ export class PostSearchResult { /** * Create a PostSearchResult instance * @param {PostSearchData} data - Search result data from API * @param {LinkedInAPI} api - API instance for pagination * @param {PostSearchParams} searchParams - Original search parameters */ constructor(data, api, searchParams) { this._data = data; this.api = api; this.searchParams = { ...searchParams }; // Defensive copy to prevent external mutation } /** * Get the data object (for advanced use cases) * @returns {PostSearchData} Search result data */ get data() { return { ...this._data }; // Return copy to prevent mutation } /** * Get search parameters used for this search * @returns {PostSearchParams} Search parameters */ get params() { return { ...this.searchParams }; // Return copy to prevent mutation } /** * Get total number of posts found * @returns {number} Total post count */ get total() { return this._data.total || 0; } /** * Get number of posts in current page (calculated from items.length) * @returns {number} Current page count */ get count() { return this._data.items?.length || 0; } /** * Get array of post search items * @returns {PostSearchItemData[]} Array of post search data objects */ get items