LinkedIn

How to Scrape LinkedIn Data with an API

Pull LinkedIn people and company pages as JSON via Social Fetch — plus DIY Voyager scraping and partner API limits.

Social FetchUpdated

This guide covers LinkedIn people and company lookups via Social Fetch's /v1/linkedin/** routes, with runnable examples below. It also explains what DIY browser scraping and LinkedIn's partner APIs actually involve on this platform. For the generic build-vs-buy comparison, see Social Fetch vs DIY scraping.

The shortest path is a single request:

Request
curl -sS \
  -H "x-api-key: $SOCIALFETCH_API_KEY" \
  -G "https://api.socialfetch.dev/v2/linkedin/profiles" \
  --data-urlencode "handle=williamhgates"

You'll need an API key and curl or the TypeScript SDK. New to the API? Start with the Quickstart.

DIY browser scraping on LinkedIn

A plain fetch() to linkedin.com gets blocked almost immediately — LinkedIn inspects TLS fingerprints, cookies, and session state. Public cards often sit behind auth walls or challenge pages; structured fields live in Voyager-style JSON that changes without notice.

Example
python
from playwright.async_api import async_playwright
import asyncio, random

async def scrape_linkedin_profile(profile_url: str):
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False,
            proxy={
                "server": "http://residential-proxy.example:9000",
                "username": "user",
                "password": "pass",
            },
        )
        context = await browser.new_context(
            user_agent=pick_real_user_agent(),
            locale="en-US",
            timezone_id="America/New_York",
        )
        # Many teams also inject a reused LinkedIn session cookie here.
        # That path breaks on auth checkpoints and ToS risk stays on you.
        page = await context.new_page()

        await page.goto(profile_url, wait_until="networkidle")
        await asyncio.sleep(random.uniform(2.0, 6.0))

        # Public cards still hide behind auth walls, challenge pages, and
        # Voyager JSON that changes without notice. HTML alone is rarely enough.
        html = await page.content()
        await browser.close()
        return html

A working DIY pipeline also needs:

  • Residential or mobile proxies. Datacenter IPs are flagged within a few hits.
  • Session reuse and checkpoint recovery. Cookie injection inherits auth challenges, CAPTCHA loops, and account risk. Every checkpoint sends you back to babysit a browser.
  • Page-type traps. /in/…, /company/…, and school/organization URLs are different entities. A single selector that ignores page type forces your code to guess.
  • Behavioral pacing. Fixed intervals read as a bot, so you add randomized delays and cap concurrency.

LinkedIn quietly reshapes cards and tightens walls. A scraper that worked Tuesday returns challenge HTML Friday.

LinkedIn official APIs

LinkedIn's developer platform fits apps inside LinkedIn's ecosystem — member login, partner features LinkedIn has approved, or Marketing Developer Platform products with matching scopes:

  • Partner / member-authorized APIs expose data LinkedIn grants your app for approved use cases and members who authorize access.
  • Marketing Developer Platform (MDP) covers LinkedIn-approved marketing and advertising workflows — not arbitrary public CRM enrichment without that relationship.
  • Official products do not unlock "any profile on the internet." They unlock what LinkedIn authorizes for your app and members.

Check LinkedIn Developers before building — terms change often.

Using the Social Fetch API

