Glasser Team9 min read
How to Use the LinkedIn API with Python
Call the LinkedIn API from Python using OAuth and OpenID Connect. Retrieve member profiles, handle missing fields, and understand profile and jobs limits.

What can the LinkedIn API actually return for your app?
To call the official LinkedIn API from Python, use OAuth and the API product enabled for your developer app. To retrieve the member who consents to your app, start with Sign In with LinkedIn using OpenID Connect and GET /v2/userinfo.
A LinkedIn profile URL, a signed-in member, and a job-search query represent different tasks. Decide which input you have before selecting a library.
| Your input and goal | Appropriate starting point | Access boundary |
|---|---|---|
| A consenting member; name and profile picture | LinkedIn OpenID Connect | The authenticated member's lite profile |
| A public profile URL; work history or enrichment | A data provider with documented URL lookup | Provider-specific coverage and permitted use |
| A job title and location; matching vacancies | A documented job-data search source | Source coverage, filters, pagination, and freshness |
| An employer's vacancy; publish it to LinkedIn | LinkedIn's approved Talent integration | Product and partner eligibility |
LinkedIn's access overview distinguishes self-service permissions from products requiring approval. Installing a client does not grant a restricted scope.
LinkedIn’s official client still requires API access
LinkedIn maintains the official linkedin-api-client Python client, with OAuth and REST request helpers. Its repository is under linkedin-developers.
The example here uses requests to make the request boundaries visible. You can adopt an official client later without changing the access your app needs.
How do you get an authorized member’s profile with Python?
Configure your app, redirect URL, and permissions
In the LinkedIn Developer Portal, select your app and request the Sign In with LinkedIn using OpenID Connect product. Register an HTTPS redirect URL you control, such as your development application's callback endpoint, and keep the client secret on your backend.
Request openid profile for the member's lite profile. Add email only if your application needs it. The OpenID Connect documentation defines these scopes and the userinfo response.
python -m pip install requests
export LINKEDIN_CLIENT_ID='your-client-id'
export LINKEDIN_CLIENT_SECRET='your-client-secret'
export LINKEDIN_REDIRECT_URI='https://your-domain.example/auth/linkedin/callback'
Replace the redirect URI with the exact registered value. LinkedIn compares the redirect URI during authorization and token exchange; changing its path or trailing slash can break the exchange.
If your goal is public-profile research, start with Glasser to access supported data providers with one key and no separate vendor accounts. Keep the OAuth flow below for a LinkedIn sign-in feature; Glasser’s data endpoints serve a different task.
Exchange the authorization code, then request the profile
The script opens LinkedIn's consent page, then asks you to paste the callback URL from your browser into your own terminal. It checks the callback destination and state before exchanging the code, and prints selected profile fields without printing tokens.
This is a local learning example. A deployed application should handle the callback on its server, bind state to the initiating browser session, and use a maintained OAuth/OIDC integration for its login flow. The script does not implement an application session or validate an ID token for signing users into your product.
import json
import os
import secrets
import webbrowser
from urllib.parse import parse_qs, urlencode, urlsplit
import requests
client_id = os.environ["LINKEDIN_CLIENT_ID"]
client_secret = os.environ["LINKEDIN_CLIENT_SECRET"]
redirect_uri = os.environ["LINKEDIN_REDIRECT_URI"]
state = secrets.token_urlsafe(32)
def checked_json(response):
if not response.ok:
raise RuntimeError(f"LinkedIn request failed: HTTP {response.status_code}")
return response.json()
def destination(url):
parts = urlsplit(url)
return parts.scheme, parts.netloc, parts.path
authorization_url = "https://www.linkedin.com/oauth/v2/authorization?" + urlencode({
"response_type": "code",
"client_id": client_id,
"redirect_uri": redirect_uri,
"state": state,
"scope": "openid profile email",
})
webbrowser.open(authorization_url)
callback_url = input("Paste your callback URL locally: ").strip()
if destination(callback_url) != destination(redirect_uri):
raise RuntimeError("Unexpected callback destination")
query = parse_qs(urlsplit(callback_url).query)
returned_state = query.get("state", [""])[0]
if not secrets.compare_digest(returned_state, state):
raise RuntimeError("OAuth state mismatch; restart authorization")
if "error" in query:
raise RuntimeError("Authorization was not granted")
code = query.get("code", [""])[0]
if not code:
raise RuntimeError("Authorization code is missing")
with requests.Session() as session:
token = checked_json(session.post(
"https://www.linkedin.com/oauth/v2/accessToken",
data={
"grant_type": "authorization_code",
"code": code,
"client_id": client_id,
"client_secret": client_secret,
"redirect_uri": redirect_uri,
},
timeout=30,
))
profile = checked_json(session.get(
"https://api.linkedin.com/v2/userinfo",
headers={"Authorization": f"Bearer {token['access_token']}"},
timeout=30,
))
record = {
"subject": profile["sub"],
"name": profile.get("name"),
"picture": profile.get("picture"),
"email": profile.get("email"),
"email_verified": profile.get("email_verified"),
}
print(json.dumps(record, ensure_ascii=False, indent=2))
The endpoints and exchange parameters follow LinkedIn's authorization-code flow. This example was checked against documentation and mocked responses; no live member authorization was performed for this article.
Treat missing email fields as optional claims
Use sub as the returned subject identifier within the relevant application context. Do not replace it with the displayed name or assume it can be reconstructed from a public /in/ URL.
LinkedIn documents email and email_verified as optional. In the example, an absent field remains null in JSON; it does not become an invented address or a false verification result. If email is required by your product, provide a separate collection path instead of treating a missing claim as an API failure.
Why can’t a sign-in token retrieve every profile or job?
OpenID Connect returns a limited member profile
The OIDC response serves member authentication and lite profile retrieval. Its documented fields include the subject, name, picture, locale, and optional email claims. It does not provide a general lookup-by-profile-URL parameter or promise a complete employment history.
If your Python task begins with a list of LinkedIn URLs, define the output you need separately: current company, title, employment dates, source URL, and observation time. Ask a provider to show which of those fields its lookup returns before mapping them into your CRM.
For the procurement side, the LinkedIn profile API pricing guide covers the cost questions associated with that separate task. An OIDC sign-in implementation and a profile-enrichment purchase should have separate success criteria.
Job posting and job search require different access
Searches for “LinkedIn jobs API Python” often mean “find vacancies matching a title and location.” LinkedIn's Job Posting API instead enables authorized third parties to publish jobs for customers. Its documentation currently says new Job Posting API partnerships are not being accepted and directs applicants to Apply Connect.
Adding a jobs URL to the userinfo request cannot produce a vacancy search. If you need job discovery, inspect a job-data source for:
- Supported source sites and geographic coverage.
- Whether the input is a search query, company identifier, or exact job URL.
- Pagination and result caps for one search.
- Job identifiers, source links, posted dates, and collection timestamps.
- How removed, closed, or missing jobs appear in the output.
These are evaluation questions for your application. They are not additional scopes available through LinkedIn's sign-in product.
Need profile details from a LinkedIn URL or vacancies matching a job title? Explore Glasser’s LinkedIn data endpoints to choose a supported profile lookup or job-search operation and check its inputs, available fields, and price.
Why is your LinkedIn request failing or missing data?
Start with the failing stage. A token-exchange error and a missing employment field call for different fixes.
| Symptom | Check first | Useful next action |
|---|---|---|
| Authorization rejects a scope | The app's enabled product and currently available scopes | Match the OIDC scope names to the product in the portal |
| Callback state does not match | The initiating session and returned state | Stop the exchange and restart that authorization flow |
| Token exchange fails | Redirect URI, code validity, client credentials | Use the same URI as authorization and a fresh code |
| Userinfo returns 401 | Access token validity and Bearer header | Reauthorize through the supported flow when needed |
| An endpoint returns 403 | Product permission and resource access | Inspect the exact endpoint's requirements |
| Email is absent | Optional OIDC claims | Preserve missingness and use your product's fallback |
| Work history or jobs are absent | Whether the chosen endpoint exposes them | Select the appropriate data source; repeated sign-in calls will not add those fields |
Avoid logging authorization codes, access tokens, the client secret, or complete callback URLs. For debugging, retain the failing stage, response status, and a sanitized error code. The table is an author troubleshooting sequence; LinkedIn's endpoint documentation remains the source for the actual permission requirements.
When should you use Glasser for profile or job data?
For a sign-in feature, complete the official OAuth/OIDC integration and test the required claims with an account that consents to your app. Keep identity verification claims out of the product description: LinkedIn explicitly states that its OIDC sign-in does not verify a person's real-world identity.
For public-profile research, Glasser brings profile lookup and job-data providers into one place. Match the endpoint to your input: a profile URL for a supported lookup, or a title and location for a supported job search.
Glasser's documented workflow is to search data sources, inspect the selected endpoint contract, and run the call. Inspect input fields, price, and outcome charge clauses before a batch. Check the documented output or a permitted sample before mapping provider fields into your application.
For an initial evaluation, select a small set of records representing your actual workload: a known profile URL, a profile with incomplete employment information, and a job that has closed. Define how your application will represent a match, no result, and an unavailable request. That gives you evidence about workflow fit without assuming complete coverage from one successful response.
Ready to try your first lookup? Open Glasser or connect a compatible agent with
set up https://glasser.ai/SKILL.md, then describe the profile or job data you need and review the selected endpoint before running.
Frequently asked questions
Is there an official LinkedIn Python client?
Yes, LinkedIn maintains linkedin-api-client under the linkedin-developers GitHub organization. It still requires the relevant app permissions and access token.
Can I retrieve any LinkedIn profile by URL with OpenID Connect?
OpenID Connect returns information about the member who authorized your app. Lookup by an arbitrary profile URL requires a separately documented capability.
Can I search LinkedIn jobs with a sign-in token?
The sign-in product does not grant a general job-search API. LinkedIn's Job Posting API is a distinct Talent integration for publishing jobs, with its own eligibility requirements.
Why did the email field disappear?
LinkedIn documents email claims as optional, even when the relevant scope is requested. Handle absence explicitly and avoid treating it as a verified negative result.
Related posts
View more
8 min read
LinkedIn Profile API Pricing: Compare Access Models and Calculate Usable-Record Cost
Compare LinkedIn profile API access models and estimate costs for 100, 1,000, or 10,000 profiles. Account for returned records, retries, and usable data.

7 min read
Business Contact Search: Choose a Database for Your Target Accounts
Choose a B2B contact source using your target accounts, role criteria, contact fields, and a pilot that measures coverage and accepted results.

13 min read
Lead Enrichment: How to Enrich B2B Leads and Choose the Right Tools
Turn incomplete B2B leads into usable CRM records. Compare enrichment tools, resolve conflicting data, and calculate cost per usable lead.
Make your agent
work with real data.
Search, inspect, and run data APIs through one Glasser key. Turn your next question into a result you can use.
Get started with Glasser