Package: linkedin-api
Linkedin API for PythonSponsorsBecome a sponsorProgrammatically send messages, get jobs, and search profiles with a regular Linkedin user account.No "official" API access required - just use a valid Linkedin account!Caution: This library is not officially supported by LinkedIn. Using it might violate LinkedIn's Terms of Service. Use it at your own risk.InstallationPython >= 3.6 requiredTo install the linkedin_api package, use the following command:pip3installgit+https://github.com/tomquirk/linkedin-api.gitQuick StartSee all methods on thedocumentation website.Below is a basic example of how to use linkedin_api:fromlinkedin_apiimportLinkedin# Authenticate using any Linkedin account credentialsapi=Linkedin('reedhoffman@linkedin.com','*******')# GET a profileprofile=api.get_profile('billy-g')# GET a profiles contact infocontact_info=api.get_profile_contact_info('billy-g')# GET 1st degree connections of a given profileconnections=api.get_profile_connections('1234asc12304')Commercial alternativesThis is a sponsored sectionProspeoExtract data and find verified emails in real-time withProspeo LinkedIn Email Finder API.Learn moreSubmit a LinkedIn profile URL to our API and get:Profile data extracted in real-timeCompany data of the profileVerified work email of the profileExclusive data points (gender, cleaned country code, time zone...)One do-it-all requestStable API, tested under high loadTry it with 75 profiles.Get your FREE API key now.ProxycurlScrape public LinkedIn profile data at scale withProxycurl APIs.Learn moreScraping Public profiles are battle tested in court in HiQ VS LinkedIn case.GDPR, CCPA, SOC2 compliantHigh rate limit - 300 requests/minuteFast - APIs respond in ~2sFresh data - 88% of data is scraped real-time, other 12% are not older than 29 daysHigh accuracyTons of data points returned per profileBuilt for developers, by developers.End sponsored sectionDocumentationFor comprehensive documentation, including available methods and parameters, visit thedocumentation.Learn moreabout how it works.DisclaimerThis library is not endorsed or supported by LinkedIn. It is an unofficial library intended for educational purposes and personal use only. By using this library, you agree to not hold the author or contributors responsible for any consequences resulting from its usage.ContributingWe welcome contributions!Learn how to find endpointsDevelopment SetupDependenciesPython 3.7A valid Linkedin user account (don't use your personal account, if possible)pipenv(optional)Development installationCreate a.envconfig file. An example is provided in.env.example- you include at least all of the settings set there.Using pipenv...pipenvinstall--dev
pipenvshellRunning testspython-mpytesttestsTroubleshootingI keep getting aCHALLENGELinkedin will throw you a curve ball in the form of a Challenge URL. We currently don't handle this, and so you're kinda screwed. We think it could be only IP-based (i.e. logging in from different location). Your best chance at resolution is to log out and log back in on your browser.Known reasons for Challengeinclude:2FARate-limit - "It looks like you’re visiting a very high number of pages on LinkedIn.". Note - n=1 experiment where this page was hit after ~900 contiguous requests in a single session (within the hour) (these included random delays between each request), as well as a bunch of testing, so who knows the actual limit.Please add more as you come across them.Search problemsMileage may vary when searching general keywords like "software" using the standardsearchmethod. They've recently added some smarts around search whereby they group results by people, company, jobs etc. if the query is general enough. Try to use an entity-specific search method (i.e. search_people) where possible.How it worksBefore using this project, please consult theTerms and ConditionsandLegal Notice.This project attempts to provide a simple Python interface for the Linkedin API.Do you mean thelegit Linkedin API?NO! To retrieve structured data, theLinkedin Websiteuses a service they callVoyager. Voyager endpoints give us access to pretty much everything we could want from Linkedin: profiles, companies, connections, messages, etc. - anything that you can see on linkedin.com, we can get from Voyager.This project aims to provide complete coverage for Voyager.How does it work?In-depth overviewVoyager endpoints look like this:https://www.linkedin.com/voyager/api/identity/profileView/tom-quirkOr, more clearly___________________________________ _______________________________
|             base path             |            resource           |
https://www.linkedin.com/voyager/api /identity/profileView/tom-quirkThey are authenticated with a simple cookie, which we send with every request, along with a bunch of headers.To get a cookie, we POST a given username and password (of a valid Linkedin user account) tohttps://www.linkedin.com/uas/authenticate.To find endpointsWe're looking at the Linkedin website and we spot some data we want. What now?The most reliable method to find the relevant endpoint is to:view sourcecommand-f/search the page for some keyword in the data. This will exist inside of a<code>tag.Scroll down to thenext adjacent elementwhich will be another<code>tag, probably with anidthat looks something like<codestyle="display: none"id="datalet-bpr-guid-3900675">{"request":"/voyager/api/identity/profiles/tom-quirk/profileView","status":200,"body":"bpr-guid-3900675"}</code>The value ofrequestis the url! 🤘You can also use thenetworktab in you browsers developer tools, but you will encounter mixed results.How Clients query Voyagerlinkedin.com queries data using theRest-li Protocol. Rest-li is an internal query language/syntax where clients (like linkedin.com) to specify what data they want (similar to the GraphQL concept).Here's an example of making a request for an organisation'snameandgroups(the Linkedin groups it manages):/voyager/api/organization/companies?decoration=(name,groups*~(entityUrn,largeLogo,groupName,memberCount,websiteUrl,url))&q=universalName&universalName=linkedinThe "querying" happens in thedecorationparameter, which looks like(
    name,
    groups*~(entityUrn,largeLogo,groupName,memberCount,websiteUrl,url)
)So here, we request an organisation name, and a list of groups, where for each group we wantlargeLogo,groupName, etc.Different endpoints use different parameters (and perhaps even different syntaxes) to specify these queries. Notice that the above query had a parameterqwhose value wasuniversalName; the query was then specified with thedecorationparameter.In contrast, the/search/clusterendpoint usesq=guided, and specifies its query with theguidedparameter, whose value is something likeList(v->PEOPLE)It could be possible to document (and implement a nice interface for) this query language - as we add more endpoints to this project, I'm sure it will become more clear if such a thing would be possible (and if it's worth it).Releasing a new versionBump__version__in__init__.pypython3 setup.py sdist bdist_wheelpython3 -m twine upload dist/*