LinkedIn Post Comments Scraper & Post Reactors avatar

LinkedIn Post Comments Scraper & Post Reactors

Pricing

from $5.99 / 1,000 results

Go to Apify Store
LinkedIn Post Comments Scraper & Post Reactors

LinkedIn Post Comments Scraper & Post Reactors

๐Ÿ’ฌ Scrape LinkedIn post comments & replies in bulk. ๐Ÿ‘ Get likes, reactions, author names & profile links. ๐Ÿ“… Filter by date. ๐Ÿ”— Add multiple posts at once. No login required by default.

Pricing

from $5.99 / 1,000 results

Rating

0.0

(0)

Developer

Scrapier

Scrapier

Maintained by Community

Actor stats

0

Bookmarked

21

Total users

4

Monthly active users

a day ago

Last modified

Share

LinkedIn Post Comments Scraper โ€” Extract Comments and Post Reactors

LinkedIn Post Comments Scraper & Post Reactors pulls every comment, every reply thread, and โ€” its signature capability โ€” every person who reacted to a LinkedIn post, starting from a post URL, an activity/ugcPost URN, or a bare numeric post ID. It returns typed JSON rows for comments, replies, reactors and per-post accounting, joined by an engagementType of commented, reacted or both, with a computed overlap percentage between the two audiences. Comments run with no LinkedIn login at all; unlocking the reactor list and full comment coverage needs an li_at session cookie. Every row is structured JSON, ready to pass directly to an LLM, index into a vector store, or feed a monitoring pipeline.

What is LinkedIn Post Comments Scraper & Post Reactors?

LinkedIn Post Comments Scraper & Post Reactors is an Apify Actor that takes one or more LinkedIn post URLs, activity/ugcPost URNs, or bare numeric post IDs and returns every comment, reply, and โ€” the capability that separates it from a plain comment scraper โ€” every person who reacted to the post, as structured dataset rows. Comments run with no LinkedIn account at all; the reactor list is walled behind login on LinkedIn's own side (every logged-out reaction link on a public post page routes to /signup/cold-join), so capturing it requires pasting your own li_at session cookie.

The reason reactors matter: as the Actor's own input description states, a post can carry 3,221 reactions against only 213 comments. Comments are the vocal minority. A comments-only scraper โ€” which is what every comparable LinkedIn comment Actor on Apify currently offers โ€” throws away the majority of the audience that engaged with a post. This Actor collects both sides and labels every person commented, reacted, or both.

Key capabilities:

  • Top-level comments and reply threads, with exact UTC timestamps decoded from the comment ID itself โ€” no extra request needed
  • Post reactors as their own rows: name, headline, profile URL, avatar and reaction type
  • A commented / reacted / both join per person, plus a commenterReactorOverlapPct figure per post
  • Two collection engines โ€” a cookie-free public page and a logged-in API โ€” with automatic escalation between them
  • Reaction-type filtering: Like, Celebrate, Support, Love, Insightful, Funny
  • A comment age filter (postedLimit) with exact UTC cutoffs, from the last 24 hours to the last year

What data can you get with LinkedIn Post Comments Scraper & Post Reactors?

The Actor writes four distinct row types into the same dataset โ€” comment, reply, reactor and accounting โ€” distinguished by the type field.

Result TypeExtracted FieldsPrimary Use Case
Commentcomment_id, text, posted_at, author, stats, commentCreatedAt, commenterProfileUrl, engagementTypeTrack what people said publicly and who said it
ReplySame shape as Comment, plus parentCommentId, isReply, threadDepth, threadPositionRebuild a comment thread outside LinkedIn
ReactorreactorName, reactorHeadline, reactorProfileUrl, reactorAvatarUrl, reactionType, engagementTypeIdentify people who engaged without ever commenting
AccountingerrorReason, errorMessage, commentsAvailable, commentsTotal, reactorsAvailableKnow exactly what was skipped, and why, without guessing

Post Reactors: the entity comment-only scrapers miss

