Linkedin Profile Post Scraper avatar

Linkedin Profile Post Scraper

Pricing

$19.99/month + usage

Go to Apify Store
Linkedin Profile Post Scraper

Linkedin Profile Post Scraper

Extract public posts from LinkedIn profiles with this reliable LinkedIn Profile Post Scraper. Collect post text, dates, reactions, comments count, media links, and engagement metrics in structured JSON. Ideal for lead research, content analysis, and competitive monitoring at scale.

Pricing

$19.99/month + usage

Rating

5.0

(3)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

53

Total users

4

Monthly active users

4 days ago

Last modified

Share

LinkedIn Profile Post Scraper — Posts, Likes and Comments

LinkedIn Profile Post Scraper extracts posts, comments, and reactions from any public LinkedIn profile (linkedin.com/in/...) and returns them as structured JSON — no login, no li_at cookie, and no LinkedIn account required. Each run pushes post text, engagement counts (numLikes, numComments), author details, and post URNs to the dataset live, as they're found, alongside every comment and reaction as its own labeled row. Paste a list of profile URLs below to start.

What is LinkedIn Profile Post Scraper?

LinkedIn Profile Post Scraper is an Apify Actor that crawls the public post history of one or more LinkedIn personal profiles and returns each post — plus its comments and reactions — as typed JSON rows. It reads only publicly visible profile pages: no LinkedIn account, no login, and no li_at session cookie are used anywhere in the code. It's built for sales and growth teams tracking prospects' activity, researchers and analysts building LinkedIn content datasets, and developers or AI engineers who need normalized post data without maintaining their own scraping and proxy stack.

What LinkedIn post data is publicly available to scrape?

Anyone who opens a LinkedIn profile page without logging in can already see its recent public posts, the author's name and headline, post images, and visible like/comment counts — LinkedIn Profile Post Scraper reads exactly that surface, nothing more.

Data categoryPublicly availableRestricted (requires login)
Post text and link
Post images
Author name, headline
Visible like/comment counts
Share count✅ not exposed to anonymous visitors at all
Comment text and commenter name✅ (best-effort from markup)
Reactor name and reaction type✅ (best-effort from markup)Full reactor list — login
Whether a post is a repost✅ not detectable anonymously
Comment/share permission settings✅ login + connection

LinkedIn Profile Post Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login wall.

What data can I extract with LinkedIn Profile Post Scraper?

Every run returns three row types — post, comment, and reaction — sharing a common set of fields, with posts carrying extra fields (nested author object, permission flags) that only apply at the post level.

