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:
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.
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.
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 htmlA 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.
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:
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
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:
| Route | Billing shape (completed lookup) |
|---|---|
| Person profiles | 2 credits per URL requested |
| Company page | 1 credit |
| Company posts | 1 credit flat per page |
| People search | 2 attempt fee + 2 per returned person |
| Profile posts list | 2 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:
{
"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 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
Is scraping LinkedIn legal?
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 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