This is the row type that does not exist in the comment-only Actors this listing competes against. When scrapeReactors is on and a valid li_at cookie is supplied, the Actor pages through LinkedIn's authenticated reactions surface and emits one row per reactor โ€” independent of whether that person ever left a comment. Each reactor row carries engagementType: "both" when the same profile also shows up in the comment set for that post, or "reacted" when it does not. A commenterReactorOverlapPct value on every row for the post tells you, in one number, what fraction of the captured commenters also reacted โ€” useful for judging how representative your comment sample is of the wider audience.

{
"type": "reactor",
"isChild": true,
"reactorName": "Amara Chen",
"reactorHeadline": "VP Product Marketing at a B2B SaaS company",
"reactorProfileUrl": "https://www.linkedin.com/in/amara-chen",
"reactorAvatarUrl": "https://media.licdn.com/dms/image/v2/D4D03AQ.../profile-displayphoto-shrink_200_200",
"reactionType": "PRAISE",
"engagementType": "reacted",
"commenterReactorOverlapPct": 18.4,
"sourceEngine": "voyager",
"reactorsAvailable": true,
"reactorsCaptured": 50,
"postReactionCount": 3221,
"postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/",
"post_input": "7289521182721093633",
"errorReason": null,
"scrapedAt": "2026-07-25T09:14:02Z"
}

Without a cookie, scrapeReactors still runs the comment pass normally, reports the post's total reaction count via postReactionCount (available logged-out), and writes an uncharged accounting row with errorReason: "login_required" instead of a faked empty reactor list โ€” an empty array would read as "nobody reacted," which would be false.

Comments and reply threads

Comments come from one of two engines. The public page (no cookie) reads a post's server-rendered ld+json block and DOM markers directly โ€” no browser required โ€” and typically returns the first page of comments. The logged-in Voyager API, used when a cookie is supplied, pages through the full comment set, exposes per-reaction-type breakdowns (like, appreciation, empathy, interest, praise) inside stats.reactions, and supports reply threads. When scrapeReplies is on, replies are written twice: nested inside the parent comment's replies array, and flattened into their own rows carrying parentCommentId, isReply and threadDepth, so a thread can be rebuilt in a spreadsheet without recursion.

Why not build a LinkedIn comment and reactor scraper yourself?

LinkedIn does not publish a general-purpose, self-serve API that returns a post's comments or reactions to an arbitrary caller โ€” the officially documented Marketing/partner APIs are partnership-gated and scoped to a company's own content, not to querying any post by URL. Building this in-house means reverse-engineering LinkedIn's internal surfaces directly, and each of the three surfaces this Actor touches has its own trap.

The public post page serves real comment data with no login, but LinkedIn's guest wall returns a plain HTTP 200 on both the real page and the block page โ€” status code alone proves nothing. This Actor detects a soft block by checking whether the result container itself rendered (the ld+json posting node, the DOM comment markers, or the post shell), not by scanning for "sorry" or "captcha" strings, which are trivial to change server-side and easy to miss.

The logged-in comment API requires a queryId โ€” an internal, versioned identifier LinkedIn embeds somewhere in its shipped JavaScript, not in any documented location. This Actor discovers it by scanning the LinkedIn feed and post page HTML for inline scripts, then falling back to HEAD-filtering and scanning every external and CDN-hosted script LinkedIn loads, in parallel, until the query module is found. That discovery logic alone is several hundred lines.

The reactor list has no public or DOM-rendered form at all โ€” every logged-out reaction affordance links straight to a signup wall โ€” so it is reachable only through LinkedIn's internal REST reactions endpoint, whose payload shape shifts between a flat list and an entity-reference index depending on the request, and whose reactor identity has to be resolved by cross-referencing three different possible URN keys against an included[] object index.

On top of all three, LinkedIn throttles aggressively with a custom HTTP 999 status. This Actor treats 999 as a throttle signal, not a data gap โ€” it rotates to a fresh proxy IP and retries serially rather than reporting nulls that would look like missing comments.

