linkedin-unofficial-api
linkedin-unofficial-api copied to clipboard
Documentation and libraries for the unofficial LinkedIn API.
linkedin-unofficial-api
Documentation and libraries for the unofficial LinkedIn API.
As of May 19th, 2015, LinkedIn, gutted their Developer Program. Leaving only a small number of rather uninteresting API Calls. Details here Developer Program Transition
This project documents the API used by the LinkedIn Mobile App. It offers access to most of the original API and a variety of endpoints that were never available.
API Reference
Domains
-
www.linkedin.com
-- Authentication Only -
touch.www.linkedin.com
-- Everything else
Endpoints
-
GET
/authenticate -
POST
/authenticate -
GET
/li/v1/people/person -
GET
/li/v1/pages/you -
GET
/li/v2/profile/:id -
GET
/li/v1/people/:id/connections -
GET
/li/v2/profile/:id/detail/background -
GET
/li/v2/people/:id/endorsements -
GET
/li/v1/pages/mailbox
TODO Endpoints
-
GET
/li/v2/profile/:id/detail/recommendations -
GET
/li/v2/notifications -
GET
/li/v2/wvmp
Headers
Name | Value |
---|---|
X-Li-User-Agent | 'LIAuthLibrary:3.2.4 com.linkedin.LinkedIn:8.8.1 iPhone:8.3' |
User-Agent | 'LinkedIn/8.8.1 CFNetwork/711.3.18 Darwin/14.0.0' |
X-User-Language | en |
X-User-Locale | en_US |
Accept-Language | en-us |
The top two headers are nessesary for user authentication, the other three make sure the results come back in English not Norwegian.
User Auth
Part One: get some cookies
GET /uas/authenticate
Request
$ curl https://www.linkedin.com/uas/authenticate -c cookie.jr -H 'X-Li-User-Agent: LIAuthLibrary:3.2.4 com.linkedin.LinkedIn:8.8.1 iPhone:8.3' -A 'LinkedIn/8.8.1 CFNetwork/711.3.18 Darwin/14.0.0'
Response
{"status":"success"}
Part Two: pass in username, password, and a previously obtained session cookie.
POST /uas/authenticate
Parameters
- session_key -- LinkedIn Username
- session_password -- LinkedIn Password
- JSESSIONID -- session token, found in cookies from first call.
Currently not sure why require you to pass in a cookie as a query paramerter. seems unnecessary.
Request
$ curl https://www.linkedin.com/uas/authenticate -d 'session_key=username%40gmail.com&session_password=sekrett&JSESSIONID=%22ajax%3A4847487595299993333%22' -H 'X-Li-User-Agent: LIAuthLibrary:3.2.4 com.linkedin.LinkedIn:8.8.1 iPhone:8.3' -A 'LinkedIn/8.8.1 CFNetwork/711.3.18 Darwin/14.0.0' -b cookie.jr -c cookie.jr
Response
{"login_result":"PASS","challenge_url":""}
login_result:
- PASS -- success, you can now call the API.
- BAD_PASSWORD -- failure, self explanatory.
- CHALLENGE -- the challenge_url will then be set, call it. solve the recaptcha, party on!
Some thoughts on how to solve or avoid the captcha. Issue #1
Current User Profile
GET /li/v1/people/person
Get summary data from the logged in user's profile.
Request
$ curl https://touch.www.linkedin.com/li/v1/people/person -b cookie.jr -c cookie.jr
Response
{
"response": {
"authToken": "name:xxyz",
"distance": 0,
"email": "[email protected]",
"firstName": "Nick",
"formattedName": "Nick Smith",
"hasPicture": true,
"headline": "Code Monkey",
"id": "293949",
"lastName": "Smith",
"picture": "https://media.licdn.com/mpr/mpr/shrinknp_100_100/p/1/000/202/111/123937.jpg"
}
}
- authToken -- current not sure the use of this token.
Current User Page
GET /li/v1/pages/you
Get linkedin page from the logged in user's profile.
Request
$ curl https://touch.www.linkedin.com/li/v1/pages/you -b cookie.jr -c cookie.jr
Response
{
"profile": {
"person": {
"lastName": "Lane-Smith",
"originalPicture": "https://media.licdn.com/mpr/mpr/p/3/000/267/3dd/3c37294.jpg",
"tType": "pt1",
"distance": 0,
"formattedName": "Nick Lane-Smith",
"authToken": "name:xzzf",
"industry": "Computer Networking",
"picture": "https://media.licdn.com/mpr/mpr/shrinknp_100_100/p/3/000/267/3dd/3c37294.jpg",
"phoneNumbers": [],
"hasPicture": true,
"firstName": "Nick",
"location": "San Francisco Bay Area",
"id": "7881116",
"headline": "Founder at BloomAPI"
},
"detail": [...]
}
}
This JSON blob is huge and not very well structured. It is tied very strongly to the UI of the linkedin app, I wonder why they made this design choice. It could be that the mobile app will adjust the UI to match how the data is layed out. It might also be tightly coupled to the code of the app -- which would suck.
- person -- profile details for the logged in user.
- detail -- has some useful data similar to the background page.
User Page
GET /li/v2/profile/:id
Get linkedin page/profile for the user's id.
Request
$ curl https://touch.www.linkedin.com/li/v2/profile/7881116 -b cookie.jr -c cookie.jr
Response
{
"personTopCard": {
"summary2": {
"text3": "March 2011 - Present (4 years 7 months)",
"tType": "sum4",
"text1": "BloomAPI",
"pictureUrl": "https://media.licdn.com/media/p/2/000/1a7/291/d8d8d8s.png",
"text2": "Founder",
"pictureLogo": "company_tc",
"link": {
"targetContextType": "experience1",
"partialData": {
"title": "Nick Lane-Smith"
},
"resourcePath": "/li/v2/profile/7881116/detail/background",
"id": "7881116",
}
},
"tType": "personTopCard",
"summary1": {
"tType": "sum1",
"values": [
{
"tType": "sum2",
"text1": "139",
"text2": "connections",
...
},
]
},
"text3": "San Francisco, California | Computer Networking",
"text1": "Nick Lane-Smith",
"text2": "Founder at BloomAPI",
"isSelf": true
}
}
This JSON blob is also huge and not very well structured. How rude!
- personTopCard -- this section has some useful summary data about the profile: name, id, picture, connections, etc.
Connections
GET /li/v1/people/:id/connections
Get all of the connections for a specific user id.
Parameters
- count -- maximum number of connections to return
Request
$ curl "https://touch.www.linkedin.com/li/v1/people/7881116/connections?count=5000" -b cookie.jr -c cookie.jr
Response
{
"total": 1055,
"count": 1055,
"values": [{
"authToken": "name:xxyz",
"distance": 2,
"firstName": "Mike",
"formattedName": "Mike Smith",
"headline": "Director of Product",
"id": "10420023",
"lastName": "Smith",
"hasPicture": true,
"picture": "https://media.licdn.com/mpr/mpr/shrinknp_200_200/AAEEAQAAAkmAAAAJDcdZzAFJRIDAKAAA84MFKFKKME.jpg",
"tType": "pt3"
},
...]
}
Background
GET /li/v2/profile/:id/detail/background
Get a user's background (eduction, work experience, patents, projects, etc.)
Request
$ curl https://touch.www.linkedin.com/li/v2/profile/39654029/detail/background -b cookie.jr -c cookie.jr
Response
{
"values": [
{
"tType": "sect4",
"values": [
{
"tType": "prt2",
"text1": "B.A., English and History",
"pictureUrl": "https://media.licdn.com/mpr/mpr/shrinknp_100_100/p/1/005/011/0ec/4deadbeef.png",
"pictureLogo": "education_grey",
"header": "Lewis and Clark College"
}
],
"contextType": "education1"
},
...]
}
- contextType -- can filter response by contextType to select desired background details: experience1, education1, education2, etc.
Endorsements
GET /li/v2/people/:id/endorsements
One of the worst LinkedIn features. Get the endorsements made for user with id.
Request
$ curl https://touch.www.linkedin.com/li/v2/people/39654029/endorsements -b cookie.jr -c cookie.jr
Response
{
"values": [
{
"resourcePath": "/li/v2/people/endorsements?skill=Internal%20Communications",
"values": [
{
"tType": "rt20",
"text1": "Internal Communications",
"count": 2,
"id": "7881116",
"type": "skill"
},
{
"tType": "rt24",
"representsCurrentUser": false,
"text1": "Joshua Reynolds",
"text2": "Head of Marketing at Quantifind, Strategic Advisor to Blanc & Otus and H+K, Holmes Report Top 25 Innovator",
"pictureUrl": "https://media.licdn.com/mpr/mpr/shrinknp_200_200/p/5/005/08b/098/34424324.jpg",
"link": {
"resourcePath": "/li/v1/people/24631444/profile",
"id": "24631444",
"type": "person",
}
},
...
]
},
- values[0] -- this is going to be the skill name and count of endorsers.
- values -- the next N array values are profile summaries (icon, name, blurb) for each of the endorsers.
Messages
GET /li/v1/pages/mailbox
Messages and invitations for the current logged in user.
Request
$ curl https://touch.www.linkedin.com/li/v1/pages/mailbox -b cookie.jr -c cookie.jr
Response
{
"invitations": {
"total": 1,
"values": [{
"isCustomMessage": false,
"read": false,
"folder": "inbox",
"tType": "invitation",
"subject": "Invitation to connect on LinkedIn",
"from": {
"hasPicture": true,
"firstName": "Stephan",
"lastName": "Jenkins",
"authToken": "name:xxzy",
"formattedName": "Stephan Jenkins",
"id": "38373",
"headline": "Singer at Third Eye Blind",
"picture": "https://media.licdn.com/mpr/mpr/shrinknp_200_200/XXaSADADAJAKDJHHJJSJSJSJSJSJJSSJJS.jpg"
},
"id": "I60118598766633_500",
"body": "Nick,\n\nI'd like to add you to my professional network on LinkedIn.\n\n- Stephan",
"seen": true,
"timestamp": 1433339090205
}],
"start": 0,
"count": 20,
"droppedCount": 0,
"unseen": 0
},
"messages": {
"total": 44,
"unread": 0,
"values": [{
"read": true,
"folder": "inbox",
"tType": "message",
"subject": "great to meet you last week",
"from": {
"hasPicture": true,
"firstName": "Mike",
"lastName": "Smith",
"authToken": "name:aLS9",
"formattedName": "Mike Smith",
"id": "58708",
"headline": "Co-Founder, Erros ",
"picture": "https://media.licdn.com/mpr/mpr/shrinknp_200_200/XXaSADADAJAKDJHHJJSJSJSJSJSJJSSJJS.jpg"
},
"isMsgTypeInmail": false,
"id": "I6043170444767420416_500",
"to": [{
"hasPicture": true,
"firstName": "Nick",
"lastName": "Smith",
"authToken": "name:VCev",
"formattedName": "Nick Smith",
"id": "293949",
"headline": "Code Monkey",
"picture": "https://media.licdn.com/mpr/mpr/shrinknp_100_100/p/1/000/265/113/1b5f6fe.jpg"
}],
"body": "XXXZZZYYY",
"seen": true,
"timestamp": 1440804110685,
"hasReplied": false
}
...],
"start": 0,
"count": 20,
"droppedCount": 0,
"unseen": 0
}
}
Installation & Usage
Python Client - by @tomquirk
Disclamer
Use at your own risk! This API is unofficial and unsupported by LinkedIn.
Credits
Thanks to the authors of: