---
title: "LinkedIn profile scraper"
description: "Scrape public LinkedIn profiles as JSON: name, location, current company, photo, work history and recent posts. One API request from Python, Node.js, the CLI or MCP."
canonical: https://www.zenrows.com/scrape/linkedin-profile
updated: 2026-10-20
---

# LinkedIn profile scraper

The Zenrows LinkedIn profile scraper turns a public LinkedIn profile URL into JSON: full name, location, vanity URL, profile photo, connection count label, current company, experience, education, languages and recent posts with likes. Fetch in Adaptive Stealth Mode opens the profile as a logged-out visitor sees it, and Extract returns the fields. Run it from an API call, the CLI or an AI agent.

- Site: linkedin.com
- Returns: Public profile fields, current company and recent posts

## Run it: one public LinkedIn profile in, JSON out

```json Input
{
  "url": "https://www.linkedin.com/in/williamhgates",
  "mode": "auto",
  "extract": "auto"
}
```

```json Output
{
  "member": {
    "full_name": "Bill Gates",
    "location": "Seattle, Washington, United States",
    "vanity_url": "https://www.linkedin.com/in/williamhgates",
    "connection_count_label": "8",
    "follower_count": null,
    "profile_photo_url": "https://media.licdn.com/dms/image/v2/D5603AQF-RYZP55jmXA/profile-displayphoto-shrink_200_200/…"
  },
  "current_position": {
    "company_name": "Gates Foundation",
    "company_linkedin_url": "https://www.linkedin.com/company/gates-foundation?trk=public_profile_topcard-current-company",
    "job_title": null
  },
  "experience": [],
  "education": [],
  "languages": [],
  "posts": [
    {
      "text": "Some of the fuel powering future flights could start with algae. A Breakthrough Energy company called Phykion is developing highly productive, oil-rich algae to use as sustainable aviation fuel…",
      "like_count": 185,
      "post_url": "https://www.linkedin.com/posts/williamhgates_some-of-the-fuel-powering-future-flights-activity-7518110626762768384-mNGV",
      "date_published": null
    }
  ]
}
```

Request and output of a live call to a public LinkedIn profile on Oct 20, 2026, trimmed for length. This member does not show work history or education to logged-out visitors, so those lists came back empty.

## What data can you scrape from a LinkedIn profile?

A LinkedIn profile URL returns what LinkedIn shows a logged-out visitor: the header card, the current company, whichever sections the member made public, and their recent activity. Each member chooses how much of that is public, so the same fields can be full on one profile and empty on the next.

| Field | What it holds | Example |
|---|---|---|
| `member.full_name` | Name on the profile | `Bill Gates` |
| `member.location` | Location shown in the header | `Seattle, Washington, United States` |
| `member.vanity_url` | The profile's canonical URL | `https://www.linkedin.com/in/williamhgates` |
| `member.connection_count_label` | Connection count as LinkedIn prints it | `8` |
| `member.follower_count` | Followers, when the profile shows them | `null` on this profile |
| `member.country_code` | Country code, when the page exposes it | `null` on this profile |
| `member.profile_photo_url` | Profile photo image | `https://media.licdn.com/dms/image/v2/…` |
| `member.cover_image_url` | Banner image | `https://media.licdn.com/dms/image/v2/…` |
| `current_position.company_name` | Current employer from the header | `Gates Foundation` |
| `current_position.company_linkedin_url` | That employer's LinkedIn page | `https://www.linkedin.com/company/gates-foundation?…` |
| `current_position.job_title` | Current title, when shown | `null` on this profile |
| `experience[]` | Public work history entries | Empty on this profile |
| `education[]` | Public education entries | Empty on this profile |
| `languages[]` | Languages the member lists | Empty on this profile |
| `posts[].text` | Text of recent posts and reposts in the activity section | `Some of the fuel powering future flights…` |
| `posts[].like_count` | Likes on each post | `185` |
| `posts[].post_url` | Link to the post, ready for the [LinkedIn post scraper](/scrape/linkedin-posts) | `https://www.linkedin.com/posts/williamhgates_…` |

## How to scrape LinkedIn profiles with Zenrows

1. **Create a free Zenrows account** and copy your API key from the dashboard.
2. **Collect public profile URLs** in the form `https://www.linkedin.com/in/<vanity>`. Use the vanity URL, not a Sales Navigator or search result link, which need a login.
3. **Send each URL with the default configuration**: Adaptive Stealth Mode to open the profile and Extract to return the fields.
4. **Read `parsed`** and treat empty lists and `null` values as "the member doesn't show this publicly". Ask for only the paths you need to keep the record small.
5. **Run your list in Batch** with the vanity name as your ID, then refresh it on a schedule.

```python Python
# pip install requests
import requests

params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": "https://www.linkedin.com/in/williamhgates",
    "mode": "auto",      # Adaptive Stealth Mode
    "extract": "auto",   # structured JSON
}
response = requests.get("https://api.zenrows.com/v1/", params=params, timeout=180)
response.raise_for_status()

profile = response.json()["parsed"]
member, current = profile["member"], profile["current_position"]
print(member["full_name"], "|", member["location"], "|", current["company_name"])
for post in profile.get("posts", [])[:5]:
    print(post.get("like_count"), (post.get("text") or "")[:80])
```

```javascript Node.js
// Node 18+ (built-in fetch)
const params = new URLSearchParams({
  apikey: "YOUR_ZENROWS_API_KEY",
  url: "https://www.linkedin.com/in/williamhgates",
  mode: "auto",
  extract: "auto",
});

const response = await fetch(`https://api.zenrows.com/v1/?${params}`, {
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);

const { parsed } = await response.json();
console.log(parsed.member.full_name, parsed.current_position.company_name, parsed.experience.length);
```

```bash cURL
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fwilliamhgates&mode=auto&extract=auto"

# Only the fields you need
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fwilliamhgates&mode=auto&extract=auto&extract_fields=member.full_name%2Cmember.location%2Ccurrent_position.company_name%2Cexperience"
```

```python Python SDK
# pip install zenrows
from zenrows import ZenRowsClient

client = ZenRowsClient("YOUR_ZENROWS_API_KEY")
for url in ["https://www.linkedin.com/in/williamhgates", "https://www.linkedin.com/in/satyanadella"]:
    response = client.get(url, params={"mode": "auto", "extract": "auto"})
    parsed = response.json()["parsed"]
    print(parsed["member"]["full_name"], "->", parsed["current_position"]["company_name"])
```

## Input

The scraper takes one public profile URL per request. A Batch job applies the same options to every profile, so the input for a list is the vanity URLs plus the settings below.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | string | required | A public profile, `https://www.linkedin.com/in/<vanity>`. Country subdomains such as `uk.linkedin.com/in/…` work too |
| `mode` | string | `auto` | Adaptive Stealth Mode: starts with the lightest setup and adds rendering and protected-site handling only when the page needs them |
| `extract` | string | `auto` | Returns the profile as JSON in `parsed`, next to the raw `html` |
| `extract_fields` | string | all fields | Comma-separated paths to keep, such as `member.full_name,current_position,experience` |
| `proxy_country` | string | any | Two-letter country code, optional. Set it when you want the request to come from a specific country |
| `wait_for` | string | none | A CSS selector to wait for before the page is read |

```json Batch input
{
  "type": "regular",
  "status": "closed",
  "zenrows_params": {
    "mode": "auto",
    "extract": "auto"
  },
  "tasks": [
    { "url": "https://www.linkedin.com/in/williamhgates", "external_id": "williamhgates" },
    { "url": "https://www.linkedin.com/in/satyanadella", "external_id": "satyanadella" },
    { "url": "https://www.linkedin.com/in/reidhoffman", "external_id": "reidhoffman" }
  ]
}
```

## Output

Every response is JSON with two keys: `html`, the profile as LinkedIn served it, and `parsed`, the structured record. The sample below is a trimmed live output; the activity section returned eleven posts and reposts, two of which are shown.

```json Public profile
{
  "current_position": {
    "company_linkedin_url": "https://www.linkedin.com/company/gates-foundation?trk=public_profile_topcard-current-company",
    "company_logo_url": null,
    "company_name": "Gates Foundation",
    "job_title": null
  },
  "education": [],
  "experience": [],
  "languages": [],
  "member": {
    "connection_count_label": "8",
    "country_code": null,
    "cover_image_url": "https://media.licdn.com/dms/image/v2/D5616AQEjhPbTCeblYg/profile-displaybackgroundimage-shrink_200_800/…",
    "follower_count": null,
    "full_name": "Bill Gates",
    "location": "Seattle, Washington, United States",
    "profile_photo_url": "https://media.licdn.com/dms/image/v2/D5603AQF-RYZP55jmXA/profile-displayphoto-shrink_200_200/…",
    "vanity_url": "https://www.linkedin.com/in/williamhgates"
  },
  "posts": [
    {
      "date_published": null,
      "like_count": 865,
      "post_url": "https://www.linkedin.com/posts/williamhgates_made-in-africa-new-measles-rapid-diagnostic-activity-7517390588447621121-fbmC",
      "text": "With measles outbreaks rising around the world, catching cases early makes a huge difference…"
    },
    {
      "date_published": null,
      "like_count": 1796,
      "post_url": "https://www.linkedin.com/posts/williamhgates_it-was-great-to-be-back-in-kemmerer-wyoming-activity-7516684739454484480-vuhP",
      "text": "It was great to be back in Kemmerer, Wyoming, today to see the progress on TerraPower's Natrium plant…"
    }
  ]
}
```