What's the difference between a comment scraper and an engagement scraper?

A comment scraper returns only the people who typed a reply. An engagement scraper โ€” what this Actor is โ€” returns everyone who interacted with the post, whether they wrote something or just clicked a reaction. The distinction matters because the two groups are not the same size or the same audience: per the Actor's own input description, a post can carry 3,221 reactions against 213 comments, meaning a comment-only export can miss the majority of everyone who actually saw and engaged with the post.

This Actor returns both. Comments and replies arrive as comment / reply rows built from LinkedIn's comment surface; reactors arrive as reactor rows built from LinkedIn's separate reactions surface. The two are joined per person through engagementType and summarized per post through commenterReactorOverlapPct, so you get commenter data, reactor data, and the overlap between them in one dataset rather than three separate tools.

How to scrape LinkedIn posts with this Actor

  1. Open LinkedIn Post Comments Scraper & Post Reactors on the Apify Store and click Try for free
  2. Paste one or more LinkedIn post URLs, activity/ugcPost URNs, or numeric post IDs into startUrls โ€” this is the only field that needs a value
  3. Set resultLimitPerPost for how many top-level comments you want per post, postedLimit if you only want recent comments, and commentEngine (leave it on auto unless you have a reason not to)
  4. To capture reactors, turn on scrapeReactors and paste your li_at cookie into liAtCookie โ€” this is the only combination that unlocks the reactor list
  5. Click Start, then open the dataset and export it as JSON or CSV, or read it through the Apify API

How to run multiple posts in one job

startUrls is an array โ€” add as many post URLs, URNs or IDs as you need, one per line in the Apify Console's list editor, or as a JSON array through the API. Each post is fetched and pushed independently, so one post failing (an invalid URL, a deleted post) does not stop the others; it produces its own accounting row instead.

โฌ‡๏ธ Input

All fields are optional except that at least one entry in startUrls is required for the Actor to do anything.

ParameterRequiredTypeDescriptionExample Value
startUrlsNoarrayLinkedIn post URLs, activity/ugcPost URNs, or bare numeric post IDs. Each post is captured separately. Default [].["https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/"]
resultLimitPerPostNointegerCaps top-level comments only. Reactors are counted separately and never eat this budget. Minimum 1, maximum 500, default 10.50
commentEngineNostring (enum)auto โ€” public page, escalates with a cookie (recommended); public โ€” never uses your cookie; voyager โ€” logged-in API only, requires a cookie. Default auto."auto"
scrapeReactorsNobooleanCollect the people who reacted to the post as their own rows. Requires the li_at cookie below. Reactor rows are never charged. Default false.true
maxReactorsPerPostNointegerHow many reactor records to collect per post, additional to the comment limit above. Minimum 1, maximum 2000, default 50.200
reactionTypeFilterNostring (enum)ALL, LIKE, PRAISE (Celebrate), EMPATHY (Support), APPRECIATION (Love), INTEREST (Insightful), ENTERTAINMENT (Funny). Default "ALL"."PRAISE"
liAtCookieNostring (secret)Unlocks the reactor list and full comment coverage. From your browser: DevTools โ†’ Application โ†’ Cookies โ†’ linkedin.com โ†’ li_at. Leave empty to run comments-only, logged out. Stored as an Apify secret input."AQEDA..."
liAtNostringThe same cookie as a plain-text field, kept for runs and integrations that already pass liAt. If both fields are filled, this one wins. Default ""."AQEDA..."
scrapeRepliesNobooleanAdds replies under each comment, both nested on the parent row and as their own flat rows carrying parentCommentId, isReply and threadDepth. Reply rows are not charged. Requires a cookie. Default false.true
profileScraperModeNostring (enum)short โ€” name and profile link; full โ€” adds headline and profile picture where LinkedIn exposes them. Default "short"."full"
postedLimitNostring (enum)any, 24h, week, month, 3months, 6months, year. Dates are exact UTC timestamps, so the cutoff is precise. Default "any"."month"
proxyConfigurationNoobjectApify Proxy settings. LinkedIn throttles bursts with HTTP 999; the Actor escalates through its own proxy ladder automatically when throttled, whether or not you set this. Prefilled with no proxy.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}