Social Fetch exposes LinkedIn under documented /v1/linkedin/** routes — people profiles, companies, organizations/schools, posts, people search, jobs, Ad Library, and transcripts where supported. One header authenticates every call; responses share the same data + meta envelope across platforms.

Request
TypeScript
const = await client.tiktok.({
handle: "charlidamelio",
});
if (result.ok) {
const { profile } = result.value.data;
}
Response
JSON
{
"data": {
"lookupStatus": ,
"profile": {
"handle": "charlidamelio",
"displayName": "Charli D'Amelio",
},
"metrics": {
"followers": 155200000
}
},
"meta": {
"creditsCharged":
}
}

Hover underlined tokens for details.

Get a person profile

The same lookup in cURL, the TypeScript SDK, Python, and more — switch tabs to match your stack:

Request
const params = new URLSearchParams({"handle":"williamhgates"});

const response = await fetch(
  `https://api.socialfetch.dev/v2/linkedin/profiles?${params.toString()}`,
  {
    headers: {
      "x-api-key": process.env.SOCIALFETCH_API_KEY,
    },
  }
);

const body = await response.json();

console.log(response.status, body);

Person profiles are 3 credits per completed lookup. Batch up to 50 /in/… URLs on GET /v2/linkedin/profiles/batch. Charge includes a verified miss — check lookupStatus. Pass a company URL into the person route and you will not get a company card; use the company or organization endpoints instead.

Get a company page

Request
const params = new URLSearchParams({"url":"https://www.linkedin.com/company/microsoft"});

const response = await fetch(
  `https://api.socialfetch.dev/v1/linkedin/companies?${params.toString()}`,
  {
    headers: {
      "x-api-key": process.env.SOCIALFETCH_API_KEY,
    },
  }
);

const body = await response.json();

console.log(response.status, body);

Company page lookups are 1 credit on a completed request. Schools and mixed organization page types use GET /v2/linkedin/organizations. Full parameters: Get LinkedIn profile and Get LinkedIn company page. Every LinkedIn operation is in the LinkedIn API reference.

People search and metered lists

Some LinkedIn routes are flat credits; others are metered by returned records. Read the operation page before you wire a loop:

RouteBilling shape (completed lookup)
Person profiles3 credits per completed lookup or URL
Company page1 credit
Company posts1 credit flat per page
People search3 credits per completed page
Profile posts list3 credits per completed page

People search (GET /v2/linkedin/people/search) takes keyword. Empty result pages still bill when the lookup completes. Preflight reserves for the documented worst case; meta.creditsCharged is what actually billed. Details: Search LinkedIn people and Credits.

Reading the response

HTTP responses for a single person profile are { data, meta } with data.lookupStatus and data.profile. URL batches use a results[] entry per requested URL. A successful single lookup looks like this:

Response
json
{
  "data": {
    "results": [
      {
        "url": "https://www.linkedin.com/in/williamhgates",
        "lookupStatus": "found",
        "profile": {
          "displayName": "Bill Gates",
          "headline": "Chair, Gates Foundation",
          "profileUrl": "https://www.linkedin.com/in/williamhgates"
        },
        "metrics": {
          "connections": null,
          "followers": 35000000
        }
      }
    ],
    "summary": {
      "requestedUrls": 1,
      "found": 1,
      "notFound": 0,
      "errored": 0
    }
  },
  "meta": {
    "requestId": "req_01example",
    "creditsCharged": 2,
    "version": "v1"
  }
}

The TypeScript SDK's client.linkedin.getProfile({ handle }) calls the v2 single-profile route. An HTTP 200 does not guarantee lookupStatus: "found". Branch before you upsert. Every response carries meta.requestId for support tracing. See Errors.

Social Fetch only returns publicly visible data. Content gated beyond public visibility is out of scope — you get a typed status, not invented private fields.

What you can build

  • CRM enrichment — resolve public /in/… and /company/… cards into structured rows before a sales or CS handoff.
  • Account research — pull company pages and recent company posts for pipeline notes without running a residential browser farm.
  • Go-to-market discovery — use people search when you need name-based discovery, with eyes open on metered pricing and latency.

For the same identity pattern across LinkedIn and creator networks, see Cross-platform creator profiles.

FAQ

It depends on your jurisdiction, what data you collect, and how you use it. Collecting public, business-level metrics for B2B analytics is a common pattern, and courts (e.g. hiQ v. LinkedIn) have generally treated public-web data as fair game in several jurisdictions. You remain responsible for LinkedIn's terms, applicable privacy law, and your own contracts. This is a technical guide, not legal advice — talk to counsel if you're collecting at scale.

Can't I just use LinkedIn's official API?

Not for arbitrary public /in/… or /company/… enrichment. Partner and MDP products are for LinkedIn-approved, member-authorized apps. For public-page enrichment without that access, a data API is the usual fit. See LinkedIn official APIs.

How fresh is the data?

Each request fetches live from LinkedIn at call time — there's no cache returning an hour-old snapshot. If a number looks off, meta.requestId lets support trace that specific lookup.

What happens if a profile is missing or gated?

Check lookupStatus on each result. Don't treat a 200 alone as "data is present." Content beyond public visibility is out of scope. Details in Errors.

How are credits charged?

Credits charge when a lookup completes. Person profiles are 2 credits per URL requested. Company pages are 1 credit. People search and profile-post lists are metered (2 attempt fee + 2 per returned record). Validation failures and failures that never complete a lookup do not bill the same way — see Credits. Always reconcile against meta.creditsCharged.

How does this compare to other providers?

See the side-by-side comparisons: vs Apify, vs Bright Data, vs Proxycurl, the full compare hub, and Best LinkedIn data APIs in 2026.


Next steps: Quickstart · LinkedIn platform hub · LinkedIn API reference · Pricing

Share this article