The `posts` list mixes the member's own posts with reposts from other pages. Check the account in each `post_url` (`/posts/williamhgates_…` versus another name) to tell them apart.

## Scrape LinkedIn profiles from your coding agent

Claude Code, Cursor, Codex and any MCP client can read a public LinkedIn profile from a prompt.

The agent sends the same Fetch and Extract request shown above and reads the same JSON. Connect the Zenrows MCP server once, or install the CLI and let the agent call it in the terminal.

```bash Connect the MCP server (Claude Code)
claude mcp add zenrows -e ZENROWS_API_KEY=YOUR_ZENROWS_API_KEY -- npx -y @zenrows/mcp
```

```json MCP config (Cursor, Claude Desktop, Windsurf)
{
  "mcpServers": {
    "zenrows": {
      "command": "npx",
      "args": ["-y", "@zenrows/mcp"],
      "env": { "ZENROWS_API_KEY": "YOUR_ZENROWS_API_KEY" }
    }
  }
}
```

```bash Or run the CLI
npx -y @zenrows/cli init
zenrows extract "https://www.linkedin.com/in/williamhgates"
```

```text Then ask your agent
Use Zenrows to read this public LinkedIn profile and tell me the
person's current company and location, then summarize the topics of
their five most liked recent posts:
https://www.linkedin.com/in/williamhgates
```

## Scrape LinkedIn profiles at scale with Batch and Crawl

**Batch** runs a list of profile URLs as one job, in parallel, with your own ID on each result. Use the vanity name as the ID, so each result joins back to your research or speaker list without parsing.

**Crawl** collects a member's public activity. Start it on the profile, keep the links that contain `/posts/`, and it returns every post page it reaches. Send those URLs to the [LinkedIn post scraper](/scrape/linkedin-posts) for the full text, date, comments and engagement.

```bash Batch (cURL)
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "regular",
    "status": "closed",
    "zenrows_params": { "mode": "auto", "extract": "auto" },
    "tasks": [
      { "url": "https://www.linkedin.com/in/williamhgates", "external_id": "williamhgates" },
      { "url": "https://www.linkedin.com/in/satyanadella", "external_id": "satyanadella" }
    ]
  }'

# Read the results when the job finishes
curl "https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY"
```

```bash Batch (CLI)
# profiles.jsonl: one {"url": "...", "external_id": "...", "extract": "auto"} per line
zenrows batch create profiles.jsonl --wait
zenrows batch results <job-id> --out linkedin-profiles.jsonl
```

```bash Crawl (cURL)
curl -X POST "https://api.zenrows.com/v1/crawls" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.linkedin.com/in/williamhgates",
    "depth": 1,
    "max_pages": 30,
    "include_patterns": ["/posts/williamhgates_"]
  }'
```

**Scheduled jobs** run the same Batch job on a timetable, on Zenrows' side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can tell your app when a run finishes, and you can pause or resume the schedule at any time, with no cron server to keep alive.

```bash Scheduled job (cURL)
# Runs every day on Zenrows' side; each run keeps its own results
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "type": "scheduled",
      "status": "closed",
      "schedule": {
        "rate": {
          "every": 1,
          "unit": "day"
        }
      },
      "zenrows_params": {
        "mode": "auto",
        "extract": "auto"
      },
      "tasks": [
        {
          "url": "https://www.linkedin.com/in/williamhgates",
          "external_id": "linkedin-profile"
        }
      ]
    }'

# Or at set times: "schedule": { "calendar": { "times_of_day": ["09:00"],
#   "cadence": { "weekly": { "days": ["mon", "thu"] } } }, "timezone": "Europe/Madrid" }
```

## What teams do with LinkedIn profile data