Example input

{
"startUrls": [
"https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/"
],
"resultLimitPerPost": 50,
"commentEngine": "auto",
"scrapeReactors": true,
"maxReactorsPerPost": 200,
"reactionTypeFilter": "ALL",
"liAtCookie": "AQEDA...",
"scrapeReplies": true,
"profileScraperMode": "full",
"postedLimit": "any",
"proxyConfiguration": { "useApifyProxy": false }
}

Common pitfalls

  • Use liAtCookie, not liAt, unless you have a reason not to. Only liAtCookie is marked as a secret input โ€” it is masked in the Apify Console and encrypted at rest. liAt is a plain-text field and appears unmasked in your run's input history. If you fill both, liAt wins, which is easy to forget if you pasted a cookie into the wrong box.
  • Setting commentEngine to voyager without supplying a cookie returns zero comments and a generic "no_comments" accounting row โ€” the public-page engine never runs in that mode, and there is no logged-in session to fall back to. Leave commentEngine on auto, or double-check liAtCookie is filled before switching to voyager.
  • profileScraperMode: "full" only enriches comments collected through the logged-in engine. On the public page, commenter headline is always null and the profile picture is served regardless of this setting โ€” there is no "full" mode for the cookie-free path because LinkedIn's guest page simply does not render a headline.
  • resultLimitPerPost and maxReactorsPerPost are independent budgets โ€” raising one does not touch the other, and reactors never draw down your comment quota.

โฌ†๏ธ Output

Every result is one row in the same dataset, typed and normalized JSON, distinguished by type (comment, reply, reactor or accounting) and isChild (true for replies and reactors, false for top-level comments and accounting rows). Export as JSON or CSV, or read the dataset through the Apify API.

One naming quirk worth knowing up front: the base comment shape โ€” comment_id, text, posted_at, is_edited, is_pinned, comment_url, author, stats, post_input โ€” uses snake_case, while every field this Actor adds on top โ€” commentCreatedAt, commenterProfileUrl, engagementType, postReactionCount and the rest โ€” uses camelCase. Both conventions appear on the same row. This is intentional: the base fields are kept byte-identical to the underlying comment engine's own shape so nothing downstream that already expects them breaks.

Comment and reply rows

Comment and reply rows share one shape. A reply row is a comment row with isReply: true and thread-position fields added.

  • comment_id โ€” comment ID
  • text โ€” comment text
  • posted_at โ€” object: { timestamp, date, relative }
  • is_edited, is_pinned โ€” booleans
  • comment_url โ€” permalink to the comment
  • author โ€” object: { name, headline, profile_url, profile_picture }
  • stats โ€” object: { total_reactions, reactions: { like, appreciation, empathy, interest, praise }, comments }
  • replies โ€” nested array of reply objects in this same shape (populated only when scrapeReplies is on and the logged-in engine ran; otherwise [])
  • post_input โ€” the numeric post ID this row belongs to
  • totalComments โ€” LinkedIn's own reported comment total for the post, from the logged-in engine
  • type โ€” "comment" or "reply"
  • isChild โ€” false for top-level comments, true for replies
  • isReply, parentCommentId, threadDepth, threadPosition โ€” reply-thread position (null/0 on top-level comments)
  • commentCreatedAt โ€” exact UTC ISO timestamp, decoded from the comment ID's embedded snowflake with no extra request, falling back to the API's own date field
  • commenterSlug โ€” the commenter's LinkedIn URL slug, used internally to join against reactors
  • commenterProfileUrl โ€” the commenter's profile URL
  • engagementType โ€” "commented", or "both" if this same person also appears in the reactor set for the post
  • commenterReactorOverlapPct โ€” overlap percentage for the post (see Post Reactors above); null when reactors were not captured
  • sourceEngine โ€” "public_page" or "voyager", whichever engine produced this row
  • commentsAvailable, commentsTotal โ€” comments captured for the post vs. LinkedIn's reported total
  • postReactionCount, postCommentCount, postAuthorName, postAuthorUrl, postPublishedAt โ€” post-level context, available even with no cookie
  • reactorsAvailable โ€” whether the reactor pass actually ran for this post
  • reactorsCaptured โ€” how many reactor rows were collected for the post (null when scrapeReactors was off or the pass failed)
  • errorReason โ€” always null on comment/reply rows; errors are reported on a separate accounting row instead
  • postUrl โ€” the post's canonical URL
  • scrapedAt โ€” UTC ISO timestamp of the run

