LinkedIn Scraping API: A Developer Guide for 2026

Last Updated on September 24, 2026
by InsightSocial Team
11 min read
Follow:
L

If you want LinkedIn data inside your own code, you have two realistic paths: apply to one of LinkedIn's partner programs, or call a third-party API that reads public pages for you. This guide is about the second path. By the end you will have code that reads a member profile, a company page and its posts, runs a filtered people search, walks the pages of a result, and tells you what the whole job cost before you run it.

Not a developer? You don't need any of this. The InsightSocial Chrome extension exports LinkedIn data straight to CSV from the page you're looking at, and our step-by-step guide to scraping LinkedIn profiles walks through it with no code.

Why not just use LinkedIn's official API?

Because it isn't built for reading other people's data. LinkedIn's self-serve permissions (Sign In with LinkedIn using OpenID Connect, and Share on LinkedIn) let your app read the name, headline, photo and email of the member who logged in, and post on their behalf. That's it. Anything broader, such as advertising data, Sales Navigator profile matching or recruiting integrations, sits behind partner programs that LinkedIn approves case by case.

That's the right design for a login button. It's the wrong tool if you need to look up 200 prospects, benchmark five competitors' company pages, or monitor posts about a keyword. For those jobs, developers use a data API that returns public LinkedIn pages as JSON. We compare the options in Best LinkedIn data APIs for developers.

What can you pull from LinkedIn with an API?

The InsightSocial LinkedIn API has 68 endpoints. These are the ones most jobs start with:

What you wantEndpointKey parameterCredits
A member's public profile/v1/linkedin/profileurl100
Their whole background in one call/v1/linkedin/profile/completeurl200
Posts a member published/v1/linkedin/profile/postsurl100
A company page/v1/linkedin/companyurl100
A company from its website/v1/linkedin/company/by-domaindomain100
A company's recent posts/v1/linkedin/company/postscompany_id100
Company + last 10 posts + metrics/v1/linkedin/profile/fullurl (company page)100
Comments on a post/v1/linkedin/post/commentsurl100
People search with filters/v1/linkedin/search/peoplequery200–1,000
Keyword search over posts/v1/linkedin/search/postsquery20–1,120

Prices come from the endpoint pricing table. A range means the endpoint is metered: we hold the top of the range while the call runs and charge what it actually read. See Credits for how that works.

How do I set up the API key?

Sign up, open the API section of the portal, and copy your key. It starts with isk_live_ and is shown once. Every request sends it in one header, x-api-key. There's no OAuth dance and nothing to configure per platform. The quickstart has the full walkthrough.

export INSIGHTSOCIAL_API_KEY="isk_live_..."

The free plan gives you 500 credits a month, and your first 10 calls priced at 200 credits or less cost nothing. That covers the profile, company and post calls below. People search is the exception: a metered call reserves its ceiling before it runs, and search/people holds 1,000 credits, more than the free plan's 500. You'll need Pro or a credit pack for that one, and you're only charged what it actually reads.

How do I scrape a LinkedIn profile?

Pass the full profile URL. Here is the same call in all three languages.

curl -G "https://api.insightsocial.app/v1/linkedin/profile" \
  --data-urlencode "url=https://www.linkedin.com/in/satyanadella/" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"
import os
import requests

BASE = "https://api.insightsocial.app/v1"
HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}

body = requests.get(
    f"{BASE}/linkedin/profile",
    params={"url": "https://www.linkedin.com/in/satyanadella/"},
    headers=HEADERS,
    timeout=60,
).json()

if body["success"]:
    author = body["data"]["author"]
    print(author["display_name"], author["followers"], author["url"])
    print("cost:", body["credits_used"], "left:", body["credits_remaining"])
else:
    print(body["error"]["type"], body["error"]["message"])
const BASE = "https://api.insightsocial.app/v1";
const headers = { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY };

const url = new URL(`${BASE}/linkedin/profile`);
url.searchParams.set("url", "https://www.linkedin.com/in/satyanadella/");

const body = await (await fetch(url, { headers })).json();
if (body.success) {
  const { display_name, followers } = body.data.author;
  console.log(display_name, followers, "cost", body.credits_used);
} else {
  console.log(body.error.type, body.error.message);
}

