Linkedin Profile Post Scraper
Pricing
$19.99/month + usage
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
Maintained by CommunityActor stats
0
Bookmarked
53
Total users
4
Monthly active users
4 days ago
Last modified
Categories
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 category | Publicly available | Restricted (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.
| Field | Description |
|---|---|
urn | Post's LinkedIn activity URN, e.g. urn:li:activity:7123456789012345678. null on comment/reaction rows. |
rowType | "post", "comment", or "reaction" — what this row represents. |
isChild | true for comment/reaction rows nested under a post; false for the post row itself. |
parentId | The parent post's urn. null on post rows. |
type | Post rows: "image" or "text". Reaction rows: the reaction type (LIKE, LOVE, CELEBRATE, SUPPORT, FUNNY, INSIGHTFUL). Comment rows: "comment". |
url | Post permalink, or the comment's permalink on comment rows. null on reaction rows. |
text | Post body or comment text. null on reaction rows. |
authorFullName | Post author's name, or the commenter/reactor's name on child rows. |
authorHeadline | Author's headline/description, read from the page's JSON-LD. |
authorProfileId | Profile URL slug of the post author, commenter, or reactor (e.g. satyanadella). |
postedAtTimestamp / postedAtISO | Publish 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 / numComments | Reaction and comment counts on a post. null on child rows. |
image / images | First image URL and up to 5 image URLs on a post. null / [] on child rows. |
scrapedAt | ISO 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—@mentionentities parsed from post text and markup.author— nested object:firstName,lastName,occupation,id,publicId,trackingId,profileId,picture,backgroundImage.authorName,authorTitle— convenience copies ofauthorFullName/authorHeadline.
Engagement, timing and permission fields
timeSincePosted— relative age string ("2d","3w","1mo"), computed frompostedAtISO.numShares— alwaysnull: LinkedIn does not expose a share count on the anonymous post markup this Actor reads.isRepost— alwaysnull: repost status isn't detectable from the anonymous page.- ⚠️
canReact,canPostComments,canShare,commentingDisabled,allowedCommentersScope,rootShare,shareAudience— alwaysnullon every row. These permission flags only exist behind an authenticated LinkedIn session; the Actor returnsnull, never a guessed value. commentsTruncated/commentsComplete/reactionsTruncated— booleans flagging whether the nested arrays below captured all ofnumComments/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 owncommentchild rows.reactions— array of reaction objects nested on the parent post row (type,profile{firstName,lastName,...}), in addition to being emitted as their ownreactionchild 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.
| Feature | LinkedIn API (official) | LinkedIn Profile Post Scraper |
|---|---|---|
| Access approval | LinkedIn Partner Program application and manual review | None — start from Apify Console |
| Arbitrary profile's post history | Not available to general developers | Works on any public profile URL |
| Setup time | Application and review process | Minutes — paste URLs and run |
| Login/session required | OAuth app with approved scopes | None — public pages only |
| Output shape | Varies by approved product tier | One fixed JSON schema for post/comment/reaction rows |
| Rate limits | Set by LinkedIn per approved app/tier | Configurable 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.
- Open the Actor's page on the Apify Store or in Apify Console → Actors.
- Provide the required input:
urls, a list of LinkedIn profile URLs (https://www.linkedin.com/in/...). - Optionally set
maxPosts,rateLimitDelay,fetchRetries, andproxyConfiguration. - Click Start.
- 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
postedAtISOandnumLikes/numCommentsto time outreach around a prospect's most engaged recent post. - 🔬 Researchers and analysts doing content or sentiment analysis use
textandauthorHeadlineacross 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
timeSincePostedandnumLikesover repeated runs. - 📊 Growth and social teams benchmark engagement by pulling
numLikes,numComments, andscrapedAtinto a BI tool or spreadsheet. - 🤖 AI engineers feed
textplus the nestedcomments[]andreactions[]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
| Parameter | Required | Type | Description | Example value |
|---|---|---|---|---|
urls | Yes | array | LinkedIn 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"] |
maxPosts | No | integer | Maximum number of posts to collect per URL. Minimum 1, maximum 500, default 10. | 20 |
rateLimitDelay | No | integer | Base delay between page fetches, in seconds. Minimum 0, maximum 30, default 1. Actual delay includes random jitter on top of this base. | 1 |
fetchRetries | No | integer | How many times to retry a single page fetch before giving up on it. Minimum 0, maximum 10, default 3. | 3 |
proxyConfiguration | No | object | Apify 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 ApifyClientclient = 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.
Is it legal to scrape LinkedIn posts?
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.
Related scrapers
| Scraper | What it extracts |
|---|---|
LinkedIn Jobs Scraper (linkedin-search-jobs-scraper) | LinkedIn job listings — titles, companies, locations |
| Facebook Group Posts and Details Scraper | Public Facebook group posts and group details |
| Threads Search Post Scraper | Public Threads posts matching a search query |
| Twitter (X.com) Tweets & Profiles Scraper | Public 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.