Reactor rows

  • type โ€” "reactor"
  • isChild โ€” always true
  • reactorName, reactorHeadline, reactorProfileUrl, reactorAvatarUrl โ€” the reactor's profile fields, as exposed by LinkedIn's reactions surface
  • reactorSlug โ€” LinkedIn URL slug, lower-cased, used for the join against commenters
  • reactionType โ€” one of LIKE, PRAISE, EMPATHY, APPRECIATION, INTEREST, ENTERTAINMENT
  • engagementType โ€” "reacted", or "both" if this same profile also left a comment on the post
  • commenterReactorOverlapPct โ€” same overlap figure carried on comment rows for the post
  • sourceEngine โ€” always "voyager" (reactors have no public-page path)
  • reactorsAvailable โ€” always true on a reactor row (the row only exists because the pass succeeded)
  • reactorsCaptured โ€” total reactor rows collected for this post
  • postReactionCount, postCommentCount, postAuthorName โ€” post-level context
  • postUrl โ€” the post's canonical URL
  • post_input โ€” the numeric post ID
  • errorReason โ€” always null on reactor rows
  • scrapedAt โ€” UTC ISO timestamp of the run

Reactor rows are also mirrored into a separate per-run dataset named reactors-<runId>, in addition to the default dataset, in case you want reactors isolated from comments without filtering.

Accounting rows

Pushed once per post whenever something was skipped, so a gap in your data always has a documented reason rather than a silently missing post.

  • type โ€” always "accounting"
  • isChild, isReply โ€” always false
  • errorReason โ€” one of invalid_input (the entry wasn't a recognizable post URL/URN/ID), login_required (reactors were requested with no cookie), reactor_fetch_failed, http_999 (LinkedIn's throttle), blocked, http_<status> (any other non-200 response), voyager_failed, or no_comments (no comments were returned and no other reason was recorded)
  • errorMessage โ€” a human-readable explanation of errorReason
  • sourceEngine โ€” which engine, if any, produced a partial result before the error
  • commentsAvailable, commentsTotal โ€” comment counts at the time of the error, if any were captured
  • reactorsAvailable โ€” false on every accounting row
  • reactorsCaptured, reactorsCollected โ€” both always 0 on an accounting row
  • postReactionCount, postCommentCount โ€” post-level context, if it was retrieved before the error
  • postUrl, post_input โ€” the post this row refers to
  • commenterReactorOverlapPct โ€” always null
  • scrapedAt โ€” UTC ISO timestamp of the run

Example output

