LinkedIn

How to Scrape LinkedIn Data with an API (2026)

DIY browsers vs LinkedIn partner/MDP APIs vs a data API for public people and company pages — tradeoffs and runnable code.

Social FetchUpdated

How to Scrape LinkedIn Data with an API (2026)

DIY browsers vs LinkedIn partner/MDP APIs vs a data API for public people and company pages — tradeoffs and runnable code.

There are three ways to pull public LinkedIn data into a product: drive your own headless browser, use LinkedIn's partner / Marketing Developer Platform APIs, or call a LinkedIn data API that hands back normalized JSON. LinkedIn's official APIs are partner- and member-authorized. A DIY LinkedIn scraper is a maintenance treadmill of auth walls and checkpoint challenges. For most enrichment pipelines, a data API is the pragmatic way to get public people and company pages as clean JSON. This guide compares all three — what each one really costs, where it breaks, and which one fits your project — with runnable code for every path, including the ones we don't sell.

The shortest path is a single request:

Request
curl -sS \
  -H "x-api-key: $SOCIALFETCH_API_KEY" \
  -G "https://api.socialfetch.dev/v1/linkedin/profiles" \
  --data-urlencode "url=https://www.linkedin.com/in/williamhgates"

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

The three ways to get LinkedIn data

Every approach is some version of these three. The right one depends on volume, whether you need member OAuth, and how much maintenance you want to own.

ApproachBest whenThe real cost
DIY (Puppeteer / Playwright)A one-off research pull, internal tooling, or you specifically want to own the pipelineResidential proxies, reused sessions, auth checkpoints — and re-fixing it every time LinkedIn tightens the wall
LinkedIn partner / MDP APIsYou already have approved access and your need maps to member-authorized scopesPartner process, app review, and product constraints — arbitrary public enrichment is usually not the contract
Data API (Social Fetch)You need public /in/… and /company/… cards, posts, or search in product code now, ideally across more than one platformMetered per completed lookup (and per returned record on some list routes); you depend on a provider to keep the scrapers working

For most production apps that need public enrichment on demand, the data API is the fastest route to reliable JSON — one REST surface across LinkedIn, Instagram, TikTok, and the rest, with the same auth header and the same data + meta envelope. DIY is genuinely worth it for a weekend research scrape or to learn the internals. Official APIs are the right call when you're already inside LinkedIn's partner programs for member-authorized experiences.

The rest of this guide covers each path, starting with the one most teams try first.

Why DIY scraping turns into a maintenance project

A plain fetch() to linkedin.com gets blocked almost immediately — LinkedIn's defenses inspect TLS fingerprints, cookies, and session state that a stock HTTP client does not match. So a working scraper isn't a script; it's a small system you build and then maintain indefinitely. Here's roughly what the browser-driven version looks like:

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

That snippet only gets you raw HTML. Public cards often sit behind auth walls or challenge pages, and the structured fields live in Voyager-style JSON that changes without notice. And around it you're standing up:

  • A residential or mobile proxy pool. Datacenter IPs are flagged within a few hits, so you need real ones with sticky, region-matched rotation.
  • Session reuse and checkpoint recovery. Teams that inject cookies to look "logged in" inherit 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 "LinkedIn scrape" 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 — slowing the whole thing down.

The build is the easy part. The maintenance is worse: LinkedIn quietly reshapes cards and tightens walls, so a scraper that worked Tuesday returns challenge HTML Friday — and you hear it from a customer, not a test. For a one-off pull that's a fine weekend. For anything customer-facing, you've signed up for a maintenance subscription paid in your own evenings. For the broader build-vs-buy math, see Social Fetch vs DIY scraping.

Why LinkedIn's official APIs won't cover it

LinkedIn's developer platform is the right answer when your app lives inside LinkedIn's ecosystem — member login, partner features LinkedIn has approved, or Marketing Developer Platform products with the matching scopes. None of these are a general "look up this public /in/… URL" enrichment tool:

  • 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 magically unlock "any profile on the internet." They unlock what LinkedIn authorizes for your app and members.

So if you need public-page enrichment in a generic backend without partner access, official APIs leave a gap by design. Reach for them when your use case maps to LinkedIn-approved, member-authorized work; reach for a data API when you need publicly visible people and company cards without that process. Terms change often — check LinkedIn Developers before building against any of them.

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, and 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({"url":"https://www.linkedin.com/in/williamhgates"});

const response = await fetch(
  `https://api.socialfetch.dev/v1/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 2 credits per URL requested (batch up to 50 URLs in one call). Charge is per URL in the request, not only per found result — check each item's lookupStatus. Pass a company URL into the person route and you will not get a company card; use the company endpoint below 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 /v1/linkedin/organizations. Full parameters: Get LinkedIn profiles and Get LinkedIn company page. Every LinkedIn operation is in the 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 profiles2 credits per URL requested
Company page1 credit
Company posts1 credit flat per page
People search2 attempt fee + 2 per returned person
Profile posts list2 attempt fee + 2 per returned post

People search (GET /v1/linkedin/people/search) takes firstName and/or lastName — at least one is required. Empty result sets still pay the attempt fee 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 person profiles are a batch envelope: one results[] entry per requested URL, plus a summary. A successful single-URL call 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 getProfile unwraps the first result so data.lookupStatus sits at the top level of that helper's return value. Either way: an HTTP 200 does not guarantee lookupStatus: "found". Branch before you upsert. Every response also carries meta.requestId, so if a number ever looks wrong, support can trace the exact lookup. 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 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 Why LinkedIn's official APIs won't cover it.

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 upstream 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