Back to guides
/Linked API

LinkedIn Profile Scraper: How to Extract Profile Data via API (2026)

A LinkedIn profile scraper pulls structured data from a person's profile – name, headline, experience, education, skills, activity – from its URL, whether you need one profile or a list of thousands. There is no official LinkedIn API for reading profile data, so every option is a scraper, and they differ a lot in reliability and account risk. This guide shows exactly what fields you can get, a real sample JSON, how to fetch one profile and a whole list in code, an honest take on Chrome extensions versus an API, and what you cannot get (spoiler: emails).

The short version. A person's headline, experience, education, skills, and activity are all extractable from their profile. Personal emails and phone numbers are not on the profile and need consented enrichment. Chrome extensions and homemade scripts run on your own account at machine speed and get it restricted; an API paces like a human and returns clean JSON.

What data is on a LinkedIn profile

A single profile is a rich record. Grouped by what it tells you:

What's in a scraped LinkedIn profile: identity, role, about, experience, education, skills, languages, and activity fields
GroupFields
Identityname, headline, publicUrl, hashedUrl
Role and companyposition, companyName, location, countryCode
Aboutabout, followersCount
Experienceexperiences[] – role, company, dates, duration, employment and location type
Educationeducation[] – school and details
Skills and languagesskills[] ({ name }), languages[] ({ name, proficiency })
Activityposts, comments, reactions

One detail worth building around: use hashedUrl as your stable key, not publicUrl. The vanity URL (/in/jane-doe) changes when someone renames their profile, but the hashed member URL is durable – it is the difference between a deduped database and silent duplicates.

A real scraped profile

Each section is opt-in: you request the parts you need, and you get back typed JSON instead of HTML to parse.

json
{
  "name": "Jane Doe",
  "publicUrl": "https://www.linkedin.com/in/jane-doe",
  "hashedUrl": "https://www.linkedin.com/in/ACoAAB1a2b3c4d",
  "headline": "Head of Growth at Acme",
  "location": "San Francisco, California, United States",
  "countryCode": "US",
  "position": "Head of Growth",
  "companyName": "Acme",
  "about": "Demand generation and lifecycle marketing leader.",
  "followersCount": 4820,
  "experiences": [
    {
      "position": "Head of Growth",
      "companyName": "Acme",
      "employmentType": "fullTime",
      "locationType": "hybrid",
      "duration": 26,
      "startTime": "2024-01-01T00:00:00Z",
      "endTime": null,
      "location": "San Francisco, CA"
    }
  ],
  "education": [
    { "schoolName": "Stanford University", "details": "MBA, Marketing" }
  ],
  "skills": [
    { "name": "Demand Generation" },
    { "name": "SEO" }
  ],
  "languages": [
    { "name": "English", "proficiency": "nativeOrBilingual" },
    { "name": "Spanish", "proficiency": "professionalWorking" }
  ]
}

Four ways to scrape a profile, compared

There is no official profile API, so the real choice is between four methods that trade off scale, output, and the risk to your account.

MethodScaleOutputMaintenanceAccount-ban risk
Manual copy-pasteA few a dayYou retype itNoneNone
Chrome extension50–100/dayA spreadsheetBreaks on layout changesHigh – acts on your account at machine speed
DIY PythonHigh, until blockedRaw HTML you parseYou own selectors and proxiesHigh – headless on your account
API on your own accountHundreds to thousands/dayStructured JSONHandled for youLower – paced like a human

The honest distinction is account risk. A Chrome extension or a headless Python script acts through your own logged-in account at machine speed, which is exactly the pattern LinkedIn flags – accounts get restricted within days. An account-based API runs the same account but paces actions like a real user and returns maintained, structured JSON, so you are not re-writing selectors every time the page changes. We rank all the methods by risk in the guide to scraping LinkedIn, and compare the API models in the LinkedIn scraper API guide.

Scrape one profile by URL

Install the SDK:

bash
# Node.js
npm install -S @linkedapi/node

# Python
pip install linkedapi

Then fetch a profile by URL, choosing which sections to pull:

typescript
import LinkedApi from '@linkedapi/node';

const linkedapi = new LinkedApi({
  linkedApiToken: process.env.LINKED_API_TOKEN,
  identificationToken: process.env.IDENTIFICATION_TOKEN,
});