[
{
"type": "comment",
"isChild": false,
"comment_id": "7331190065471078400",
"text": "This matches what we're seeing in our own funnel data.",
"posted_at": { "timestamp": 1747891918534, "date": "2025-05-22 05:31:58", "relative": "3d" },
"is_edited": false,
"is_pinned": false,
"comment_url": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/?commentUrn=urn%3Ali%3Acomment%3A...",
"author": {
"name": "Priya Raman",
"headline": "Head of Growth at a Series B startup",
"profile_url": "https://www.linkedin.com/in/priya-raman",
"profile_picture": "https://media.licdn.com/dms/image/v2/D4D03AQ.../profile-displayphoto-shrink_400_400"
},
"stats": { "total_reactions": 12, "reactions": { "like": 9, "appreciation": 2, "empathy": 1, "interest": 0, "praise": 0 }, "comments": 1 },
"replies": [],
"post_input": "7289521182721093633",
"totalComments": 213,
"isReply": false,
"parentCommentId": null,
"threadDepth": 0,
"threadPosition": null,
"commentCreatedAt": "2025-05-22T05:31:58Z",
"commenterSlug": "priya-raman",
"commenterProfileUrl": "https://www.linkedin.com/in/priya-raman",
"engagementType": "both",
"commenterReactorOverlapPct": 18.4,
"sourceEngine": "voyager",
"commentsAvailable": 213,
"commentsTotal": 213,
"postReactionCount": 3221,
"postCommentCount": 213,
"postAuthorName": "Satya Nadella",
"postAuthorUrl": "https://www.linkedin.com/in/satyanadella",
"postPublishedAt": "2025-05-19T14:02:00Z",
"reactorsAvailable": true,
"reactorsCaptured": 200,
"errorReason": null,
"postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/",
"scrapedAt": "2026-07-25T09:14:02Z"
},
{
"type": "reply",
"isChild": true,
"comment_id": "7331191002233445566",
"text": "Same here โ€” worth a follow-up post on the methodology.",
"author": { "name": "Diego Fernandez", "headline": null, "profile_url": "https://www.linkedin.com/in/diego-fernandez", "profile_picture": null },
"isReply": true,
"parentCommentId": "7331190065471078400",
"threadDepth": 1,
"threadPosition": 1,
"commenterReactorOverlapPct": 18.4,
"sourceEngine": "voyager",
"postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/",
"scrapedAt": "2026-07-25T09:14:02Z"
},
{
"type": "reactor",
"isChild": true,
"reactorName": "Amara Chen",
"reactorHeadline": "VP Product Marketing at a B2B SaaS company",
"reactorProfileUrl": "https://www.linkedin.com/in/amara-chen",
"reactorAvatarUrl": "https://media.licdn.com/dms/image/v2/D4D03AQ.../profile-displayphoto-shrink_200_200",
"reactionType": "PRAISE",
"engagementType": "reacted",
"commenterReactorOverlapPct": 18.4,
"sourceEngine": "voyager",
"reactorsAvailable": true,
"reactorsCaptured": 200,
"postReactionCount": 3221,
"postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/",
"post_input": "7289521182721093633",
"errorReason": null,
"scrapedAt": "2026-07-25T09:14:02Z"
},
{
"type": "accounting",
"isChild": false,
"isReply": false,
"errorReason": "login_required",
"errorMessage": "LinkedIn serves the reactor list only to logged-in members. Add an li_at cookie to collect reactors. Comments were scraped normally and the post's total reaction count is reported.",
"sourceEngine": "public_page",
"commentsAvailable": 9,
"commentsTotal": 213,
"reactorsAvailable": false,
"reactorsCaptured": 0,
"reactorsCollected": 0,
"postReactionCount": 3221,
"postCommentCount": 213,
"postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/",
"post_input": "7289521182721093633",
"commenterReactorOverlapPct": null,
"scrapedAt": "2026-07-25T09:14:02Z"
}
]

Billing

Only top-level comment rows (type: "comment") are charged, on the row_result event. Reply rows, reactor rows, accounting rows, and the mirrored reactors-<runId> dataset are all pushed uncharged โ€” capturing reactors and reply threads never increases your cost per post. To isolate charged rows in the dataset, filter on type == "comment".

