Post Comments Engagements Scraper Linkedin: Filter Spam & Ads
Pricing
from $3.99 / 1,000 results
Post Comments Engagements Scraper Linkedin: Filter Spam & Ads
LinkedIn Post Comments & Engagements Scraper extracts comments, reactions, engagement metrics, and author details while filtering spam and promotional ads. Ideal for audience research, engagement analysis, content performance tracking, sentiment insights, and B2B lead generation.
Pricing
from $3.99 / 1,000 results
Rating
0.0
(0)
Developer
Scrapio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
LinkedIn Comment Scraper — Extract Comments, Duplicates & Promo Flags
A LinkedIn comment scraper built to answer one question competitors don't ask: is this comment real engagement, or noise? Every comment comes back as typed JSON carrying a duplicate-cluster fingerprint (copy-paste comments sprayed across multiple posts) and a promotional score with an auditable reasons array — not a black-box label, but the exact lexicon rule and substring that fired. Unlike scraping frameworks that return raw HTML, this actor returns structured rows ready for your database, your moderation queue, or your model without any parsing. Runs logged-out by default, no LinkedIn account required. The rest of this guide covers every input and output field, the exact scoring rule behind promotionalScore, and how teams deploy comment screening as a recurring pipeline rather than a one-off export.
What Does This LinkedIn Comment Scraper Do?
It reads the public comment thread on one or more LinkedIn posts and returns one row per comment, then layers two deterministic classifiers on top: near-duplicate clustering across every post in the run, and a published promotional-phrase scorer. No LinkedIn account or cookie is required — the default engine reads the same public post page a logged-out visitor sees. Supplying your own li_at cookie is optional and only unlocks the depth LinkedIn itself withholds from guests (nested replies, per-reaction-type counts, edited/pinned markers).
What it returns, in one run:
- Comment text, author identity, engagement stats and exact UTC timestamps for every post you submit
- A
duplicateClusterIdandduplicateCountfor comments whose (normalised) body repeats across two or more of the posts in the run - A
promotionalScore(0–1) pluspromotionalReasons[]naming every lexicon rule that fired - Outbound link domains, contact emails and contact phone numbers parsed from the comment body, with false-positive guards
- Three handling modes per classifier (
flag,exclude/collapse,only) so you decide whether noise rows are kept, dropped, or isolated — and dropped rows are never billed - Honest coverage accounting (
commentsAvailablevscommentsTotal) so a guest-ceiling slice is visible, never silently passed off as the full thread
Features & Capabilities
This actor pairs standard comment/engagement extraction with two classifiers most comment scrapers don't attempt: cross-post duplicate detection and transparent promotional scoring.
Core features
- Flexible post input — LinkedIn post URLs,
urn:li:activity:…/urn:li:ugcPost:…URNs, or bare numeric activity IDs, one per line (startUrls) - Cross-post near-duplicate clustering — every comment in the run is normalised (case, punctuation, emoji, URLs, whitespace stripped), fingerprinted with SHA-1, and grouped with union-find across all posts submitted in that run (
duplicateFingerprint,duplicateClusterId,duplicateCount,duplicatePostCount,duplicateAuthorCount,duplicateMatchType) - Published promotional lexicon —
promotionalScoreis the sum of fixed rule weights, capped at 1.0, with every contributing rule and its matched substring listed inpromotionalReasons[] - Link and contact extraction —
linkDomains[](LinkedIn's own domains excluded),contactEmails[],contactPhones[], each with a false-positive guard described in the Output section below - Low-effort labelling —
lowEffortCommentflags bodies at or under a configurable character threshold, or emoji-only bodies - Billing-aware filtering — rows removed by
promotionalHandling: exclude/onlyordedupeHandling: collapse/onlyare never pushed with a charge attached - Dual engine — a logged-out public-page engine by default, with an authenticated GraphQL engine that only activates when you supply your own
li_atcookie
How this scraper compares to other LinkedIn comment scrapers
As observed on the Apify Store (competitor snapshot dated 2026-07-08):
| Feature | This actor | apimaestro/linkedin-post-comments-replies-engagements-scraper-no-cookies | api-empire (simpleapi) /post-comments-engagements-scraper-linkedin |
|---|---|---|---|
| Login required | No (default); optional li_at cookie | No, marketed as cookie-free | No (default); optional li_at cookie |
| Promotional/spam scoring | Yes — published lexicon, promotionalReasons[] | Not documented | Not documented |
| Cross-post duplicate detection | Yes — union-find clustering across the run | Not documented | Not documented |
| Nested replies | Yes, cookie path only (guest path returns none) | Yes | Yes |
| Per-reaction-type counts | Yes, cookie path only | Yes | Yes |
| Proxy/anti-bot handling | Documented — direct → datacenter → residential escalation, IP rotation on HTTP 999 | Not documented | Documented — proxy fallback with retries |
| Billing on filtered-out rows | Excluded/collapsed rows never billed | Not documented (dataset-item pricing) | Not documented |
| Output shape | One row per comment | One row per comment/reply | One row per post, comments nested in an array |
If your use case is feeding structured data to an LLM or a moderation queue, the output-shape row is the decision-maker — a nested per-post array means your pipeline has to walk into comments[] before it can filter anything, while a flat per-comment row filters and joins directly.
When another tool might suit you better
apimaestro's listing (checked 2026-07-08) reports a large run history and user base built on straightforward comment-and-reply extraction with no noise classification at all. If all you need is the raw thread — every comment and reply, no scoring, no filtering — a simpler extractor with a larger track record may be the lower-friction choice. This actor is built specifically for the case where the thread is large enough, or spans enough posts, that separating pitch comments and copy-paste spam from genuine engagement is itself the task.
This actor within the Scrapio LinkedIn data stack
This actor covers post comments and their engagement/noise classification. For buying-intent filtering by keyword, reaction count, author or exact date range instead of noise detection, use linkedin-post-comments-scraper-buying-signal-detection. For company firmographics, use linkedin-company-url-mass-profile-finder-by-industry-size. For job listings, use linkedin-jobs-scraper-exact-posting-dates-reposts. For profile credibility scoring, use linkedin-profile-credibility-scraper.
Why do developers and data teams scrape LinkedIn comments?
Community managers and moderation teams
A brand or creator running several LinkedIn posts a week needs to know which comments are genuine replies and which are pitch accounts working the comment section. Feed startUrls with the account's last several posts, set promotionalHandling to flag for the first pass, and sort the output table on promotionalScore and duplicateClusterId to see exactly which accounts are repeating a pitch across posts before deciding whether to exclude them from the next run.
AI training data and RAG indexing
text, promotionalReasons and duplicateClusterId make this data usable two ways. For RAG enrichment, the text field on rows where isPromotional is false and isDuplicate is false is the highest-signal set — genuine audience reaction, not repeated spam. For training data, promotionalReasons[] is itself a labelled dataset: every row is scored against a fixed, published lexicon, so a model trained to detect promotional comments has ground truth it can audit rule-by-rule rather than a black-box label. All fields return as typed primitives (strings, numbers, booleans, arrays) requiring no HTML parsing before they hit a context window.
Competitive and market intelligence
duplicateAuthorCount and duplicatePostCount surface the mechanical signature of coordinated activity — the same body appearing under a competitor's last ten posts from a handful of accounts is visible directly in those two columns, without manual thread-by-thread review.
Research and academic use
Public-post comment threads are a common source for studying engagement manipulation, astroturfing and comment-section spam on professional networks. This actor reads only what a logged-out visitor can already see; it collects no private-profile or connection-gated data.
Product and SaaS development
The promotionalScore/promotionalReasons and duplicateClusterId fields are enough to build a lightweight moderation-queue feature or a "clean feed" toggle on top of LinkedIn comment data, without training or hosting a classifier of your own.
🍚 Input Parameters
No parameter is marked required in the schema — startUrls defaults to an empty list. In practice the actor needs at least one entry: if none is provided it logs a warning and exits without pushing any rows.
| Parameter | Type | Default | Constraints | Description |
|---|---|---|---|---|
startUrls | array (stringList) | [] | — | LinkedIn post URLs, activity URNs (urn:li:activity:123…) or bare numeric activity IDs, one per line. Add multiple posts from the same author or company — duplicate clustering runs across every post in the run, so a copy-paste comment only becomes visible when more than one post is present. |
commentsPerPost | integer | 100 | min 1, max 500 | Upper bound on comments read per post. Logged-out, LinkedIn itself serves a guest roughly the first 9–10 comments per post, so a higher number only changes the result when an li_at cookie is supplied. |
promotionalHandling | string (select) | "flag" | enum: flag, exclude, only | flag keeps every comment and adds the score. exclude drops comments at or above the threshold — those rows are not pushed and not billed. only returns the flagged comments by themselves, for moderation or reporting. |
promotionalThreshold | number | 0.5 | min 0, max 1 | Score at which a comment counts as promotional. The score is the sum of the published lexicon weights that fired (see the lexicon table below). Default 0.5 means any two rules firing together are enough to flag a comment. |
dedupeHandling | string (select) | "flag" | enum: flag, collapse, only | flag keeps every comment and adds duplicateClusterId/duplicateCount. collapse keeps the earliest comment of each duplicate cluster and drops the rest — dropped rows are not pushed and not billed. only returns the duplicated bodies alone. |
duplicateSimilarity | number | 0.9 | min 0.5, max 1 | How alike two comments must be to land in the same cluster. Bodies are compared after case, punctuation, emoji, URLs and whitespace are normalised away. 1 means only identical normalised bodies cluster; 0.9 also catches a line with a word or an emoji swapped. Comments shorter than 15 normalised characters are never clustered. |
lowEffortMaxChars | integer | 25 | min 0, max 200 | A comment whose normalised body is this many characters or shorter (or is nothing but emoji) is marked lowEffortComment = true. Label only — it never removes or charges anything. |
emitDuplicateClusters | boolean | false | — | Writes one record per duplicate cluster (member count, posts spanned, authors, sample text, comment IDs) to a separate child dataset named after the run. The child dataset is never billed. |
commentOrder | string (select) | "REVERSE_CHRONOLOGICAL" | enum: REVERSE_CHRONOLOGICAL, RELEVANCE | Order requested from LinkedIn on the cookie path. Logged-out, LinkedIn serves a fixed guest slice and ignores any order parameter, so this only changes the result when an li_at cookie is supplied. |
liAt | string (textfield, secret) | — | — | Optional. Leave empty to run logged-out, which is the default and the tested path. This actor contains no built-in credential of any kind. Supplying your own li_at cookie lifts LinkedIn's ~10-comments-per-post guest ceiling and adds nested replies, per-reaction-type counts and the edited/pinned markers. Marked isSecret in the schema — masked in the Apify Console and excluded from run input logs. |
proxyConfiguration | object (proxy) | {"useApifyProxy": false} | — | Optional. The actor already escalates direct → datacenter → residential on its own and rotates the exit IP when LinkedIn answers with HTTP 999, so a proxy group is not required for the default path. |
Two schema fields are worth flagging by name: commentsPerPost and commentOrder are the variant's own key names. For backward compatibility with the base actor this scraper is built on, if your input JSON also sets the original keys resultLimitPerPost or sortOrder, those base keys take precedence over commentsPerPost/commentOrder when both are present in the same input — so existing base-actor input JSON keeps working unchanged.
Example input
{"startUrls": ["https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/","https://www.linkedin.com/feed/update/urn:li:activity:7351242287143972864/"],"commentsPerPost": 100,"promotionalHandling": "exclude","promotionalThreshold": 0.5,"dedupeHandling": "collapse","duplicateSimilarity": 0.9,"lowEffortMaxChars": 25,"emitDuplicateClusters": true,"commentOrder": "REVERSE_CHRONOLOGICAL"}
Supported URL types and input formats
Each line in startUrls is parsed independently and accepts three shapes:
- Full post URL —
https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/ - Activity or ugcPost URN, with or without a surrounding URL —
urn:li:activity:7289521182721093633orurn:li:ugcPost:7289521182721093633 - Bare numeric activity ID —
7289521182721093633
A line that doesn't match any of these three shapes is skipped and reported as an uncharged invalid_input accounting row rather than silently dropped.
📦 Output Format
Dataset rows are typed JSON — every field is a string, number, boolean, array or nested object, never raw HTML. The Apify platform's standard dataset export formats (JSON, CSV, Excel, XML, RSS) apply to every run.
The default dataset view shows 36 columns; the pushed row actually carries 38 keys on a normal comment row — authorSlug and linkCount are written to every row but are not part of the default view. Both are documented below.
Output for a comment row
{"comment_id": "7289521199912345678","text": "Great insights, thanks for sharing this with the community","posted_at": {"timestamp": 1752483693000,"date": "2026-07-14T09:21:33Z","relative": "2w"},"is_edited": null,"is_pinned": null,"comment_url": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/?commentUrn=urn:li:comment:(activity:7289521182721093633,7289521199912345678)","author": {"name": "Jane Doe","headline": null,"profile_url": "https://www.linkedin.com/in/jane-doe","profile_picture": "https://media.licdn.com/dms/image/…"},"authorSlug": "jane-doe","stats": { "total_reactions": 12, "reactions": {}, "comments": null },"replies": [],"post_input": "7289521182721093633","commentCreatedAt": "2026-07-14T09:21:33Z","sourceEngine": "public_page","commentsAvailable": 9,"commentsTotal": 213,"totalComments": 213,"postFormat": "video","scrapedAt": "2026-07-25T12:00:00Z","postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/","duplicateFingerprint": "3f9a1c4e07a82b11","duplicateClusterId": "dup_3f9a1c4e07","duplicateCount": 3,"duplicatePostCount": 2,"duplicateAuthorCount": 2,"isDuplicate": true,"duplicateMatchType": "exact","promotionalScore": 0.3,"promotionalReasons": ["duplicate_across_posts:same body under more than one post"],"isPromotional": false,"linkDomains": [],"linkCount": 0,"lowEffortComment": false,"contactEmails": [],"contactPhones": [],"commentCharCount": 60,"isError": false,"errorReason": null,"errorMessage": null}
is_edited, is_pinned, the per-type breakdown inside stats.reactions, the comments (reply count) inside stats, and populated replies[] are all cookie-path only — logged-out, LinkedIn's markup carries none of these, so they are emitted as null/empty rather than a fabricated false or 0. author.headline is likewise null logged-out; LinkedIn does not render it to guests.
Accounting rows — a post that throttles, walls, returns no comments, or is an invalid input still produces one row, with isError: true, a populated errorReason (http_999, login_required, no_comment_container, empty_body, no_comments, fetch_failed, invalid_input, empty_comment_row, or filtered_out) and errorMessage. Accounting rows are never billed.
Output for a duplicate-cluster record (optional child dataset)
Only written when emitDuplicateClusters is true, to a separate, unbilled child dataset named duplicate-clusters-<runId>:
{"type": "duplicate_cluster","duplicateClusterId": "dup_3f9a1c4e07","duplicateFingerprint": "3f9a1c4e07a82b11","memberCount": 3,"postCount": 2,"authorCount": 2,"sampleText": "Great insights, thanks for sharing this with the community","authors": ["Jane Doe", "Sam Lee"],"postUrls": ["https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/","https://www.linkedin.com/feed/update/urn:li:activity:7351242287143972864/"],"commentIds": ["7289521199912345678", "7351242299912345000"],"runId": "a1b2c3d4e5f6","scrapedAt": "2026-07-25T12:00:00Z"}
Output for the promotional-flag fields (the exact keyword rule)
promotionalScore is not a model. It is the sum of fixed weights from a published lexicon (noise.PROMO_LEXICON in the source), capped at 1.0 and rounded to 3 decimals. Every point is traceable to a named rule and the exact matched substring, emitted as rule:evidence in promotionalReasons[]:
| Rule code | Weight | Fires when |
|---|---|---|
contact_prompt | 0.40 | Body contains any of: dm me, dm for, dms open, send me a dm, inbox me, message me, pm me, text me, whatsapp me, whatsapp:, contact me, email me, reach out to me, hit me up, ping me, drop me a line, let's connect and, connect with me for |
outbound_link | 0.35 | linkDomains[] is non-empty (LinkedIn's own domains excluded) |
service_list | 0.35 | 3 or more distinct terms from: seo, web design, web development, app development, logo design, graphic design, video editing, lead generation, digital marketing, social media marketing, data entry, wordpress, shopify, virtual assistant, content writing, copywriting, ui/ux, mobile app |
self_promo_phrase | 0.30 | Any of: check out my, check my profile, visit my, follow me for, link in bio, link in my bio, see my profile, my agency, my company helps, we help brands, we help businesses, i help brands, i help businesses, i help founders, i build websites, i build apps, i am a freelance, i'm a freelance, our services include, dm me for details, swipe up, sign up here, join my — or either regex pattern below |
duplicate_across_posts | 0.30 | The comment's duplicate cluster spans more than one post in the run |
price_offer | 0.25 | Any of: free trial, free consultation, book a call, book a demo, limited offer, limited time offer, special discount, starting at $, only $, get 50% off, get 20% off, money back guarantee, no upfront cost, risk free |
Two self_promo_phrase triggers are regex patterns rather than fixed phrases, because the shape — not the exact wording — identifies them:
\bi(?:'m| am)\s+an?\s+(?:\w+[\s,]+){0,3}(?:developer|designer|marketer|freelancer|consultant|copywriter|strategist|specialist|expert|coach|agency|virtual assistant)\b\bi\s+build\s+(?:an?\s+|your\s+)?(?:websites?|web ?apps?|apps?|saas|bots?|software|automations?|funnels?|stores?)\b
A comment is marked isPromotional: true once its score reaches promotionalThreshold (default 0.5) — so any two rules firing together are enough by default. Measured on a 125-comment, 13-post live corpus (documented in the source): the pitch lexicon fired on 1 of 125 comments (0.8%), an outbound link/domain appeared on 6 of 125 (4.8%), low-effort comments were 3 of 125 (2.4%), and a verbatim duplicate body across posts appeared on 25 of 125 rows (20.0%). The pitch-lexicon rate is audience-dependent and is low on corporate/brand posts — it is not presented as a universal rate anywhere in this actor.
Schema stability and export options
duplicateFingerprint, promotionalReasons and the other classifier fields are computed entirely from text and are stable across LinkedIn front-end changes — they don't depend on any DOM selector. The comment-extraction fields (author, stats, posted_at) depend on LinkedIn's public page markup and JSON-LD structure; when a field is unavailable on the path used for that row it is emitted as null rather than omitted, so the schema shape stays constant across every row in a dataset. Results export from the Apify Console or API in JSON, CSV, Excel, XML or RSS — there is no delivery mechanism beyond the standard Apify dataset.
💡 Post Comments Engagements Scraper Strategy Guide
Strategy 1: Real-time moderation pipeline
Trigger a run whenever a monitored post crosses a comment-count threshold or on a short interval after publishing. Feed the post URL into startUrls with promotionalHandling: "flag" and dedupeHandling: "flag" for visibility, then read promotionalScore, promotionalReasons and duplicateClusterId off each row and write them back into your moderation queue or CRM alongside comment_id and author.profile_url, so a human reviewer sees the evidence, not just a verdict.
Strategy 2: Scheduled monitoring and alerting
Use an Apify Schedule to run this actor against a company's or creator's recent posts on a recurring interval. Diff each run's output against the previous run on duplicateClusterId and duplicateAuthorCount — a new cluster appearing with a rising duplicateAuthorCount across otherwise unrelated posts is the signal worth alerting on, since it indicates the same body is now spreading through more accounts than the last check.
Strategy 3: Bulk research dataset build
Give startUrls a large batch of post URLs from the accounts you want to study, set commentsPerPost near the 500 maximum, and run with dedupeHandling: "flag" so nothing is dropped from the raw dataset. Export the resulting dataset to CSV for downstream analysis in a notebook or database — no concurrency limit is documented for this actor, so plan batch size around your own dataset export needs rather than a stated ceiling.
Strategy comparison at a glance
| Strategy | Best for | Run pattern | Output format |
|---|---|---|---|
| Real-time moderation | Community managers reviewing incoming comments | On-demand or short-interval run per monitored post | Dataset rows read into a moderation queue |
| Scheduled monitoring | Tracking spam/duplicate campaigns over time | Apify Schedule, recurring | Run-over-run diff on duplicateClusterId |
| Bulk dataset build | Research or training data | One or few runs, large startUrls batch | CSV/JSON export |
🌴 Related LinkedIn Scrapers & Tools
| Scraper | What it extracts |
|---|---|
| linkedin-post-comments-scraper-buying-signal-detection | LinkedIn post comments filtered by keyword, reaction count, author and exact date range, scored for buying intent |
| linkedin-jobs-scraper-exact-posting-dates-reposts | LinkedIn job listings with exact posting dates and repost grouping |
| linkedin-company-url-mass-profile-finder-by-industry-size | LinkedIn company URLs enriched with industry, employee size and follower data |
| linkedin-profile-credibility-scraper | Public LinkedIn profiles scored for credibility from recommendations, network and experience |
| instagram-comment-sentiment-scraper | Instagram post comments with sentiment labelling |
| tiktok-comments-scraper-top-comments-by-language | TikTok video comments ranked and filtered by language |
| facebook-videos-scraper-reactions-comment-counts | Facebook video posts with reaction and comment counts |
| linkedin-profile-phone-deduper-exporter | Phone numbers extracted from LinkedIn-related search results, globally deduplicated |
How to integrate this LinkedIn comment scraper with your stack
This actor works with any language or tool that can make an HTTP request through the Apify API, or through Apify's official client libraries.
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run_input = {"startUrls": ["https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/","https://www.linkedin.com/feed/update/urn:li:activity:7351242287143972864/",],"commentsPerPost": 100,"promotionalHandling": "flag","dedupeHandling": "flag",}run = client.actor("<YOUR_USERNAME>/post-comments-engagements-scraper-linkedin-promo-detection").call(run_input=run_input)rows = []for item in client.dataset(run["defaultDatasetId"]).iterate_items():rows.append({"comment_id": item.get("comment_id"),"text": item.get("text"),"promotionalScore": item.get("promotionalScore"),"duplicateClusterId": item.get("duplicateClusterId"),})import csvwith open("comments.csv", "w", newline="", encoding="utf-8") as f:writer = csv.DictWriter(f, fieldnames=["comment_id", "text", "promotionalScore", "duplicateClusterId"])writer.writeheader()writer.writerows(rows)
Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });const run = await client.actor('<YOUR_USERNAME>/post-comments-engagements-scraper-linkedin-promo-detection').call({startUrls: ['https://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/',],promotionalHandling: 'exclude',dedupeHandling: 'collapse',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(`${items.length} comment row(s) returned`);
Async and scheduled pipelines
For fire-and-forget large jobs, start the run with client.actor(...).start() instead of .call() and poll the run status, or configure an Apify webhook on ACTOR.RUN.SUCCEEDED to be notified when the dataset is ready instead of polling. For recurring screening, use an Apify Schedule to trigger the same input on an interval — see Strategy 2 above.
Who Needs This LinkedIn Comment Scraper? (Use Cases & Industries)
Community and social media managers
Screening a brand's or creator's comment sections for pitch accounts and copy-paste spam before reporting engagement numbers to stakeholders — promotionalScore and duplicateClusterId separate genuine audience reaction from noise before it reaches a report.
AI/ML and data teams
Building a labelled dataset for a promotional-comment classifier, or filtering a RAG corpus down to genuine engagement text using isPromotional/isDuplicate as exclusion flags, with promotionalReasons[] available as auditable ground truth.
Competitive intelligence analysts
Tracking whether a competitor's comment sections show signs of coordinated or purchased engagement, using duplicateAuthorCount and duplicatePostCount as the mechanical signal rather than manual thread review.
Researchers
Studying engagement manipulation and comment-section spam patterns on a professional network using only publicly visible comment data — no private-profile or connection-gated content is collected.
SaaS and product builders
Building a lightweight LinkedIn comment-moderation or "clean feed" feature on top of this actor's classifier fields, without training or hosting a spam-detection model independently.
Is it legal to scrape LinkedIn comments?
Scraping publicly accessible data is generally lawful in the United States: in hiQ Labs, Inc. v. LinkedIn Corp., 938 F.3d 985 (9th Cir. 2019), reaffirmed on remand in 2022, the Ninth Circuit held that accessing publicly available LinkedIn data does not violate the Computer Fraud and Abuse Act. That precedent concerns criminal/civil liability under the CFAA, not LinkedIn's own Terms of Service — LinkedIn's user agreement independently prohibits automated scraping, and violating it is a civil contract matter between you and LinkedIn, not a criminal one. Because comment rows include a commenter's name, profile URL and profile picture — public personal data — data protection obligations (GDPR, CCPA and similar laws) may apply to what you do with it once collected, even though the data itself is publicly posted. This actor extracts only publicly accessible data from LinkedIn. What you do with that data is your responsibility — consult legal counsel for commercial applications involving personal data.
❓ Frequently asked questions
Does this scraper work without a LinkedIn account?
Yes. Logged-out is the default and the tested path — no cookie, no login, and this actor ships with no built-in credential of any kind. Supplying your own li_at cookie is optional and only lifts LinkedIn's own guest ceiling.
How does it handle LinkedIn's anti-scraping measures?
It escalates proxy tier automatically: direct connection first, then Apify datacenter proxy, then residential proxy, rotating the exit IP whenever LinkedIn responds with HTTP 999 (its rate-limit status). A login/join redirect or a page missing the expected comment container is detected and retried on a fresh IP rather than being misread as an empty post — a guest wall can return HTTP 200 with no comments, so status code alone is never used to decide success.
Can I run this at scale without getting blocked?
The actor's proxy escalation and IP rotation are documented behaviour, but no uptime or success-rate figure is published for this actor, and no numeric concurrency limit is documented in the source. At scale, a throttled post is labelled http_999 in the output rather than silently skipped, so you can see exactly which posts need a retry.
How fresh is the data this scraper returns?
Every run fetches the post page live at run time — there is no caching layer. scrapedAt on every row records the exact run timestamp in UTC.
Which fields work best for AI training and RAG indexing?
For RAG, text on rows where isPromotional is false and isDuplicate is false is the highest-signal set. For training a classifier, promotionalReasons[] gives auditable rule-level labels rather than a single opaque score. All fields return as typed primitives — strings, numbers, booleans and arrays — requiring no normalization before use.
Does this scraper collect personal data, and who is responsible for it?
Yes — author.name, author.profile_url and author.profile_picture are public personal data attached to each comment, and contactEmails/contactPhones may surface personal contact details a commenter posted publicly. The actor returns only what LinkedIn already displays publicly; lawful basis for storing and using that data sits with you, the user, under whatever data protection law applies in your jurisdiction.
Does this scraper work with Claude, ChatGPT and other AI agent tools?
There is no dedicated MCP server for this actor. It is callable as an HTTP endpoint through the Apify API by any agent framework, or through the apify-client library shown above — every response is typed JSON, so no HTML parsing is needed before it enters an LLM's context window.
How does this scraper compare to other LinkedIn comment scrapers?
As observed on the Apify Store (checked 2026-07-08), apimaestro's cookie-free comment-and-reply scraper and the api-empire/simpleapi listing both extract comments, replies, reactions and author info competently, and neither documents promotional scoring or cross-post duplicate detection. This actor adds those two classifiers on top of comparable core extraction, with the tradeoff that its own listing has a shorter track record than apimaestro's.
Disclaimer
This scraper extracts only publicly available data from LinkedIn. This tool is intended for lawful use cases only. Users are responsible for complying with LinkedIn's terms of service and applicable data protection laws in their jurisdiction.