const workflow = await linkedapi.fetchPerson.execute({
  personUrl: 'https://www.linkedin.com/in/jane-doe',
  retrieveExperience: true,
  retrieveEducation: true,
  retrieveSkills: true,
  retrieveLanguages: true,
});

const { data } = await linkedapi.fetchPerson.result(workflow.workflowId);
console.log(data?.name, data?.position, data?.hashedUrl);

If you only have a name rather than a URL, search for people first and feed the resulting URLs into the fetch.

Scrape a list of profiles in bulk

The common job is enrichment: you have a list of profile URLs – from a CSV, a search export, or your CRM – and you want a structured row for each.

Bulk profile scraping: a list of URLs goes through fetchPerson and comes back as structured rows

Loop over the URLs and collect the results. Linked API runs workflows sequentially on your account, so this paces itself automatically:

typescript
const profileUrls = [
  'https://www.linkedin.com/in/jane-doe',
  'https://www.linkedin.com/in/john-smith',
  // one row per profile
];

const rows = [];
for (const personUrl of profileUrls) {
  const workflow = await linkedapi.fetchPerson.execute({ personUrl, retrieveExperience: true });
  const { data, errors } = await linkedapi.fetchPerson.result(workflow.workflowId);
  if (data) rows.push(data);
  else console.warn('Skipped', personUrl, errors);
}

Two realities to plan for: a share of URLs in any list are stale (people rename or delete profiles), and some profiles are private, so expect to skip a percentage rather than get a clean 100%. Handle the error case, as the loop above does, instead of assuming every fetch succeeds. Bulk volume is also bounded by your account's daily limits – this is your real account, not a proxy pool.

Can you get an email or phone from a profile?

Mostly no, and this is where a lot of tools oversell. Personal emails and phone numbers are almost never shown on a LinkedIn profile, so a scraper reading the profile cannot reliably return them. Tools that advertise "verified emails" are matching the person against a separate, ideally consented, B2B enrichment database – that is a different product from profile scraping, and the data does not come off the profile itself.

The practical split: scrape the profile for role, company, experience, and activity; get contact details from a consented enrichment provider. We cover why direct contact-harvesting is the riskiest move in the scraping guide.

Profile data is largely public, and US courts (hiQ Labs v. LinkedIn) have held that scraping publicly available data is not a Computer Fraud and Abuse Act violation. But public is not a free pass: LinkedIn's User Agreement prohibits automated access (a civil matter, enforced with account restrictions and lawsuits – the data API Proxycurl was sued into shutting down), and personal data falls under GDPR and CCPA.

The ban risk is highest exactly where most people start: Chrome extensions and headless scripts driving your own account too fast. Pace like a human, stop on a CAPTCHA or restriction notice, and keep contact data to consented sources. The full four-layer legal model is in our guide to scraping LinkedIn. This is not legal advice.

Frequently Asked Questions (FAQ)

Name, headline, location, current role and company, the full experience and education history, skills, languages, follower count, and recent activity (posts, comments, reactions). Personal email and phone are generally not on the profile.

No. LinkedIn's official APIs are limited to your own data and approved partner use cases; there is no public API to read other people's profile data. Every "profile API" you see is a scraping or enrichment product.

Yes – pass a list of profile URLs and fetch each one in a loop, collecting structured rows. Plan for a percentage of stale or private URLs, and remember that volume on an account-based approach is bounded by your account's safe daily pace.

Not from the profile itself in most cases. Emails and phone numbers are rarely published on profiles, so reliable contact data comes from a separate consented enrichment provider, not from scraping the profile.

An API on your own account, by a wide margin. Extensions act through your logged-in account at machine speed and tend to get it restricted quickly; an API paces actions like a human, returns structured JSON, and is maintained against LinkedIn UI changes.

Install the SDK (pip install linkedapi), then call fetch_person.execute(...) with the profile URL and the sections you want, and read the result with fetch_person.result(...). See the code above for a full example, including the bulk loop.

Only the limited public version LinkedIn shows logged-out, which omits much of the profile. Full, structured profile data comes through an authenticated session – your own account, driven by an API.

There is no universal number; it depends on your account's age and history. A new account should stay conservative and ramp up, while an established one handles more. Behavior matters more than raw count – pace like a human and stop on warnings.


Need clean LinkedIn profile data by URL or in bulk, without running an extension or a fragile script? Start with Linked API – fetch a profile or a whole list from your own account and get structured JSON back.