How can I use the data extracted with this Actor?

  • Sales and social sellers: pull commenterProfileUrl and reactorProfileUrl off a competitor's or a target account's high-performing post to build a warm-outreach list, and use engagementType to prioritize people who both commented and reacted over those who only clicked a reaction.
  • AI engineers and LLM developers: feed the Actor's typed JSON straight into an agent as retrieved context โ€” a sales-assistant agent can ask "who engaged with our last product launch post" and get structured comment and reactor rows back, no parsing step required.
  • Social media managers and community teams: track stats.reactions per comment and commenterReactorOverlapPct per post to see which posts generate real conversation versus posts that get reactions without discussion.
  • Market and competitive researchers: run the same competitor post URLs on a schedule and diff commentsAvailable, postReactionCount and the reactor list between runs to see how a post's engagement compounds over its first week.

๐Ÿ”„ How do you monitor post engagement over time?

Engagement on a LinkedIn post keeps growing after it's published, so a single run only ever captures a snapshot. Repeated runs on the same startUrls turn this Actor into a monitoring tool: schedule a run every few hours or once a day across the posts you care about, and compare each run's output to the previous one.

The fields worth diffing between runs are postReactionCount and postCommentCount (total growth), commentsAvailable and reactorsCaptured (how much of that growth you actually captured this run), and the set of commenterProfileUrl / reactorProfileUrl values (who is new since the last run). A rising commenterReactorOverlapPct over successive runs tells you the comment section is increasingly drawing from the same people who already reacted, rather than pulling in new voices โ€” useful for judging whether a post's conversation is still growing or has plateaued.

A concrete loop: schedule a daily run across a fixed list of post URLs โ†’ extract postReactionCount, commentsAvailable and the reactor/commenter profile sets from each run's dataset โ†’ diff against the previous day's dataset โ†’ alert when a monitored post crosses a reaction or comment threshold, or when a specific profile (say, a target prospect) shows up in the reactor or commenter set for the first time. Use an Apify schedule to run the Actor automatically and an Apify webhook to fire on each completed run, pulling the new dataset into your own comparison logic.

Integrate this Actor and automate your workflow

This Actor works with any language or tool that can send an HTTP request through the Apify API.

REST API with Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/linkedin-post-comments-scraper-post-reactors").call(run_input={
"startUrls": ["https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/"],
"resultLimitPerPost": 50,
"scrapeReactors": True,
"maxReactorsPerPost": 200,
"liAtCookie": "AQEDA...",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["type"] == "comment":
print(row["author"]["name"], "->", row["text"][:60])
elif row["type"] == "reactor":
print(row["reactorName"], "reacted with", row["reactionType"])

Works the same way in Go, Ruby, Node.js or curl โ€” any language that can make an HTTP request and read your token.

Scheduled monitoring and delivery

Set up a recurring Apify schedule against the same startUrls to poll a post's engagement over time, and attach an Apify webhook on the run to push each run's dataset to your own endpoint automatically instead of pulling it manually.

Scraping publicly accessible LinkedIn content is broadly permitted under U.S. law โ€” in hiQ Labs, Inc. v. LinkedIn Corp. (9th Cir., 2019), the court held that scraping data LinkedIn makes available to logged-out visitors does not violate the Computer Fraud and Abuse Act. This Actor's public-page comment engine returns only what any logged-out visitor already sees on the post page.

Commenter and reactor names, headlines and profile URLs are personal data, so GDPR and CCPA still apply to how you store, process and retain what you collect, independent of whether the scrape itself was lawful. The li_at-gated reactor list and full comment coverage require your own logged-in session and are subject to LinkedIn's Terms of Service, which restrict automated data collection while logged in โ€” using this mode is a contractual risk with LinkedIn, distinct from the legal question of whether the data itself is scrapeable. Scraping for one-off research carries a different risk profile than storing profile data in bulk for outreach or model training.

Consult your legal team for commercial use cases involving bulk data storage or logged-in scraping at scale.

โ“ Frequently asked questions

Which post identifier formats does this Actor accept?

startUrls accepts full LinkedIn post URLs, activity:/ugcPost: URNs embedded in a URL or string (urn:li:activity:7289521182721093633), and bare numeric post IDs (7289521182721093633) โ€” the Actor detects the format automatically and normalizes it to a canonical post URL.