| Use case | Data they pull | Who runs it |
|---|---|---|
| Speaker and expert research | Location, current company and recent posts for a known list of people | Event organizers, journalists and analysts |
| Thought leadership tracking | Recent posts and likes from executives in a market | Marketing and comms teams |
| Talent market research | Titles, employers and education across a field, in aggregate | Economists, workforce analysts and HR researchers |
| Candidate research for open roles | Public work history and education of applicants who shared their profile | Recruiters and hiring managers |

## Tips for scraping LinkedIn profiles

- **Use the vanity URL.** `https://www.linkedin.com/in/<vanity>` is the public form. Links from LinkedIn search, Sales Navigator or a feed need a login and don't open for logged-out visitors.
- **Strip tracking parameters.** Profile links copied from company pages carry `?trk=org-employees` and similar. Remove them before you store or dedupe.
- **Expect sparse profiles.** What a logged-out visitor sees is the member's choice. Write your code so an empty `experience` list or a `null` title is a normal result, not an error.
- **Date every run.** Store each run with its date, so a study of a field or a speaker list always says when the data was read.
- **Read reposts carefully.** The activity list includes posts the member reshared. The account name in each `post_url` tells you who wrote it.
- **Keep only what you need.** List the paths you use, such as the name, location and current company, and leave photos and post text out of your store if they have no purpose in your workflow.

## Frequently asked questions

### Is it legal to scrape LinkedIn profiles?

Public profiles hold personal data, so the rules for personal data apply: the GDPR for anyone in the EU or UK, the CCPA in California, and similar laws elsewhere. You need a lawful basis such as legitimate interest, you must tell people when you process their data, and you should collect only the fields you use. LinkedIn's User Agreement also restricts automated collection. Check its terms for your use case and ask your lawyer.

### Is there an official LinkedIn profile API?

LinkedIn's API returns a profile only for the member who signs in to your app, through "Sign In with LinkedIn" with OpenID Connect, and that covers name, photo and email of that member alone. Broader profile data is limited to approved partner programs. There is no API that returns any public profile on request.

### What does a logged-out visitor see on a LinkedIn profile?

The header (name, location, photo, current company), the connection count label and the recent activity, plus whichever of experience, education and languages the member has made public. Contact details, full connection lists and anything restricted to members are never shown, and the scraper returns none of them.

### Can I get emails or phone numbers from LinkedIn profiles?

No. LinkedIn does not show contact details on public profiles, and Zenrows does not look for them. This scraper returns public professional data only.

### Do I need a LinkedIn account or session cookie?

No. Zenrows opens each profile as a logged-out visitor, so you never share an account, a cookie or a password, and none of your accounts can be restricted.

### How do I scrape LinkedIn profiles with Python?

Send a GET request to https://api.zenrows.com/v1/ with your API key, the profile URL, Adaptive Stealth Mode and Extract turned on, as in the Python example above. The parsed key holds the profile as a dictionary. The zenrows package on PyPI wraps the same request.

### Can AI agents scrape LinkedIn profiles with Zenrows?

Yes. Connect the Zenrows MCP server to Claude, Cursor, Codex or any MCP client, or give your agent the Zenrows CLI. Give it a public profile URL and a question, and the agent calls Fetch and Extract for you.

### Can I scrape LinkedIn on a schedule?

Yes. Create a scheduled Batch job with your LinkedIn URLs and the same settings as a single request, and Zenrows runs it on its side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can notify your app when a run finishes, and the schedule can be paused or resumed at any time.

### Which integrations work with the LinkedIn profile scraper?

Zenrows works with n8n, Make, Zapier, Clay, Dify, LangChain, LlamaIndex, CrewAI and Mastra, plus the Python, Node.js and Go SDKs and any HTTP client. An n8n or Make workflow can refresh a speaker or research list on a schedule. See the integrations page for the full list.

### What format does the data come in?

JSON by default, with the structured profile in parsed and the original page in html . You can also ask for Markdown or plain text when the profile goes to an LLM.

## More LinkedIn scrapers

- [LinkedIn scraper](https://www.zenrows.com/scrape/linkedin.md)
- [LinkedIn jobs scraper](https://www.zenrows.com/scrape/linkedin-jobs.md)
- [LinkedIn post scraper](https://www.zenrows.com/scrape/linkedin-posts.md)

All scrapers: https://www.zenrows.com/scrape. Start free: https://app.zenrows.com/register. Docs: https://docs.zenrows.com
