LinkedIn Profile Posts Lookup: posts scraper from $3/1k
Pricing
from $2.28 / 1,000 post returneds
LinkedIn Profile Posts Lookup: posts scraper from $3/1k
LinkedIn posts scraper with no cookies and no account. Give it public LinkedIn profile links and get one flat row per post: link, exact timestamp, text, reaction and comment counts, media type and repost flag. Nothing found, nothing charged.
Pricing
from $2.28 / 1,000 post returneds
Rating
0.0
(0)
Developer
Adrian Voss
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
LinkedIn Profile Posts Lookup: LinkedIn Posts Scraper, No Login
Give it public LinkedIn profile links and get one flat row per post: the permalink, an exact timestamp, the post text, reaction and comment counts, what media it carried, whether it links out, and whether it is a repost of someone else. No account, no cookies, no session token. You pay per post that came back, and a profile that returns nothing costs nothing.
Who it's for
The people who need this are usually watching a short list of named humans rather than crawling LinkedIn at large.
- Founders and demand-gen teams running a warm-outbound motion. You have thirty prospects in a table and you want a first line that references what each of them actually said last week, not a merge field. One run gives you their recent posts with dates you can sort by.
- Competitive and partner intelligence. Track what the four executives at a competitor are publishing, how it is landing, and what they are amplifying from other people. The repost flag and the original author column make "what are they signal-boosting" a filter rather than a reading exercise.
- Creator and influencer scouting. Reaction and comment counts per post, with the exact post time, so you can rank a shortlist on engagement that is real and recent instead of on follower count.
- Anyone feeding an agent or an LLM. Field names are stable, one post is one row, and the timestamp is ISO 8601, so a model gets a table it can reason over instead of "3d ago".
If you need a member's whole posting history back to 2015, this is the wrong tool and the FAQ says so plainly.
Why this one
- Exact post times, not "2w". LinkedIn prints a relative age next to every post and that is
all most scrapers pass on. A post's activity id is a Snowflake-style integer whose top bits are
the millisecond it was created, so this actor decodes the real timestamp and returns it as ISO
8601 in
postedAt. It was checked against all nine posts in the capture this actor was built from and agreed with LinkedIn's own label every time.postedAtTextkeeps the page's wording next to it. "Everything since Tuesday" becomes a filter you can write. - No login, no cookies, no session token, and the actor has no field to put one in. It reads
the same public page a signed-out browser gets. When LinkedIn decides a request needs an
account it answers with a sign-in wall, and this actor reports that as
BLOCKEDand charges you nothing rather than trying to get around it. - A repost is one row and it says so. LinkedIn renders a repost as a card wrapping the
original post. Read carelessly that is two rows for one post, and you get billed twice for a
duplicate. Here it is one row with
isRepost: trueandoriginalAuthorfilled in. - You are billed per post, and you can cap it.
maxPostssets the most any single profile can return and therefore the most it can cost. A profile that returns nothing is a free row. - Reactions and comments are the page's own numbers. Exact integers off the markup, not
rounded display strings. A post published minutes ago that genuinely has no reactions yet comes
back as
null, not a fake zero.
What you get
One row per post, with the profile-level fields repeated on each row so the table joins cleanly.
| Field | What it is |
|---|---|
profileUrl, publicId, authorName | Who the posts belong to, echoed on every row |
postCount | How many posts this run returned for that profile, which is also what it billed |
postsAvailable | How many the page actually carried, before maxPosts capped it |
postUrl, postId | The post permalink and its numeric activity id |
postedAt | Exact ISO 8601 timestamp, decoded from the activity id |
postedAtText | LinkedIn's own wording: Now, 5h, 3d, 2w |
text | The poster's commentary, line breaks kept, cut at 1000 characters |
textTruncated | True when the cut happened, so you know the text is partial |
reactionCount, commentCount | Exact counts, or null when the page published none |
mediaType | video, image, document, article or none |
hasLink, linkUrl | Whether the post sends people somewhere, and where, unwrapped out of LinkedIn's redirect |
isRepost, originalAuthor | Whether this is someone else's post being amplified, and whose |
query, found, status, message, scrapedAt | The standard result columns on every actor here |
Two things the public page does not publish, and this actor therefore does not invent:
- There is no repost or share counter anywhere in the markup. Reaction and comment counts are the only numbers LinkedIn emits. Rather than ship a column that is empty on every row, there is no repost-count column at all.
- There is no full archive. The page carries a recent window, nine posts in the profile this was built against, and there is no next page a signed-out visitor can request.
Pricing
Misses are free. A profile that is blocked, gone, or has no public activity produces a row with
found: false and is never charged.
Use it from Clay, n8n, Make, or an AI agent
This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.
curl "https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-posts-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"profileUrls":["https://www.linkedin.com/in/satyanadella","reidhoffman"]}'
n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-posts-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"profileUrls":["https://www.linkedin.com/in/satyanadella","reidhoffman"]} (swap in an expression from an earlier node for a real value).
Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-posts-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"profileUrls":["{{profile}}"]}, mapping the row's profile into the profileUrls array.
MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "LinkedIn Posts Scraper, No Login | Apify" — the agent will find and run this actor.
How to use
- In the Apify Console. Open the actor page and click Start — the
profileUrlsfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - Via the API. Call it directly with a POST request — no Console needed once you have an API token:
curl "https://api.apify.com/v2/acts/accountable_eel~linkedin-profile-posts-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \-X POST \-H "Content-Type: application/json" \-d '{"profileUrls":["https://www.linkedin.com/in/satyanadella","reidhoffman"]}'
- On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.
Input
{"profileUrls": ["https://www.linkedin.com/in/satyanadella","reidhoffman"]}
One profile per line. Paste the full profile link, the /recent-activity/ link you copied while looking at someone's posts, or just the part after /in/. Accepted formats: https://www.linkedin.com/in/williamhgates, https://www.linkedin.com/in/satyanadella/recent-activity/all/, satyanadella.
Useful options, all optional:
| Option | What it does |
|---|---|
maxPosts | The most posts one profile may return, newest first. This is also the most it can cost you for that profile. |
testRun | Process only the first five profiles, so you can check the shape before spending on the whole list. |
onlyFound | Drop the rows that came back with nothing. Misses are free either way. |
includeKeywords / excludeKeywords | Keep or drop posts by the words in them. Filtered-out posts are not billed. |
columns | Choose which fields land in the table. |
Output
| query | found | status | profileUrl | publicId | authorName | postCount | postsAvailable | posts | postUrl | postId | postedAt | postedAtText | text | textTruncated | reactionCount | commentCount | mediaType | hasLink | linkUrl | isRepost | originalAuthor | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| https://www.linkedin.com/in/satyanadella | true | OK | <all posts found (full list)> | <posted at (exact)> | <posted (as linkedin shows it)> | <text was cut at 1000 characters> | 1970-01-01T00:00:00.000Z |
Tips
- Sort on
postedAt, filter onpostedAtTextnever. The relative label is what the page said at fetch time and it ages the moment you store it. The ISO timestamp does not. excludeKeywordsis the cheap way to drop noise. A post filtered out by your keywords is not billed, so a run that only wants posts mentioning your category costs less than the same run unfiltered.- Set
maxPoststo 3 for a wide sweep. If you are checking two hundred people for "posted anything recently", three posts each answers it at a fifth of the cost of twenty. - Leave the residential proxy on. LinkedIn's sign-in wall is IP-reputation driven. The very first probe of this build took a wall on a clean home address, so a datacenter address is not a saving.
- Keep concurrency low. Two at a time is the default for a reason. Pushing it raises the
share of rows that come back
BLOCKED, and while those are free, they are also not data.
vs. alternatives
| What it costs | What you get | Trade-off | |
|---|---|---|---|
This actor (linkedin-profile-posts-lookup) | $0.003 per post returned, $0.00005 actor start, nothing for a miss | One row per post with an exact ISO timestamp, text, reaction and comment counts, media type, outbound link and a repost flag | It reads the recent window the public page serves, roughly the last handful of posts. There is no way to page back through a member's history without an account, and this actor will not try. |
| harvestapi/linkedin-profile-posts | $2 per 1,000 posts | The category leader by a distance, 8,248 users in the last 30 days at a 4.41 rating, with deeper post history | Cheaper per post and it goes back further. Pick this one when you need the exact publish time, a repost separated from an original, or a per-profile cost cap. |
| Clay | $0.08 to $0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace, waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you are not counting rows, that is Clay. This is one column, priced per column, callable from Clay via its HTTP step. |
| Doing it yourself | Your time, plus a sign-in wall that fires on reputation rather than on rate, and markup that nests a repost inside its own container | The same posts | This absorbs the wall handling, the session rotation, the repost de-duplication and the timestamp decoding, and it stops charging the moment a page comes back empty. |
Prices for third-party tools are their published list prices as of September 2026 and are not tracked here. Check the vendor before relying on the comparison.
FAQ
Do I need a LinkedIn account, a cookie, or a li_at token?
No, and there is nowhere to put one. The actor requests the public profile page a signed-out
browser gets. If LinkedIn decides that request needs an account, the row comes back BLOCKED
and free.
How far back does it go?
As far as the public page carries, which was nine posts for the profile this was built against
and varies by member. There is no page-two for a signed-out visitor, so maxPosts can lower
what you get but never raise it above what the page served. postsAvailable tells you what was
there.
Why is reactionCount sometimes empty?
Because the page published no number for that post. That is normal on a post published minutes
ago. It is reported as null rather than 0, because "nobody has reacted yet" and "LinkedIn
did not say" are different facts.
Why is there no repost count? Because LinkedIn's public page does not emit one. The only social numbers in the markup are reactions and comments. A column that is empty on every row is worse than no column.
A repost shows the original author's name. Is that a bug?
No. When someone reposts without commentary, LinkedIn's own permalink points at the original
post, so that is what postUrl carries. isRepost is true and originalAuthor names the
person who wrote it. Filter on isRepost if you only want a person's own writing.
Can it read comments, or who reacted? No. The public page renders a comments tab and a reactions tab, and both hold other people's posts and other people's names. This actor reads only the member's own Posts tab.
What happens to a profile with no public activity?
One free row, found: false, with a message saying the page loaded but published no posts a
signed-out visitor can read. Nothing is charged.
Does it work for company pages?
No. Feed it a /company/ link and it returns a free BAD_FORMAT row without spending a
request. Company pages have their own actor, linkedin-company-posts-lookup.
Is the exact timestamp really exact?
It is decoded from the post's own activity id, and it was checked against every relative label
on the page it was built from. If LinkedIn ever changes how those ids are minted, postedAt
comes back null rather than wrong, and postedAtText still carries the page's own wording.
Personal data, and your responsibility
This actor reads public LinkedIn profile pages only. It never signs in, never sends a cookie or a session token, and never requests anything behind the sign-in wall.
What it returns is still personal data about identifiable people. If the GDPR, the UK GDPR, the CCPA or a comparable law applies to you, you need your own lawful basis for collecting and using it, and your own answer on retention, notice and the rights of the people in your table. LinkedIn's User Agreement also restricts automated collection from its services, and that agreement is between you and LinkedIn.
None of this is legal advice, and nothing here says that any particular use is lawful. That call is yours.
Related actors
- LinkedIn Company Posts Lookup: the same thing for company pages.
- LinkedIn Profile Lookup: who the person is, rather than what they posted.
- LinkedIn Company Lookup: firmographics behind a company page.
- LinkedIn Jobs Search Lookup: open roles from LinkedIn's public job search.