Post Comments Engagements Scraper Linkedin: Filter Spam & Ads avatar

Post Comments Engagements Scraper Linkedin: Filter Spam & Ads

Pricing

from $3.99 / 1,000 results

Go to Apify Store
Post Comments Engagements Scraper Linkedin: Filter Spam & Ads

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

Scrapio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

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 duplicateClusterId and duplicateCount for comments whose (normalised) body repeats across two or more of the posts in the run
  • A promotionalScore (0–1) plus promotionalReasons[] 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 (commentsAvailable vs commentsTotal) 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 lexiconpromotionalScore is the sum of fixed rule weights, capped at 1.0, with every contributing rule and its matched substring listed in promotionalReasons[]
  • Link and contact extractionlinkDomains[] (LinkedIn's own domains excluded), contactEmails[], contactPhones[], each with a false-positive guard described in the Output section below
  • Low-effort labellinglowEffortComment flags bodies at or under a configurable character threshold, or emoji-only bodies
  • Billing-aware filtering — rows removed by promotionalHandling: exclude/only or dedupeHandling: collapse/only are 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_at cookie

How this scraper compares to other LinkedIn comment scrapers

As observed on the Apify Store (competitor snapshot dated 2026-07-08):

FeatureThis actorapimaestro/linkedin-post-comments-replies-engagements-scraper-no-cookiesapi-empire (simpleapi) /post-comments-engagements-scraper-linkedin
Login requiredNo (default); optional li_at cookieNo, marketed as cookie-freeNo (default); optional li_at cookie
Promotional/spam scoringYes — published lexicon, promotionalReasons[]Not documentedNot documented
Cross-post duplicate detectionYes — union-find clustering across the runNot documentedNot documented
Nested repliesYes, cookie path only (guest path returns none)YesYes
Per-reaction-type countsYes, cookie path onlyYesYes
Proxy/anti-bot handlingDocumented — direct → datacenter → residential escalation, IP rotation on HTTP 999Not documentedDocumented — proxy fallback with retries
Billing on filtered-out rowsExcluded/collapsed rows never billedNot documented (dataset-item pricing)Not documented
Output shapeOne row per commentOne row per comment/replyOne 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.

ParameterTypeDefaultConstraintsDescription
startUrlsarray (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.
commentsPerPostinteger100min 1, max 500Upper 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.
promotionalHandlingstring (select)"flag"enum: flag, exclude, onlyflag 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.
promotionalThresholdnumber0.5min 0, max 1Score 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.
dedupeHandlingstring (select)"flag"enum: flag, collapse, onlyflag 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.
duplicateSimilaritynumber0.9min 0.5, max 1How 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.
lowEffortMaxCharsinteger25min 0, max 200A 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.
emitDuplicateClustersbooleanfalseWrites 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.
commentOrderstring (select)"REVERSE_CHRONOLOGICAL"enum: REVERSE_CHRONOLOGICAL, RELEVANCEOrder 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.
liAtstring (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.
proxyConfigurationobject (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 URLhttps://www.linkedin.com/feed/update/urn:li:activity:7289521182721093633/
  • Activity or ugcPost URN, with or without a surrounding URLurn:li:activity:7289521182721093633 or urn:li:ugcPost:7289521182721093633
  • Bare numeric activity ID7289521182721093633

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 codeWeightFires when
contact_prompt0.40Body 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_link0.35linkDomains[] is non-empty (LinkedIn's own domains excluded)
service_list0.353 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_phrase0.30Any 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 myor either regex pattern below
duplicate_across_posts0.30The comment's duplicate cluster spans more than one post in the run
price_offer0.25Any 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

StrategyBest forRun patternOutput format
Real-time moderationCommunity managers reviewing incoming commentsOn-demand or short-interval run per monitored postDataset rows read into a moderation queue
Scheduled monitoringTracking spam/duplicate campaigns over timeApify Schedule, recurringRun-over-run diff on duplicateClusterId
Bulk dataset buildResearch or training dataOne or few runs, large startUrls batchCSV/JSON export
ScraperWhat it extracts
linkedin-post-comments-scraper-buying-signal-detectionLinkedIn post comments filtered by keyword, reaction count, author and exact date range, scored for buying intent
linkedin-jobs-scraper-exact-posting-dates-repostsLinkedIn job listings with exact posting dates and repost grouping
linkedin-company-url-mass-profile-finder-by-industry-sizeLinkedIn company URLs enriched with industry, employee size and follower data
linkedin-profile-credibility-scraperPublic LinkedIn profiles scored for credibility from recommendations, network and experience
instagram-comment-sentiment-scraperInstagram post comments with sentiment labelling
tiktok-comments-scraper-top-comments-by-languageTikTok video comments ranked and filtered by language
facebook-videos-scraper-reactions-comment-countsFacebook video posts with reaction and comment counts
linkedin-profile-phone-deduper-exporterPhone 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 ApifyClient
client = 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 csv
with 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.

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.