Every response uses the same envelope: the payload sits under data, and credits_used, credits_remaining and charge_reason tell you what the call cost and why. The field names in data.author are shared across all nine platforms we cover, so a parser written for LinkedIn also works for Instagram or X. The response schema lists every field.

Two cheaper habits worth building early:

  • Repeat calls are free for a while. Re-running the exact same profile call from your account within 24 hours costs 0 credits (charge_reason: "owned"). You don't need your own cache for short-lived retries.
  • Pick the right depth. /profile gives you identity and counts. If you also need experience, education and skills, /profile/complete returns them in one 200-credit call instead of four separate 100-credit calls.

How do I get a company page and its posts?

Company endpoints other than /company itself take a numeric company_id, which you can't read off the page URL. Resolve it once with /company (it comes back as data.author.id), store it, and reuse it.

def get(path, **params):
    return requests.get(f"{BASE}/{path}", params=params,
                        headers=HEADERS, timeout=60).json()

company = get("linkedin/company", url="https://www.linkedin.com/company/hubspot/")
company_id = company["data"]["author"]["id"]
print(company["data"]["author"]["display_name"], "id", company_id)

posts = get("linkedin/company/posts", company_id=company_id, sort_by="recent")
for item in posts["data"]["items"]:
    post = item["post"]
    likes = (post.get("engagement") or {}).get("likes")
    print(post.get("published_at"), likes, post.get("url"))

That's two calls and 200 credits. If all you need is a snapshot for a dashboard, /v1/linkedin/profile/full does both in one 100-credit call. Despite the name, it takes a company page URL and returns the company, its latest 10 posts, and computed engagement figures together.

For a company you only know by its website, /v1/linkedin/company/by-domain?domain=hubspot.com returns the same record, including the company_id.

How do I search LinkedIn people through an API?

/v1/linkedin/search/people takes a keyword plus optional filters. The filters want LinkedIn's own ids, and there are cheap lookup endpoints for each one:

FilterWhere the id comes fromLookup cost
current_company, past_company/v1/linkedin/company (author.id)100
geocode_location/v1/linkedin/search/location20
school/v1/linkedin/search/schools20
industry/v1/linkedin/search/industry20
titleFree text, no lookup0

A search page returns up to 10 people and costs 200 credits. By default the follower figure on each row is LinkedIn's rounded display number, and author.ext.followers_approximate is true. Add include=profile to join each row to that member's full profile in the same call. That adds 80 credits per row it fills, and the page is capped at 1,000 credits. Use limit (1 to 10) to cap both the rows and the extra cost.

const search = new URL(`${BASE}/linkedin/search/people`);
search.searchParams.set("query", "growth marketing");
search.searchParams.set("title", "Head of Growth");
search.searchParams.set("geocode_location", "103644278"); // from /search/location

const page = await (await fetch(search, { headers })).json();
for (const row of page.data.items) {
  console.log(row.author.display_name, row.author.url);
}
console.log("cost", page.credits_used);

How does pagination work?

List endpoints return a pagination object next to data. Send pagination.next_cursor back as the cursor parameter exactly as you got it, and stop when has_more is false. Don't judge completeness by an empty page or a total count. The pagination docs cover the details.

This helper walks any LinkedIn list and stops at a page cap, so a large result can't quietly eat your balance:

import time

def walk(path, max_pages=5, **params):
    cursor, spent = None, 0
    for _ in range(max_pages):
        query = dict(params, **({"cursor": cursor} if cursor else {}))
        res = requests.get(f"{BASE}/{path}", params=query,
                           headers=HEADERS, timeout=120)
        if res.status_code in (429, 503):
            time.sleep(float(res.headers.get("Retry-After", 2)))
            continue  # retry the same cursor
        body = res.json()
        if not body["success"]:
            raise RuntimeError(f'{body["error"]["type"]} {body["request_id"]}')
        spent += body["credits_used"]
        yield from body["data"]["items"]
        page = body.get("pagination") or {}
        if not page.get("has_more"):
            break
        cursor = page["next_cursor"]
    print(f"{path}: {spent} credits")

