├── .gitignore ├── README.md ├── linkedin_api_method_1.py ├── linkedin_api_method_2.py ├── poetry.lock ├── proxycurl_people_profile_api_endpoint.py └── pyproject.toml /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LinkedIn API Python3 Code Examples 2 | 3 | This project contain three files: 4 | 1. `./linkedin_api_method_1.py` 5 | 2. `./linkedin_api_method_2.py` 6 | 3. `./proxycurl_people_profile_api_endpoint.py` 7 | 8 | ## `linkedin_api_method_1.py` 9 | 10 | This file uses the official LinkedIn Profile API to fetch profile information of at most ONE user (per access token). 11 | Each user must give explicit information before the application can request information of that user. 12 | 13 | This method is available to most developers including free-tier LinkedIn developers. 14 | 15 | ## `linkedin_api_method_2.py` 16 | 17 | This file uses the official LinkedIn Profile API to fetch profile information of **any** profile IDs. 18 | 19 | In order to use this method, the developer must 20 | 1. have a pre-approved application. 21 | 2. Be on a paying subscription to LinkedIn Consumer Product Solution 22 | 23 | ## `proxycurl_people_profile_api_endpoint.py` 24 | 25 | This file uses Proxycurl's LinkedIn API. A third-party alternative to LinkedIn API that is able to fulfill what LinkedIn API provides 26 | without rate limits or approval barriers. -------------------------------------------------------------------------------- /linkedin_api_method_1.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import secrets 3 | 4 | LINKEDIN_CLIENT_ID = '' # todo - fill this field up 5 | LINKEDIN_CLIENT_SECRET = '' # todo - fill this field up 6 | LINKEDIN_REDIRECT_URI = '' # todo - fill this field up 7 | 8 | 9 | def generate_authorization_url(): 10 | """ 11 | Generate an authorization URL for a user to give permission to extract their LinkedIn Profile. 12 | 13 | The genereated URL will take the user to a LinkedIn page where the user will be asked to give explicit 14 | permission to share their profile with you (the application creator). 15 | 16 | Should the user agree, they will be redirected to `LINKEDIN_REDIRECT_URI`. 17 | In the redirect, two fields will appear in the URL parameter, namely `code` and `state`. 18 | 19 | * `state` is generated below using `secrets.token_hex(8).upper()`. This is as a form of identifier for this user. 20 | * `code` is the authorization_code, and can be used in `get_access_token()` to exchange for an `access_token`. 21 | 22 | """ 23 | LI_AUTH_URL = 'https://www.linkedin.com/oauth/v2/authorization' 24 | url = requests.Request('GET', LI_AUTH_URL, 25 | params={ 26 | 'response_type': 'code', 27 | 'client_id': LINKEDIN_CLIENT_ID, 28 | 'redirect_uri': LINKEDIN_REDIRECT_URI, 29 | 'state': secrets.token_hex(8).upper(), 30 | 'scope': '%20'.join(['r_liteprofile', 'r_emailaddress', 'w_member_social']), 31 | }).prepare().url 32 | return url 33 | 34 | 35 | def get_access_token(authorization_code): 36 | """ 37 | Given a authorization `code`, this function will return you `access_token` which can then be used to access a user's LinkedIn profile. 38 | """ 39 | LI_ACCESS_TOKEN_EXCHANGE_URL = 'https://www.linkedin.com/oauth/v2/accessToken' 40 | access_token = requests.post(LI_ACCESS_TOKEN_EXCHANGE_URL, params={ 41 | 'grant_type': 'authorization_code', 42 | 'code': authorization_code, 43 | 'redirect_uri': LINKEDIN_REDIRECT_URI, 44 | 'client_id': LINKEDIN_CLIENT_ID, 45 | 'client_secret': LINKEDIN_CLIENT_SECRET, 46 | }).json()['access_token'] 47 | return access_token 48 | 49 | 50 | def get_profile(access_token): 51 | """ 52 | Fetches the profile of a LinkedIn user who has given you his permission to view his profile 53 | """ 54 | LI_PROFILE_API_ENDPOINT = 'https://api.linkedin.com/v2/me' 55 | r = requests.get(LI_PROFILE_API_ENDPOINT, headers={ 56 | 'Authorization': 'Bearer ' + access_token}) 57 | return r.json() 58 | -------------------------------------------------------------------------------- /linkedin_api_method_2.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | LINKEDIN_CLIENT_ID = '' # todo - fill this field up 4 | LINKEDIN_CLIENT_SECRET = '' # todo - fill this field up 5 | 6 | 7 | def get_access_token(): 8 | """ 9 | If you are 10 | 11 | 1. an approved LinkedIn developer 12 | 2. on a paid subscription to their Consumer Product 13 | 14 | You can use this function to fetch an `access_token` to access the API. 15 | """ 16 | LI_ACCESS_TOKEN_EXCHANGE_URL = 'https://www.linkedin.com/oauth/v2/accessToken' 17 | access_token = requests.post(LI_ACCESS_TOKEN_EXCHANGE_URL, params={ 18 | 'grant_type': 'client_credentials', 19 | 'client_id': LINKEDIN_CLIENT_ID, 20 | 'client_secret': LINKEDIN_CLIENT_SECRET, 21 | }).json()['access_token'] 22 | return access_token 23 | 24 | 25 | def get_profile(access_token, profile_id): 26 | """ 27 | Given an `access_token`, fetch structured data of any profile. 28 | """ 29 | LI_PROFILE_API_ENDPOINT = f'https://api.linkedin.com/v2/people/{profile_id}' 30 | r = requests.get(LI_PROFILE_API_ENDPOINT, headers={ 31 | 'Authorization': 'Bearer ' + access_token, 32 | 'X-RestLi-Protocol-Version': '2.0.0'}) 33 | return r.json() 34 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "certifi" 3 | version = "2020.12.5" 4 | description = "Python package for providing Mozilla's CA Bundle." 5 | category = "main" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "chardet" 11 | version = "4.0.0" 12 | description = "Universal encoding detector for Python 2 and 3" 13 | category = "main" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 16 | 17 | [[package]] 18 | name = "idna" 19 | version = "2.10" 20 | description = "Internationalized Domain Names in Applications (IDNA)" 21 | category = "main" 22 | optional = false 23 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 24 | 25 | [[package]] 26 | name = "manage.py" 27 | version = "0.2.10" 28 | description = "Human friendly CLI builder" 29 | category = "main" 30 | optional = false 31 | python-versions = "*" 32 | 33 | [[package]] 34 | name = "requests" 35 | version = "2.25.1" 36 | description = "Python HTTP for Humans." 37 | category = "main" 38 | optional = false 39 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 40 | 41 | [package.dependencies] 42 | certifi = ">=2017.4.17" 43 | chardet = ">=3.0.2,<5" 44 | idna = ">=2.5,<3" 45 | urllib3 = ">=1.21.1,<1.27" 46 | 47 | [package.extras] 48 | security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] 49 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 50 | 51 | [[package]] 52 | name = "urllib3" 53 | version = "1.26.3" 54 | description = "HTTP library with thread-safe connection pooling, file post, and more." 55 | category = "main" 56 | optional = false 57 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 58 | 59 | [package.extras] 60 | brotli = ["brotlipy (>=0.6.0)"] 61 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 62 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 63 | 64 | [metadata] 65 | lock-version = "1.1" 66 | python-versions = "^3.9" 67 | content-hash = "d03da8e17bcae5c1a1c36c348fc0596a9180611b121f7304c478caf0a2e87d2d" 68 | 69 | [metadata.files] 70 | certifi = [ 71 | {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, 72 | {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, 73 | ] 74 | chardet = [ 75 | {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, 76 | {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, 77 | ] 78 | idna = [ 79 | {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, 80 | {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, 81 | ] 82 | "manage.py" = [ 83 | {file = "manage.py-0.2.10.tar.gz", hash = "sha256:3879bbe304794f36f98fd04af4c9f1ef2b8640fc76ee25572c591beef7cfb1c1"}, 84 | ] 85 | requests = [ 86 | {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, 87 | {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, 88 | ] 89 | urllib3 = [ 90 | {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, 91 | {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, 92 | ] 93 | -------------------------------------------------------------------------------- /proxycurl_people_profile_api_endpoint.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | PROXYCURL_API_KEY = '' # todo - fill this field up 4 | 5 | 6 | def get_profile(profile_id): 7 | api_endpoint = 'https://nubela.co/proxycurl/api/v2/linkedin' 8 | header_dic = {'Authorization': 'Bearer ' + PROXYCURL_API_KEY} 9 | params = { 10 | 'url': f'https://www.linkedin.com/in/{profile_id}', 11 | } 12 | response = requests.get(api_endpoint, 13 | params=params, 14 | headers=header_dic) 15 | return response.json() 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "linkedin-api-python3-examples" 3 | version = "0.1.0" 4 | description = "Project with sample python scripts to invoke Linkedin API" 5 | authors = ["Steven Goh"] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.9" 9 | requests = "^2.25.1" 10 | "manage.py" = "^0.2.10" 11 | 12 | [tool.poetry.dev-dependencies] 13 | 14 | [build-system] 15 | requires = ["poetry-core>=1.0.0"] 16 | build-backend = "poetry.core.masonry.api" 17 | --------------------------------------------------------------------------------