Use LinkedIn Keyword & Hashtag Post Discovery Scraper with Python
Search LinkedIn posts by keyword or hashtag with filters for date, author type, industry, connection degree, mentions, and minimum reactions. Requires a LinkedIn session cookie.
1. Choose the data you need
Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.
2. Prepare a source-based input
This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.
{
"authorType": "",
"contentType": [
"articles",
"posts",
"documents",
"videos",
"images"
],
"countryCode": "auto",
"datePosted": "",
"emitNoResultsMarker": true,
"enrichSdui": false,
"excludeReposts": false,
"hydratePosts": true,
"includeDocumentDetails": false,
"includeRepostCounts": false,
"includeUnmatched": false,
"includeVideoTranscript": false,
"language": "en",
"maxResults": 3,
"mentions": [],
"mentionsCompany": "",
"mentionsPerson": "",
"minComments": 0,
"minReactions": 0,
"mode": "auto",
"monitorMode": false,
"postUrls": [],
"postedAfter": "",
"postedBefore": "",
"searchQueries": [],
"searchQuery": "artificial intelligence",
"sortBy": "relevance",
"talksAbout": []
}
Download input.example.json and save your reviewed copy as input.json.
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | optional | What to run. 'auto' searches when searchQuery/searchQueries are set and enriches when postUrls are set (both can run together). |
searchQuery | string | optional | Keyword, phrase, or hashtag to find in public LinkedIn posts. Examples: 'artificial intelligence', '"product launch"', '#MachineLearning'. (mode=search/auto) |
searchQueries | array | optional | Additional queries to run in the same run; each is searched independently, duplicates are delivered once. Combine with Search Query. (mode=search/auto) |
postUrls | array | optional | LinkedIn post or article URLs to enrich directly (activity, share, ugcPost, feed/update, /pulse/ links and country subdomains). (mode=postUrls/auto) |
sortBy | string | optional | How to order the posts that were found. 'relevance' keeps the search engine's order; 'recency' sorts the found posts by date (newest first). |
datePosted | string | optional | Only keep posts from this time window. |
postedAfter | string | optional | Only keep posts published on or after this date (YYYY-MM-DD). Leave empty to disable. |
postedBefore | string | optional | Only keep posts published on or before this date (YYYY-MM-DD). Leave empty to disable. |
authorType | string | optional | Keep only posts written by a person or by a company page. Applied after each post is opened, so a post whose author type cannot be determined is kept. |
contentType | array | optional | Keep only these post types: articles (LinkedIn articles), posts (text/reposts/polls/events), documents (PDF/PPTX document cards), videos, images. |
minReactions | integer | optional | Keep only posts with at least this many reactions. Posts whose reaction count is unknown are skipped when this is above 0. |
minComments | integer | optional | Keep only posts with at least this many comments. Posts whose comment count is unknown are skipped when this is above 0. |
excludeReposts | boolean | optional | When enabled, skip posts that repeat someone else's content. |
includeUnmatched | boolean | optional | When enabled, posts that do not show every word of your query are also returned, marked keywordMatch: false. Useful for hashtags. |
language | string | optional | Preferred interface language used when opening each post page (affects LinkedIn's rendered text). |
countryCode | string | optional | Prefer LinkedIn posts indexed on this country's LinkedIn domain (site:xx.linkedin.com). 'auto' uses linkedin.com worldwide. |
maxResults | integer | optional | Maximum number of posts to return per run, after filters. The search index holds fewer posts for narrow queries. |
hydratePosts | boolean | optional | When enabled, each found post is opened so the record carries the full text, author, date, engagement counts and top comments. Turn off for faster runs with link + snippet only. |
includeVideoTranscript | boolean | optional | When enabled and a video post exposes a transcript on its public page, the transcript is added to the record. |
monitorMode | boolean | optional | When enabled, only posts that were not delivered by an earlier run with the same input are returned. Ideal for schedules. |
emitNoResultsMarker | boolean | optional | When enabled and a clean search finds nothing, push one marker record ({searchQuery, noResults: true}) so zero matches can be told apart from a failed run. |
mentions | array | optional | Additional words added to every search query, for example a company or person name. |
mentionsCompany | string | optional | Company name added to the search query. Numeric LinkedIn IDs cannot be used without a session and are ignored (a note is added to the run status). |
mentionsPerson | string | optional | Person name added to the search query. Numeric LinkedIn IDs cannot be used without a session and are ignored (a note is added to the run status). |
talksAbout | array | optional | Topics or hashtags added to the search query; each value becomes #topic. Values with a leading # are accepted as-is. |
includeDocumentDetails | boolean | optional | For document (PDF/PPTX) posts, also fetch the per-page transcript and page-image URLs in addition to the document URL and page count. |
includeRepostCounts | boolean | optional | Last-resort fallback: if the richer public guest layout was not obtained for a post, retry a few more times to catch the repost/share counts. Normally unnecessary when the guest session is active. |
enrichSdui | boolean | optional | Use LinkedIn's anonymous public guest session (an ephemeral cookie issued by the site, like a normal browser) to read the richer public post layout. This adds reaction-type breakdowns, up to ~18 comments with profile links, and repost counts when available. No user account or credentials are involved. |
3. Start an Actor run
Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.
Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.
import json
import os
import urllib.request
# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
actor_input = json.load(stream)
token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
"https://api.apify.com/v2/acts/wuq1JzPEwyJuIWboy/runs?timeout=120&maxTotalChargeUsd=0.50",
data=json.dumps(actor_input).encode("utf-8"),
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.
4. Inspect the actual results
Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.
One record per matching LinkedIn post, with full text, author, date, engagement metrics, top comments and media where the public post page exposes them. A no-results marker row ({searchQuery, noResults: true}) is added when a clean search finds nothing.
Questions before you run
Which inputs are required?
No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.
What does this guide verify?
The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.
What if the run request times out?
Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.
Generated from source build zL8UURqaufW1YxiHA and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.