# LinkedIn Post Search Scraper - $0.90/1k, No Login (`dami_studio/linkedin-post-search-scraper`) Actor

Search public LinkedIn posts by keyword and get the author, full post text, exact posted time, reaction and comment counts and the post URL. No login, no cookies, no proxy to supply. At $0.90 per 1,000 posts it is the lowest per-post price on the market.

- **URL**: https://apify.com/dami\_studio/linkedin-post-search-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 public linkedin posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## LinkedIn Post Search Scraper

Type keywords, get public LinkedIn posts back as rows: who wrote it, the full post text, the exact
timestamp it was published, how many reactions and comments it has, and the link to the post.

No LinkedIn account. No cookies. No session token to paste in. No proxy of your own to buy.

### Price

**$0.90 per 1,000 posts** ($0.0009 per post), plus **$0.003 to start a run**.

That per-post rate is the lowest on the Apify store for LinkedIn post search. It is flat: the same
$0.0009 per post on the free plan as on any paid plan, with no volume tiers, no minimum spend and no
monthly fee.

| Posts returned | What you pay |
|---|---|
| 10 | $0.012 |
| 100 | $0.093 |
| 1,000 | $0.903 |
| 10,000 | $9.003 |

#### What actually bills

- **$0.0009 per post row.** Charged once, immediately after a real post is written to the dataset.
  The event is called `post-found`.
- **$0.003 per started run.** One charge per run at the default 1 GB of memory. The start fee is
  billed per gigabyte, so if you raise the memory in the run options you raise the start fee with it.
  There is no reason to: this Actor is built for 1 GB.
- **Free: the sample row.** Start a run with empty input and you get one labelled row showing the
  output shape, marked `"_sample": true, "charged": false`. Nothing is billed for it.
- **Free: diagnostic rows.** If a query finds nothing, or an input field cannot be honoured, the
  Actor writes a row explaining why, marked `"_diagnostic": true, "charged": false`, and finishes
  successfully. Those rows are never charged.
- **Free: everything the Actor fetched and then discarded.** A run with a date, author or media
  filter reads more post pages than it returns, and a search engine sometimes answers with posts
  that have nothing to do with your keywords. You are billed for the rows you keep, never for the
  work it took to find them.

There is no free trial run. The list above is exactly what is charged, and nothing else is.

### Input

| Field | What it does |
|---|---|
| `searchQueries` | Keywords to search public posts with. Several queries per run is fine, up to 20. |
| `maxPosts` | Posts per query. `0` means "as many as discovery finds", bounded by `resultsLimit`. |
| `resultsLimit` | Hard cap on charged rows for the whole run. Max 1000. |
| `sortBy` | `relevance` keeps search ranking order, `date` puts the newest first. |
| `postedLimit` | `any`, `1h`, `24h`, `week`, `month`, `3months`, `6months`, `year`. Checked against each post's real publish timestamp. |
| `postedLimitDate` | Keep only posts published on or after this date. Accepts `2026-01-31`, a full ISO string, or a millisecond timestamp. |
| `authorUrls` | Profile or company URLs. Narrows results to posts written by those accounts. Works with or without keywords. |
| `contentType` | `all`, `videos`, `images`, `documents`. |
| `includeAuthorDetails` | Adds profile URL, avatar URL and follower count. Free, no extra request. |
| `requestTimeoutSecs` | Per-request timeout. Default 25. |

```json
{
  "searchQueries": ["b2b sales", "hiring software engineer"],
  "maxPosts": 25,
  "resultsLimit": 50,
  "sortBy": "date",
  "postedLimit": "month",
  "includeAuthorDetails": true
}
```

Run it with empty input and nothing is charged — you get one labelled sample row that shows every
field, so you can wire up your pipeline before spending anything.

#### Searching one person's or one company's posts

Put the profile or company URL in `authorUrls` and leave `searchQueries` empty to get that account's
recent public posts, or fill in both to get only their posts about a topic:

```json
{
  "authorUrls": ["https://www.linkedin.com/in/williamhgates"],
  "searchQueries": ["climate"],
  "maxPosts": 20
}
```

### Output

One row per post. This is a real row, trimmed only in the `text` field:

```json
{
  "ok": true,
  "_sample": false,
  "charged": true,
  "postUrl": "https://www.linkedin.com/posts/coquinn_b2bsales-saleswisdom-thoughtleadership-activity-7378846497251106816-cw7k",
  "activityId": "7378846497251106816",
  "authorName": "Corey Quinn",
  "authorType": "Person",
  "text": "What B2B Sales Taught Me About B2B Sales:\n\nI've been in B2B sales for literally years. Here's what I learned...",
  "headline": "B2B Sales: What I Learned",
  "postedAt": "2025-09-30T17:41:37.263Z",
  "reactionsCount": 151,
  "commentsCount": 41,
  "postType": "SocialMediaPosting",
  "mediaType": "text",
  "videoUrl": null,
  "imageUrl": null,
  "query": "b2b sales",
  "retrievedAt": "2026-08-15T13:12:44.907Z",
  "authorUrl": "https://www.linkedin.com/in/coquinn",
  "authorImageUrl": "https://media.licdn.com/dms/image/v2/D5603AQFJV5OVNEHgbQ/profile-displayphoto-shrink_400_400/0/1711075880570",
  "authorFollowers": null
}
```