How many comments and reactors can I collect per post?

resultLimitPerPost caps top-level comments at up to 500 per post (default 10). maxReactorsPerPost caps reactors at up to 2000 per post (default 50), and this budget is completely separate โ€” reactors never count against your comment limit, or vice versa.

Not for comments. Comments, commenter names, profile URLs, and the post's total reaction count are all available with no login. A cookie (liAtCookie) is required only for two things: collecting the reactor list at all, and getting comment coverage beyond what the public page serves plus reply threads.

Does this Actor extract post reactors โ€” the people who reacted but didn't comment?

Yes โ€” this is the Actor's differentiator. Set scrapeReactors: true and supply liAtCookie, and each reactor is written as its own type: "reactor" row with reactorName, reactorHeadline, reactorProfileUrl, reactorAvatarUrl and reactionType. Without a cookie, reactors are unavailable and the run instead writes an uncharged accounting row with errorReason: "login_required".

What's the difference between liAtCookie and liAt?

Both carry the same LinkedIn li_at session cookie. liAtCookie is marked as a secret input โ€” masked in the Console and encrypted at rest โ€” and is the field you should use. liAt is a legacy plain-text field kept for existing integrations; if you fill in both, liAt takes precedence, so leave it empty unless you specifically need it.

No. profileScraperMode: "full" only adds a commenter's headline and profile picture when comments come from the logged-in engine. On the cookie-free public page, headline is always null โ€” LinkedIn's guest page does not render it โ€” regardless of this setting.

How does this Actor handle LinkedIn's anti-bot throttling?

LinkedIn returns a custom HTTP 999 status when it throttles a burst of requests. The Actor treats 999 as a throttle rather than a data gap: it rotates to a fresh proxy IP and retries serially instead of reporting nulls that would look like missing comments. Soft blocks on the public page (which return HTTP 200 with a login-wall page) are caught by checking whether the comment container or post shell actually rendered, not by scanning for block-page keywords.

How do I monitor a post's engagement over time?

Re-run the same startUrls on an Apify schedule and diff postReactionCount, commentsAvailable, reactorsCaptured and the commenter/reactor profile URL sets against the previous run's dataset. See "How do you monitor post engagement over time?" above for the full loop.

Does this Actor work with Claude, ChatGPT and other AI agent frameworks?

Yes. It is callable as a standard HTTP endpoint through the Apify API, so any agent framework that can issue a request โ€” LangChain, CrewAI, a custom tool definition, or a direct API call from Claude or ChatGPT โ€” can invoke it and receive typed JSON rows back, ready to ground a response in a post's real engagement data.

How does this Actor compare to other LinkedIn comment scrapers?

Checked on the Apify Store on 25 July 2026: harvestapi/linkedin-post-comments and apimaestro/linkedin-post-comments-replies-engagements-scraper-no-cookies both scrape comments and nested replies with no cookie required, and both document per-comment reaction-type counts; neither documents capturing post reactors as a distinct entity. datadoping/linkedin-post-comments-scraper scrapes comments only, with a documented free-tier cap of 4 posts and 100 comments per post per run. This Actor's difference is the reactor row type and the engagementType/commenterReactorOverlapPct join against commenters โ€” none of the three document returning the people who reacted without commenting.

Can I use this Actor without managing proxies or LinkedIn credentials?

For comments: yes. The Actor runs its own proxy escalation ladder (direct โ†’ datacenter โ†’ residential) automatically when LinkedIn throttles a request, whether or not you configure proxyConfiguration yourself. For reactors and full comment coverage, you do need to supply your own li_at cookie โ€” that credential is LinkedIn's login requirement, not something an Actor can substitute with proxy infrastructure.

๐Ÿ’ฌ Your feedback

Found a bug, or a field LinkedIn exposes that this Actor doesn't capture yet? Open an issue on the Actor's Issues tab in the Apify Console with the post URL and the field you expected โ€” that's the fastest way to get it looked at.