FieldDescription
urnPost's LinkedIn activity URN, e.g. urn:li:activity:7123456789012345678. null on comment/reaction rows.
rowType"post", "comment", or "reaction" — what this row represents.
isChildtrue for comment/reaction rows nested under a post; false for the post row itself.
parentIdThe parent post's urn. null on post rows.
typePost rows: "image" or "text". Reaction rows: the reaction type (LIKE, LOVE, CELEBRATE, SUPPORT, FUNNY, INSIGHTFUL). Comment rows: "comment".
urlPost permalink, or the comment's permalink on comment rows. null on reaction rows.
textPost body or comment text. null on reaction rows.
authorFullNamePost author's name, or the commenter/reactor's name on child rows.
authorHeadlineAuthor's headline/description, read from the page's JSON-LD.
authorProfileIdProfile URL slug of the post author, commenter, or reactor (e.g. satyanadella).
postedAtTimestamp / postedAtISOPublish time as epoch ms / ISO 8601. On comment rows, postedAtTimestamp is the scrape time (the anonymous markup doesn't expose the original comment timestamp) and postedAtISO is null.
numLikes / numCommentsReaction and comment counts on a post. null on child rows.
image / imagesFirst image URL and up to 5 image URLs on a post. null / [] on child rows.
scrapedAtISO 8601 timestamp of when the row was fetched.

Post identity, author and content fields

  • authorType"Person" or "Company", detected from JSON-LD.
  • authorProfileUrl — the profile URL that was scraped.
  • attributes@mention entities parsed from post text and markup.
  • author — nested object: firstName, lastName, occupation, id, publicId, trackingId, profileId, picture, backgroundImage.
  • authorName, authorTitle — convenience copies of authorFullName / authorHeadline.

Engagement, timing and permission fields

  • timeSincePosted — relative age string ("2d", "3w", "1mo"), computed from postedAtISO.
  • numShares — always null: LinkedIn does not expose a share count on the anonymous post markup this Actor reads.
  • isRepost — always null: repost status isn't detectable from the anonymous page.
  • ⚠️ canReact, canPostComments, canShare, commentingDisabled, allowedCommentersScope, rootShare, shareAudience — always null on every row. These permission flags only exist behind an authenticated LinkedIn session; the Actor returns null, never a guessed value.
  • commentsTruncated / commentsComplete / reactionsTruncated — booleans flagging whether the nested arrays below captured all of numComments / numLikes, or were cut short.

Nested comment and reaction fields

  • comments — array of comment objects nested on the parent post row (text, link, author{firstName,lastName,...}, time), in addition to being emitted as their own comment child rows.
  • reactions — array of reaction objects nested on the parent post row (type, profile{firstName,lastName,...}), in addition to being emitted as their own reaction child rows.

🤖 Add-on: Need additional LinkedIn data?

Pair this Actor with LinkedIn Jobs Scraper (linkedin-search-jobs-scraper) in this account for job-listing data from the same platform, or with a general profile scraper if you need bio/skills fields this post-focused Actor doesn't collect.

How does LinkedIn Profile Post Scraper differ from the official LinkedIn API?

LinkedIn's official API requires joining the LinkedIn Partner Program and getting a specific product tier approved before any meaningful post, engagement, or profile data becomes accessible — LinkedIn Profile Post Scraper needs no approval and works from any public profile URL immediately. Per Microsoft Learn's LinkedIn API documentation (checked 2026-08-04), all API access has required Partner Program approval since 2015, and most permissions beyond "Open Permissions" need a case-by-case review.

FeatureLinkedIn API (official)LinkedIn Profile Post Scraper
Access approvalLinkedIn Partner Program application and manual reviewNone — start from Apify Console
Arbitrary profile's post historyNot available to general developersWorks on any public profile URL
Setup timeApplication and review processMinutes — paste URLs and run
Login/session requiredOAuth app with approved scopesNone — public pages only
Output shapeVaries by approved product tierOne fixed JSON schema for post/comment/reaction rows
Rate limitsSet by LinkedIn per approved app/tierConfigurable per run via rateLimitDelay and fetchRetries

Use the official API if you already hold Partner Program access and need first-party guarantees. Use LinkedIn Profile Post Scraper when you need public post data now, without an approval cycle.

How to use LinkedIn Profile Post Scraper

Run it directly from the Apify platform — no separate signup, API key request, or approval step beyond an Apify account.

  1. Open the Actor's page on the Apify Store or in Apify Console → Actors.
  2. Provide the required input: urls, a list of LinkedIn profile URLs (https://www.linkedin.com/in/...).
  3. Optionally set maxPosts, rateLimitDelay, fetchRetries, and proxyConfiguration.
  4. Click Start.
  5. Open the Dataset tab to watch rows arrive live, or export as JSON/CSV/Excel once the run finishes.

How to scale to bulk post extraction

urls is an array — add as many profile URLs as you need in a single run and the Actor crawls each one independently, in sequence, up to maxPosts per profile. There is no separate "bulk" input mode; a longer urls list is the bulk path. For very large lists, running multiple Actor runs (e.g. via the Apify API or a schedule, split across batches of URLs) parallelizes further, since each run only processes its own list sequentially.

What can you do with LinkedIn post data?

  • 📈 Sales and SDR teams researching prospects use postedAtISO and numLikes/numComments to time outreach around a prospect's most engaged recent post.
  • 🔬 Researchers and analysts doing content or sentiment analysis use text and authorHeadline across a list of profiles to build a topic dataset without manual copy-paste.
  • 🕵️ Competitive intelligence teams track an executive's or competitor's posting cadence using timeSincePosted and numLikes over repeated runs.
  • 📊 Growth and social teams benchmark engagement by pulling numLikes, numComments, and scrapedAt into a BI tool or spreadsheet.
  • 🤖 AI engineers feed text plus the nested comments[] and reactions[] into a RAG pipeline or agent tool as grounded, structured context for LinkedIn-activity questions.

How does LinkedIn Profile Post Scraper handle rate limits and blocking?

The Actor starts every profile scrape on Apify Proxy's residential group — LinkedIn profile pages are switched to this connection automatically before the first request, regardless of the proxyConfiguration you pass in, because a direct or datacenter connection gets blocked on /in/ pages almost immediately. If a request still comes back 401, 403, 429, or 999, or the response body contains a captcha/forbidden signal, the Actor escalates further and pins one proxy session for the rest of that profile's crawl so the exit IP stays "warm." A 429 response triggers a longer wait before retrying the same page; other failures retry up to fetchRetries times with a growing delay. If a whole profile scrape throws an exception while already on the residential connection, the Actor retries that profile up to 3 times before giving up and logging the failure. ⚠️ The Actor does not solve CAPTCHAs — it only avoids and retries around them via proxy escalation. There is no headless browser in this Actor; every fetch is a plain HTTP request, and pages are parsed from the returned HTML/JSON-LD.

⬇️ Input

ParameterRequiredTypeDescriptionExample value
urlsYesarrayLinkedIn profile URLs to scrape. Only linkedin.com/in/... personal profile URLs are accepted — company, school, group, and post URLs are skipped with a log warning.["https://www.linkedin.com/in/satyanadella"]
maxPostsNointegerMaximum number of posts to collect per URL. Minimum 1, maximum 500, default 10.20
rateLimitDelayNointegerBase delay between page fetches, in seconds. Minimum 0, maximum 30, default 1. Actual delay includes random jitter on top of this base.1
fetchRetriesNointegerHow many times to retry a single page fetch before giving up on it. Minimum 0, maximum 10, default 3.3
proxyConfigurationNoobjectApify Proxy settings. Default prefill is {"useApifyProxy": false}, but profile pages always run on Apify Proxy's residential group regardless — use this field to set a specific proxy group or country rather than to disable proxying.{"useApifyProxy": false}

Example input

{
"urls": [
"https://www.linkedin.com/in/satyanadella",
"https://www.linkedin.com/in/ajjames"
],
"maxPosts": 20,
"rateLimitDelay": 1,
"fetchRetries": 3,
"proxyConfiguration": { "useApifyProxy": false }
}

⬆️ Output

Every row is typed, normalized JSON with a fixed set of keys per row type, consistent across runs. Download results as JSON, CSV, Excel, or the other formats Apify's dataset export supports, or stream them from the API while the run is still going. Every row pushed to the dataset — post, comment, or reaction — is charged as one row_result pay-per-event; skipped/invalid input URLs and fetch failures are only logged, never pushed as dataset rows, so they are never charged.

Example output

A post row:

{
"urn": "urn:li:activity:7123456789012345678",
"text": "Excited to share how AI is transforming the way we work across every industry...",
"url": "https://www.linkedin.com/posts/satyanadella_ai-activity-7123456789012345678",
"postedAtTimestamp": 1753123200000,
"postedAtISO": "2026-07-21T14:00:00.000000Z",
"timeSincePosted": "2w",
"isRepost": null,
"authorType": "Person",
"authorProfileUrl": "https://www.linkedin.com/in/satyanadella",
"authorProfileId": "satyanadella",
"authorHeadline": "Chairman and CEO at Microsoft",
"authorFullName": "Satya Nadella",
"image": "https://media.licdn.com/dms/image/sample.jpg",
"type": "image",
"images": ["https://media.licdn.com/dms/image/sample.jpg"],
"author": {
"firstName": "Satya",
"lastName": "Nadella",
"occupation": "Chairman and CEO at Microsoft",
"id": "satyanadella",
"publicId": "satyanadella",
"trackingId": "aBcDeFgHiJkLmNoPqRsTuA",
"profileId": "satyanadella",
"picture": "",
"backgroundImage": ""
},
"authorName": "Satya Nadella",
"authorTitle": "Chairman and CEO at Microsoft",
"attributes": [],
"comments": [],
"reactions": [],
"numShares": null,
"numLikes": 4213,
"numComments": 187,
"commentsTruncated": true,
"commentsComplete": false,
"reactionsTruncated": true,
"canReact": null,
"canPostComments": null,
"canShare": null,
"commentingDisabled": null,
"allowedCommentersScope": null,
"rootShare": null,
"shareAudience": null,
"rowType": "post",
"isChild": false,
"parentId": null,
"scrapedAt": "2026-08-04T09:15:32.104521Z"
}

The matching comment child row:

{
"rowType": "comment",
"isChild": true,
"parentId": "urn:li:activity:7123456789012345678",
"type": "comment",
"urn": null,
"url": "https://www.linkedin.com/feed/update/comment/7123456789099999999",
"text": "Great insights — this applies directly to how our team plans rollouts.",
"authorFullName": "Priya Sharma",
"authorProfileId": "commenter-8f2a91cd44e0",
"authorType": null,
"authorProfileUrl": null,
"authorHeadline": "",
"postedAtTimestamp": 1754297732104,
"postedAtISO": null,
"timeSincePosted": null,
"image": null,
"images": [],
"numLikes": null,
"numComments": null,
"numShares": null,
"isRepost": null,
"attributes": [],
"commentsTruncated": null,
"commentsComplete": null,
"reactionsTruncated": null,
"scrapedAt": "2026-08-04T09:15:32.104521Z"
}

And the matching reaction child row:

{
"rowType": "reaction",
"isChild": true,
"parentId": "urn:li:activity:7123456789012345678",
"type": "LIKE",
"urn": null,
"url": null,
"text": null,
"authorFullName": "James Ortiz",
"authorProfileId": "james-ortiz-9b21",
"authorType": null,
"authorProfileUrl": null,
"authorHeadline": null,
"postedAtTimestamp": null,
"postedAtISO": null,
"timeSincePosted": null,
"image": null,
"images": [],
"numLikes": null,
"numComments": null,
"numShares": null,
"isRepost": null,
"attributes": [],
"commentsTruncated": null,
"commentsComplete": null,
"reactionsTruncated": null,
"scrapedAt": "2026-08-04T09:15:32.104521Z"
}

How does it work?

LinkedIn Profile Post Scraper reads each profile URL with a plain HTTP session (requests) — there is no headless browser. It warms the session up against LinkedIn's homepage and feed first, then fetches the profile page and any linked post/feed pages it discovers, extracting posts from embedded JSON-LD, falling back to Open Graph meta tags when JSON-LD is missing, and parsing comments/reactions/images out of the raw HTML with parsel selectors. Requests start on a direct connection but LinkedIn profile pages switch straight to Apify Proxy's residential group, escalating further and retrying with backoff if a page comes back blocked. Only what's visible on the public page is ever read — no login, no session cookie. The set of JSON keys returned per row type is fixed in the Actor's code, so field names stay the same run to run even though the underlying HTML selectors may need updating if LinkedIn changes its markup.

Integrations

LinkedIn Profile Post Scraper runs on the Apify platform, so it works with anything that can call the Apify API or trigger an Apify run.

Calling LinkedIn Profile Post Scraper programmatically

from apify_client import ApifyClient
client = ApifyClient("<APIFY_API_TOKEN>")
run = client.actor("your-username/linkedin-profile-post-scraper").call(
run_input={
"urls": ["https://www.linkedin.com/in/satyanadella"],
"maxPosts": 20,
}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["rowType"], item.get("text"), item.get("numLikes"))

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request.

No-code tools (n8n, Make)

In n8n, use Apify's official n8n integration node (or a plain HTTP Request node against the run endpoint) to start this Actor and pull results into your workflow. In Make, add Apify's native module, select this Actor from the actor dropdown, and map its output dataset into downstream steps like a CRM update or spreadsheet row.

Scraping publicly available data — data that doesn't require logging in to view — is generally permitted; LinkedIn Profile Post Scraper only returns what any signed-out visitor to a profile page can already see. Because posts, comments, and reactions carry personal data (names, headlines, post content tied to identifiable people), collection and storage of this data can fall under data protection regimes such as GDPR (EU/UK) or the CCPA (California) depending on who you are and who the data is about — these regimes govern how you store, use, and retain personal data, not whether reading a public page is itself unlawful. Consult legal counsel if your use case involves bulk storage of personal data.

Frequently asked questions

What LinkedIn post fields does LinkedIn Profile Post Scraper return?

The core fields on every post row are text, authorFullName, authorHeadline, numLikes, and numComments, plus postedAtISO, url, and nested comments[]/reactions[]. See What data can I extract above for the full field list.

Does LinkedIn Profile Post Scraper require a LinkedIn account or login?

No. The Actor never uses a LinkedIn login, account, or li_at session cookie — it only reads public profile pages over plain HTTP requests.

How many posts can I extract in one run?

Up to maxPosts per URL (1–500, default 10), for as many URLs as you put in the urls list. Total dataset rows per run are higher than maxPosts × number of URLs, since each qualifying post also adds its own comment and reaction child rows.

What happens if a profile is private, doesn't exist, or has no posts?

If a URL isn't a /in/ profile URL, it's skipped with a log warning and never fetched. If the profile page fails to load or comes back blocked after retries and proxy escalation, no rows are pushed for that URL and the run log records the failure — no placeholder or error row is written to the dataset for it.

Can I scrape multiple LinkedIn profiles at once?

Yes. urls accepts an array — the Actor processes each profile URL independently and tracks its own set of seen post URNs so posts across different profiles never collide.

Does LinkedIn Profile Post Scraper work with Claude, ChatGPT, and other AI agent tools?

It isn't set up as an MCP server, but it's callable as a standard Apify API run by any agent framework that can make an HTTP request or use the apify-client SDK — see Integrations above.

How does LinkedIn Profile Post Scraper compare to other LinkedIn post scrapers?

This README doesn't benchmark against other Store listings. What's verifiable here from the source: comments and reactions are emitted both nested on the parent post row and as their own standalone, filterable dataset rows, and fields LinkedIn doesn't expose anonymously (isRepost, numShares, comment/reaction permission flags) are always returned as null rather than a guessed value.

Does LinkedIn Profile Post Scraper return data in a format LLMs can use directly?

Yes. Output is typed, normalized JSON with consistent field names across runs — no HTML parsing or CSS selectors needed on your side. Pass it directly to an LLM prompt, index it into a vector store, or feed it to an agent tool.

What happens when LinkedIn changes its layout or anti-bot system?

The Actor's output schema (the fixed set of keys per row type) is defined in its code and doesn't change with LinkedIn's markup. The underlying HTML/JSON-LD extraction may need maintenance if LinkedIn changes its page structure significantly; no specific turnaround time is published for such fixes.

Can I use LinkedIn Profile Post Scraper without managing proxies or browser infrastructure?

Yes — there's no browser to manage (the Actor uses plain HTTP requests, not a headless browser), and proxy escalation across Apify Proxy's datacenter and residential groups is handled internally. You can still pass your own proxyConfiguration to pin a specific group or country.

Which LinkedIn post fields work best for AI training data and RAG indexing?

For RAG context, index text, authorHeadline, and authorFullName — the highest-information text fields. For structured training data, numLikes, numComments, and postedAtISO are the most consistently populated numeric/date fields across post rows; all fields return as typed primitives (strings, integers, booleans, or null), never as unparsed HTML.

ScraperWhat it extracts
LinkedIn Jobs Scraper (linkedin-search-jobs-scraper)LinkedIn job listings — titles, companies, locations
Facebook Group Posts and Details ScraperPublic Facebook group posts and group details
Threads Search Post ScraperPublic Threads posts matching a search query
Twitter (X.com) Tweets & Profiles ScraperPublic tweets and profile data from X/Twitter

Your feedback

Found a bug or missing a field? Let us know through the Issues tab on this Actor's Apify Store listing, or send feedback via Apify Console — reports get read and factored into the next update.