| Field | Notes |
|---|---|
| `postUrl` | The canonical post URL. A repost resolves to the original post here. |
| `activityId` | LinkedIn's numeric id for the post. Stable, good as a primary key for deduplication. |
| `authorName`, `authorType` | `authorType` is `Person` or `Organization`. |
| `text` | The full public post body, newlines and emoji intact. |
| `headline` | LinkedIn's own short title for the post. Often the first line. |
| `postedAt` | Exact ISO timestamp, to the millisecond. Not "3d ago". |
| `reactionsCount`, `commentsCount` | The public counters at the moment the post was read. |
| `mediaType` | `text`, `images`, `videos` or `documents`. |
| `videoUrl`, `imageUrl` | Direct media URLs when the post carries them, otherwise `null`. |
| `query` | Which of your `searchQueries` produced this row. |
| `authorUrl`, `authorImageUrl`, `authorFollowers` | Only when `includeAuthorDetails` is true. `authorFollowers` is frequently `null` — LinkedIn does not publish it on every post page. |

Rows that are not posts are clearly labelled and never billed: `_sample: true` for the empty-input
example row, `_diagnostic: true` with an `errorCode` and a plain-English `details` string for
anything that went wrong.

### How it works, briefly

LinkedIn's own content search redirects a logged-out visitor to the login page, so this Actor does
not use it. What it uses is the fact that every individual `linkedin.com/posts/...` page is public
and indexed: it finds the post URLs through public web indexes, then reads each post page directly
and pulls the structured record LinkedIn publishes on it. That record is where the author, the exact
timestamp, the body text and the reaction and comment counters come from — they are not guessed from
a search snippet.

Two consequences worth knowing about. First, the date on every row is exact, and the date filter is
applied before a post page is ever downloaded, so a "past week" run does not waste your run time
opening four-year-old posts. Second, if a search engine cannot match your keywords it will sometimes
answer with real but unrelated LinkedIn posts. The Actor checks that each post actually mentions
something you searched for and drops the ones that do not, uncharged, rather than billing you for
them.

Requests go out over rotating datacenter addresses. There is no browser anywhere in this Actor,
which is a large part of why the price is what it is.

### Limits, honestly

- **About 90 posts per query is the ceiling.** The public indexes hand back roughly that many
  distinct post URLs for a given phrase, and asking for more does not produce more. If you need
  thousands of posts on a topic, split it into several narrower queries — `maxPosts` applies per
  query, `resultsLimit` caps the run.
- **Very fresh posts take time to appear.** A post published in the last hour is often not indexed
  yet. `postedLimit: "1h"` is supported and filtered exactly, but expect thin results; `24h` and
  `week` are much more productive.
- **Coverage is what the public web has indexed, not LinkedIn's internal search.** Well-discussed
  topics, hashtags and named people are covered well. Very obscure phrases are not.
- **Some posts cannot be read.** Members-only updates, deleted posts and a small number of pages
  return no public record. Those are skipped silently and never billed.
- **`contentType` values `jobs`, `live_videos` and `collaborative_articles` are not detectable** on
  a public post page. They are accepted so an existing input file still runs, and the Actor writes a
  free diagnostic row saying the filter was not applied instead of pretending it was.
- **`authorFollowers` is often null.** LinkedIn only publishes it on some post pages.
- **Counts are a snapshot.** `reactionsCount` and `commentsCount` are the numbers at the moment of
  reading, not a running total.
- **Reactions are a total, not a breakdown.** You get how many, not how many of each type, and not
  who reacted.
- **Comment bodies are not returned.** You get `commentsCount`. The comments themselves are a
  different job.
- **The author filter is applied to the post's own identifier**, which is reliable, but a company
  that posts under several pages needs each page URL listed.
- **A run has a page budget, and a very selective filter can hit it.** Each run starts with an
  allowance of pages it may read and earns more as it delivers posts, so a request that needs a
  hundred pages to produce three matches stops early rather than burning your run time. It writes a
  free `PAGE_BUDGET_REACHED` row when that happens, and you are charged only for the posts you got.
  In practice only heavy `contentType` or multi-author runs reach it; a normal keyword run does not.
- **If every public index is rate-limiting at that moment**, the run finishes with a free
  `SEARCH_ENGINES_REFUSED` row and no charge beyond the start fee. Running again a few minutes later
  normally works — the Actor reads three independent indexes and only one has to be up.

### FAQ

**Do I need a LinkedIn account, cookies or a session token?**
No. Nothing is logged in and nothing is stored. Only pages LinkedIn serves publicly are read.

**Is this legal / is it scraping private data?**
It reads only what LinkedIn publishes to anyone without an account, the same pages a search engine
indexes. No private profiles, no connections, no email addresses, no member-only content.

**What is the cheapest way to run it?**
Ask for what you need. Charging is per returned post, so `maxPosts: 20` costs $0.018 plus the
$0.003 start fee. There is no penalty for many small runs beyond the start fee, and no discount for
one big one.