people = list(walk("linkedin/search/people", max_pages=3,
                   query="data engineer", current_company=company_id))

Each key allows 60 requests a minute and 10 in flight. Going over returns a 429 with a Retry-After header, and a rejected request costs nothing. Pages of one list are sequential because each needs the previous cursor, but you can walk several lists in parallel. The rate limits page explains how to size a job.

What does a real LinkedIn job cost?

Here is a realistic B2B research job, costed from the published prices:

  1. Snapshot 5 competitor company pages with their latest posts: 5 × /profile/full at 100 = 500
  2. Resolve those 5 companies' company_id for filtering: 5 × /company at 100 = 500
  3. Find decision-makers: 5 people-search pages (50 rows), no join, at 200 = 1,000
  4. Pull the full profile for the 25 best matches: 25 × /profile at 100 = 2,500
  5. Read the comments on 5 of the competitors' top posts: 5 × /post/comments at 100 = 500

Total: 5,000 credits. That fits inside one month of the Pro plan (10,000 credits for $9.99/month), with room to run it again. On the free plan's 500 credits you'd get through step 1, or a handful of profile lookups.

Four things bring the real number down:

  • Failed and empty calls cost 0. A private or deleted profile doesn't charge you.
  • Re-runs inside the window are free. Rerun your script the same afternoon and the owned calls cost nothing.
  • Shared-cache hits cost 5 credits. If someone recently fetched the same public page, you get it from our cache.
  • Metered calls settle at actual usage. Search pages often come in under their ceiling.

You can check prices without spending anything: GET /v1/endpoints is public, needs no key, and returns every endpoint's price and cache window as JSON. Some endpoints also accept dry_run=1, which returns an estimate for 0 credits. Your balance is shared with extension exports, and GET /v1/credits shows both kinds of usage. Plans are on the pricing page.

How do I scrape LinkedIn responsibly?

A few rules we'd follow even if nobody enforced them:

  • Stick to public data. These endpoints return public profiles, pages and posts. There's no endpoint for private messages or anyone's inbox, and you never hand over a LinkedIn login.
  • Know the platform's terms. LinkedIn's User Agreement bans scraping and unauthorized automated access to its services. Using a third-party API means you aren't logging in with your own account, but it doesn't make privacy law go away.
  • Collect what the job needs. If you only need job titles for a market map, don't store phone numbers. GDPR and similar laws apply to personal data even when it's public.
  • Give people a way out. If you use the data for outreach, honor opt-outs and delete records you no longer need.

This is general guidance, not legal advice. If LinkedIn data is central to your product, talk to a lawyer.

Can an AI agent write this integration?

Yes. The API is plain REST with one header, so coding agents handle it well. Point Claude, Cursor or another agent at the AI agents page, which lists what to hand it. For the broader picture across platforms, see the social media API overview.

FAQ

Do I need a LinkedIn account to use the API?

No. You authenticate with your InsightSocial API key only. Your own LinkedIn account is never connected, so it can't be flagged for automation.

Can I get email addresses from LinkedIn profiles?

Only what a member chooses to show publicly. /v1/linkedin/profile/contact returns the contact details on the profile, such as websites, phone numbers and a Twitter handle. Most members don't publish an email, and we don't guess one.

How many LinkedIn profiles can I pull per month?

At 100 credits per profile, the free plan's 500 credits covers about 5 (plus your 10 free calls), and Pro's 10,000 covers about 100. For more, buy credit packs, which never expire. Rate limits allow 60 requests per minute per key.

Why does people search return rounded follower counts?

Search rows carry the figure LinkedIn displays in results, which is rounded. Add include=profile to get the exact count on each row. author.ext.followers_approximate tells you which kind you have.

What happens when a call fails?

You aren't charged. The response has success: false, credits_used: 0 and an error.type to branch on. Retry only transient types such as RATE_LIMITED or SERVICE_UNAVAILABLE. The rest fail the same way until you change the request.

Is this the same data as the Chrome extension?

It's the same LinkedIn and the same credit balance, but a different way in. The extension captures what's on your screen as you browse. The API fetches public pages on request from your server. Use the extension for one-off exports and the API for anything scheduled or built into a product.

#api#linkedin#developers#python#nodejs#scraping