**What happens if my keywords return nothing?**
The run finishes as SUCCEEDED with one free diagnostic row explaining why, and you are charged only
the start fee. A failed or empty run never bills for results. If the first three queries in a run
all come back empty, the Actor stops early and says so rather than spending the rest of your run
time on the same outcome.

**Can I get posts from a specific person or company?**
Yes — `authorUrls`. Leave `searchQueries` empty for everything recent from them, or combine both to
filter by topic.

**Can I filter by date?**
Yes, two ways, and they stack. `postedLimit` is a rolling window (`24h`, `week`, `month` and so on)
and `postedLimitDate` is a fixed date. Both are checked against the post's real publish timestamp,
not against a relative "2w" label.

**How do I get more than 90 posts on a topic?**
Use several queries in one run. `["b2b sales", "b2b sales prospecting", "b2b sales pipeline"]` will
find substantially more distinct posts than any single phrase, because each phrase reaches a
different slice of the index.

**Does it work in languages other than English?**
Yes. The post text is returned exactly as published, in any script, and non-English keywords work.

**Can I schedule it?**
Yes, like any Actor. `sortBy: "date"` with `postedLimit: "24h"` is a good daily monitor. Use
`activityId` to deduplicate against what you already stored.

**What do I do about duplicates across runs?**
Key on `activityId`. It is LinkedIn's own id for the post and it does not change.

**Can I use my own proxies?**
The Actor manages its own egress and does not need any input from you. It never routes traffic
through metered per-gigabyte proxies, which is what keeps the per-post price this low.

**Will it ever fail a run?**
Only on a genuine crash. Blocks, empty results and bad input all finish as SUCCEEDED with a free
diagnostic row, because a failed run still costs you a start fee and being told "you forgot a field"
is not worth paying for.

### Integration

The dataset is available as JSON, CSV, Excel, XML and RSS through the standard Apify dataset API, so
it drops into Make, Zapier, n8n, Google Sheets or a plain HTTP call. To use it from the API:

```
POST https://api.apify.com/v2/acts/dami_studio~linkedin-post-search-scraper/run-sync-get-dataset-items?token=<YOUR_TOKEN>
Content-Type: application/json

{"searchQueries":["b2b sales"],"maxPosts":25}
```

That returns the rows directly in the response body.

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords to search public LinkedIn posts with. One run handles several queries.

## `maxPosts` (type: `integer`):

How many posts to return for each query. 0 means 'as many as discovery finds', bounded by the total limit. Discovery tops out around 90 posts per query.

## `resultsLimit` (type: `integer`):

Hard cap on charged rows for the whole run, across every query.

## `sortBy` (type: `string`):

'relevance' keeps search ranking order, 'date' puts the newest posts first.

## `postedLimit` (type: `string`):

Only keep posts newer than this window. Checked against each post's real publish timestamp.

## `postedLimitDate` (type: `string`):

Only keep posts published on or after this date. Accepts '2026-01-31', a full ISO string, or a millisecond timestamp.

## `authorUrls` (type: `array`):

LinkedIn profile or company URLs. Results are narrowed to posts written by them, for example https://www.linkedin.com/in/williamhgates.

## `contentType` (type: `string`):

Filter by the media in the post. Videos, images and documents are detected from the post page; jobs, live videos and collaborative articles are not detected and behave as 'All'.

## `includeAuthorDetails` (type: `boolean`):

Add the author's profile URL, avatar URL and follower count to every row. Free, no extra request.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout for search pages and post pages.

## Actor input object example

```json
{
  "searchQueries": [
    "b2b sales"
  ],
  "maxPosts": 20,
  "resultsLimit": 50,
  "sortBy": "relevance",
  "postedLimit": "any",
  "authorUrls": [],
  "contentType": "all",
  "includeAuthorDetails": true,
  "requestTimeoutSecs": 25
}
```

# Actor output Schema

## `results` (type: `string`):

Every public LinkedIn post the run matched, one row each, in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "b2b sales"
    ],
    "maxPosts": 20,
    "resultsLimit": 50,
    "sortBy": "relevance",
    "postedLimit": "any",
    "authorUrls": [],
    "contentType": "all",
    "includeAuthorDetails": true,
    "requestTimeoutSecs": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/linkedin-post-search-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["b2b sales"],
    "maxPosts": 20,
    "resultsLimit": 50,
    "sortBy": "relevance",
    "postedLimit": "any",
    "authorUrls": [],
    "contentType": "all",
    "includeAuthorDetails": True,
    "requestTimeoutSecs": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/linkedin-post-search-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "b2b sales"
  ],
  "maxPosts": 20,
  "resultsLimit": 50,
  "sortBy": "relevance",
  "postedLimit": "any",
  "authorUrls": [],
  "contentType": "all",
  "includeAuthorDetails": true,
  "requestTimeoutSecs": 25
}' |
apify call dami_studio/linkedin-post-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/linkedin-post-search-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/rI3qWzuQwGE1Mggrp/builds/0Baea3vf3RALouZWf/openapi.json
