Threads Scraper โ Posts, Profiles, Replies & Search
Pricing
from $1.05 / 1,000 posts
Threads Scraper โ Posts, Profiles, Replies & Search
Scrape public Threads posts without login: profile feeds, a post with its replies, or keyword search. Text, likes, replies, reposts, media, quoted post and author with followers in clean JSON. Pay per post.
Pricing
from $1.05 / 1,000 posts
Rating
0.0
(0)
Developer
Cliqto Media
Maintained by CommunityActor stats
0
Bookmarked
8
Total users
4
Monthly active users
2 hours ago
Last modified
Categories
Features
Share
Scrape public Threads posts with no login: the latest posts, replies or reposts of a profile, one post with its replies, or the top posts for a keyword. Each post is one clean JSON row with text, date, likes, replies, reposts, media, the quoted post and the author with followers.
Use it for social listening, brand and creator research, or AI pipelines. The smallest run is one username, such as zuck, with 20 posts: it finishes in under 10 seconds and costs $0.032 on the Free plan.

Table of contents
- What it does
- How it works
- Quick start
- Use cases
- Input
- Output
- Pricing
- Limits
- Switching from another Threads scraper
- API, export and integrations
- Troubleshooting
- FAQ
- Related Actors
- Support
What it does
Threads Scraper reads public pages of threads.com the way a logged-out visitor sees them. It needs no account, no cookies and no API token. It has three modes:
| Mode | You give | You get |
|---|---|---|
profile | Usernames or profile links | The latest posts of each profile (or its replies or reposts tab), with the author's follower count |
post | Post links (threads.com or threads.net) | The post, the author's own thread below it, and the replies Threads shows without login |
search | Keywords or hashtags | The top posts Threads shows for each keyword |
A profile, post or search with no public posts returns one not_found row with the reason. It does not disappear silently, so you can see what happened to every input.
Good fit
- Track what a brand, creator or competitor posts on Threads, with likes, replies and reposts.
- Get only new posts since your last run with Posted after, for example
7 days. - Collect a post and its replies for sentiment or discussion analysis.
- Find top posts about a topic or hashtag.
- Feed clean, typed Threads rows into an AI agent, a spreadsheet, a database, Slack or Notion.
Not a fit
- Only public data. No DMs, no private profiles, nothing that needs login.
- No view counts: Threads does not show them without login.
- No followers or following lists and no account search: they need login.
- No "recent" search and no date-sorted search: threads.com redirects these to login.
- Replies of a post: only the first page Threads shows without login, usually 10โ15. More pages need login.
- Some large profiles are shown only to logged-in visitors. They return
not_found. The Actor does not try to get around this. - Counts are a snapshot at fetch time.
How it works
- You give usernames, post links or keywords. The Actor opens the matching public threads.com pages as a logged-out visitor, through Apify Proxy.
- It reads the posts on those pages and loads more pages of a profile until it has your number of posts, reaches Posted after, or the public feed ends. Duplicates are dropped.
- It saves one row per post to the Dataset, one
not_foundorerrorrow for every input without posts, and a run summary inRUN_SUMMARY. If you chose a connector, a short summary also goes to Slack or Notion.
Quick start
-
Open the Input tab. Mode is already Profile posts (box 1 in the picture).
-
Keep
zuckin Usernames, or add your own usernames or profile links (box 2). -
Keep Max posts per input at
20, the prefill and default (box 3). -
Click Start.
-
Open the Posts view in the Output tab and check the follower count, text, date, likes, replies and reposts (box 5).
Expected result: the 20 newest posts of the profile, one row per post.
Use cases
| Buyer job | Input path | Output entity | Key fields | Next integration |
|---|---|---|---|---|
| Brand or creator monitoring | mode: profile, usernames, postedAfter: "7 days" | post | text, createdAt, likes, replies, reposts | Weekly schedule, Google Sheets or Slack |
| Reply and repost activity of an account | mode: profile, profileTab: replies or reposts | post | text, replyToUsername, repostedPost, createdAt | Spreadsheet or database |
| Discussion analysis | mode: post, postUrls | post with role | role, text, replyToUsername, likes | Sentiment or topic analysis |
| Topic research | mode: search, queries | post | text, author.username, likes, url | AI summary, content ideas |
| Influencer shortlist | mode: profile, usernames | post with author | author.followers, author.verified, likes | Compare engagement per follower |
Input
Input fields
| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect | Example |
|---|---|---|---|---|---|---|---|
mode | Mode | string | no | profile | profile, post, search | Which list is read when several are filled: profile (default) reads all of them, post or search only its own | "post" |
usernames | Usernames | string[] | for profile | prefill ["zuck"] | up to 50; name, @name or a threads.com / threads.net profile link | Profiles to read | ["zuck", "@natgeo"] |
postUrls | Post URLs | string[] | for post | none | up to 50; โฆ/@user/post/CODE on threads.com or threads.net | Posts to read | ["https://www.threads.com/@zuck/post/DeZtp-TkQHh"] |
queries | Search keywords | string[] | for search | none | up to 50; up to 100 characters each | One search per line | ["web scraping", "#coffee"] |
maxItems | Max posts per input | integer | no | 20 (prefill 20) | 1โ500 | Cap for each username, post URL or keyword | 200 |
maxTotalItems | Max posts per run (optional) | integer | no | none | 1โ25,000 | Cap for the whole run; paid rows (posts and not_found) count toward it | 1000 |
includeReplies | Include replies (post mode) | boolean | no | true | Post mode: also save the author's thread and the replies; off = only the post | false | |
profileTab | Profile tab (profile mode) | string | no | threads | threads, replies, reposts | Which profile tab is read. replies and reposts give the first page only | "replies" |
postedAfter | Posted after (optional) | string | no | none | date 2026-10-01 or a period like 7 days, 2 weeks, 24 hours | Keep only newer posts (profile and search) | "7 days" |
postedBefore | Posted before (optional) | string | no | none | same as above | Keep only older posts (profile and search) | "2026-11-01" |
proxyConfiguration | Proxy | object | no | Apify Proxy (datacenter) | Apify Proxy groups or your own proxies | Where requests come from | {"useApifyProxy": true} |
deliveryConnector | Send results to (optional) | string | no | none | one of your MCP connectors in Apify Console | Where the short run summary goes | Slack or Notion connector |
deliveryTarget | Notion page or channel (optional) | string | no | none | Notion page link or Slack channel ID | Where in that service the summary goes | C0123456789 |
With mode: profile (the default) every filled list is read; post or search reads only its own list. Duplicate values are removed, also when the same profile is written two ways (for example @NatGeo and its link).
A value that cannot be read, such as bad name!!, does not stop the run. It becomes one free error row with errorCode: INVALID_INPUT and the reason, and the other values are scraped. The run fails with INVALID_INPUT only when nothing usable is left; then no requests are made and only the start is charged.
Interacting settings
| When | Input A | Input B | Effective behavior |
|---|---|---|---|
| Post mode | postUrls | maxItems | The post, its thread and its replies together count toward maxItems |
| Post mode | includeReplies: false | maxItems | Only the post itself, one row per URL |
| Search mode | queries | maxItems above 30 | Threads shows only the top ~25โ30 posts per keyword; add more specific keywords to get more posts |
| Profile mode | usernames | maxItems | Reading stops at the end of the profile's public feed, even if maxItems is higher |
| Profile mode | postedAfter | maxItems | Reading stops at the first older post that is not pinned; filtered posts are free |
| Post mode | postedAfter / postedBefore | Not used: the requested posts are returned as they are | |
| Any mode | maxTotalItems | maxItems | Each input gets up to maxItems; the run stops when maxTotalItems is reached and the remaining inputs are marked not_processed |
Input examples
New posts of several profiles in the last 7 days
{"mode": "profile","usernames": ["zuck", "natgeo", "https://www.threads.com/@nasa"],"maxItems": 100,"postedAfter": "7 days"}
Expected output: up to 100 post rows per profile from the last 7 days, with text, likes, media and author.followers.
A post with its replies
{"mode": "post","postUrls": ["https://www.threads.com/@zuck/post/DeZtp-TkQHh"],"includeReplies": true,"maxItems": 50}
Expected output: one row with role: target, rows with role: self_thread for the author's thread, and rows with role: reply.
Search with a run cap
{"mode": "search","queries": ["web scraping", "#coffee"],"maxItems": 30,"maxTotalItems": 50}
Output
Every row goes to the default Dataset. The Posts view in the Output tab shows the main columns. The run summary is in the key-value store as RUN_SUMMARY.
Output fields
| Field | Meaning | Type | Can be empty? |
|---|---|---|---|
status | complete (a post), not_found or error | string | no |
statusReason, errorCode | Why a not_found or error row has no post | string | null on posts |
postId, code, url | Threads post ID, short code and link | string | null on status rows |
mode, input | Mode and the username, post URL or keyword this row came from | string | no |
role | profile_post, search_result, target, self_thread or reply | string | null on status rows |
author | username, fullName, userId, verified, followers, profilePicUrl | object | followers is only filled in profile mode, else null |
text | Post text | string | can be empty for media-only posts |
createdAt | Publish time, ISO 8601 UTC | string | |
likes, replies, reposts, quotes, shares | Counts at fetch time | number | null when Threads does not show it, never a fake 0 |
mediaType | text, photo, video, carousel or other | string | |
media | Photos and videos: type, url, width, height, thumbnailUrl | object[] | empty for text posts |
quotedPost, repostedPost | postId, url, username, text, createdAt | object | null when not a quote or repost |
isReply, replyToUsername | Reply marker and who it replies to | boolean, string | |
linkUrl, linkTitle | Attached link (Threads redirect removed) and its title | string | null without a link |
topic | Topic tag | string | often null |
pinned | Pinned on the author's profile | boolean | |
fetchedAt | When the row was scraped, ISO 8601 UTC | string | no |
Media links are signed by Threads and stop working after some days. Download the files soon if you need them.
Status rows
status | When | errorCode |
|---|---|---|
not_found | The profile does not exist or is shown only to logged-in visitors | NOT_FOUND |
not_found | The profile is private | PRIVATE_PROFILE |
not_found | The profile has no public posts | NO_POSTS |
not_found | The post was deleted or is not public | NOT_FOUND |
not_found | The keyword has no results | NO_RESULTS |
error | A username, URL or keyword cannot be read (free) | INVALID_INPUT |
error | Threads failed or refused the request after retries (free) | RATE_LIMITED, BLOCKED, LOGIN_REQUIRED, SOURCE_UNAVAILABLE |
A source failure is never shown as "no results". It is an error row, and the run status says so.
Run summary (RUN_SUMMARY)
| Field | Meaning |
|---|---|
status | COMPLETE, PARTIAL, NOT_FOUND, NO_RESULTS, ERROR, INVALID_INPUT or CANCELLED |
partialReason | For PARTIAL: CHARGE_LIMIT, SOURCE_ERROR, SAFETY_LIMIT or CANCELLED |
message | One plain-English sentence about the result |
counts | inputs, postRows, chargedPostRows, emptyResultEvents, notFoundRows, errorRows, duplicatesSkipped |
inputs | One entry per input (input, status, posts, reason): complete, partial, not_found, no_results, error or not_processed. reason is MAX_TOTAL_ITEMS when the run cap stopped it, INVALID_INPUT: โฆ for a value that could not be read |
warnings | Plain-English notes, for example the first-page-of-replies limit or a field that was not used |
delivery | Only with a connector: status (delivered, skipped or failed) and the reason |
Send results to Slack or Notion
Pick your connector in the optional deliveryConnector field. After the run saves its data, the Actor posts a short summary (up to 25 rows and a link to the full dataset) there. Put a Notion page link or a Slack channel ID in deliveryTarget: in Notion every run is added on top of that one page, so your workspace does not fill up with new pages. Without a page link, Notion delivery is skipped and the log says so. A delivery error never fails the run and costs nothing extra. Connect a service once in Apify Console โ MCP connectors.
Output example
A real profile post, trimmed (long media and picture links shortened):
{"status": "complete","statusReason": null,"errorCode": null,"postId": "4007137472223477566","code": "DecNkzeCX8-","url": "https://www.threads.com/@instagram/post/DecNkzeCX8-","mode": "profile","input": "instagram","role": "profile_post","author": {"username": "instagram","fullName": "Instagram","userId": "63404918397","verified": false,"followers": 38354704,"profilePicUrl": "https://scontent-den2-1.cdninstagram.com/v/โฆ"},"text": "@hichiyan686 made a whole retro wall phone by hand โ๏ธโจ","createdAt": "2026-10-13T16:14:12.000Z","likes": 170,"replies": 10,"reposts": 6,"quotes": 1,"shares": 2,"mediaType": "video","media": [{ "type": "video", "url": "https://scontent-den2-1.cdninstagram.com/o1/v/โฆmp4?โฆ", "width": 720, "height": 1280, "thumbnailUrl": "https://scontent-den2-1.cdninstagram.com/v/โฆjpg?โฆ" }],"quotedPost": null,"repostedPost": null,"isReply": false,"replyToUsername": null,"linkUrl": null,"linkTitle": null,"topic": null,"pinned": false,"fetchedAt": "2026-10-14T15:36:27.390Z"}
null means Threads did not provide the value. It never stands for zero.
Pricing
You pay per event. Platform usage (compute, proxy, storage) is included in the price.
| Event | Free plan | Starter (Bronze) | Scale (Silver) | Business (Gold) and higher |
|---|---|---|---|---|
| Post | $0.0015 | $0.00135 | $0.0012 | $0.00105 |
| Empty result | $0.0005 | $0.0005 | $0.0005 | $0.0005 |
| Actor start | $0.002 | $0.002 | $0.002 | $0.002 |
- Post: one per post row saved (profile post, the target post, a thread post, a reply or a search result). So 1,000 posts cost $1.50 on the Free plan and $1.05 on Business.
- Empty result: one per input that returned no posts: a profile that does not exist, is private, has no posts or is shown only to logged-in visitors; a missing post; a search with no results. It covers the requests made for that input.
- Actor start: charged by Apify once per run, per GB of memory. The default 512 MB counts as one.
- Free:
errorrows (Threads failed or a value could not be read), posts dropped by the date filter, duplicate posts and duplicate inputs.
Cost examples (Free plan prices)
| Scenario | Input | Charged events | Cost |
|---|---|---|---|
| Prefill | 1 profile, 20 posts | start + 20 posts | $0.032 |
| 1,000 posts | 5 profiles ร 200 | start + 1,000 posts | $1.502 |
| Profile that does not exist | 1 username | start + 1 empty result | $0.0025 |
| Only invalid values | 1 value | start | $0.002 |
Spending limit
Set Maximum cost per run in the run options. The Actor checks the limit before every post. When the next post does not fit, the run stops with status PARTIAL and partialReason: CHARGE_LIMIT, and keeps everything saved so far. The platform's max paid results option works the same way for the number of rows.
Limits
- Up to 50 usernames, post URLs or keywords per run, up to 500 posts per input, and up to 25,000 posts per run with
maxTotalItems. - One run has about 4 minutes of scraping time. In tests, 10 profiles ร 200 posts gave 1,563 posts and 2
not_foundrows in 53 seconds. A bigger request stops at this limit withPARTIALandSAFETY_LIMIT, keeps the posts saved so far, and charges only for them. For more data, use several runs. - A profile ends where its public feed ends, even if
maxItemsis higher. - Profile tabs
repliesandreposts: the first page only, as Threads shows it without login. - Search: top ~25โ30 posts per keyword. Replies of a post: the first page only, usually 10โ15.
author.followersis filled only in profile mode. Threads does not show it on post and search pages without login.- Speed: a 20-post profile takes under 10 seconds. Three inputs are read at the same time; a slow request is retried in a new proxy session after 12 seconds.
Switching from another Threads scraper
You can keep the input you used with another Threads scraper. The Actor reads these field names too and picks the mode from the filled list. Mixed lists (urls, targets, links) are split per value: a post link goes to post mode, a profile name or link to profile mode, a search link to search.
| Your field | Read as |
|---|---|
profiles, user, username, handles, users, profileUrls | usernames |
post_links, post_urls, postLinks, startUrls (strings or { "url": โฆ }), posts | postUrls |
searchQueries, keywords, searchTerms, searchQuery, query, search, hashtags | queries |
maxPosts, max_posts, maxResults, maxItemsPerKeyword, maxPostsPerProfile, maxPostsPerQuery, maxPostsPerSource, maxResultsPerInput, resultsLimit, limit | maxItems |
maxTotalPosts, maxTotalResults | maxTotalItems |
start_date, startDate, afterDate, after, onlyPostsNewerThan, dateFrom, since | postedAfter |
end_date, endDate, beforeDate, before, onlyPostsOlderThan, dateTo, until | postedBefore |
action or mode: user, posts, user_posts | mode: profile |
action or mode: user_replies, user_reposts | mode: profile with profileTab: replies or reposts |
action or mode: urls, post_link, thread | mode: post |
action or mode: keyword, hashtag | mode: search |
maxReplies, fetchAllReplies | includeReplies |
Output field names are camelCase and the author is a nested object. Typical mapping:
| Other Actor | This Actor |
|---|---|
post_url, post_code | url, code |
text_content | text |
created_at | createdAt |
like_count, reply_count, repost_count, quote_count, share_count | likes, replies, reposts, quotes, shares |
media_type, media_url(s) | mediaType, media[].url |
quoted_post_url | quotedPost.url |
is_reply, reply_to_username | isReply, replyToUsername |
is_pinned, topic_tag, link_title | pinned, topic, linkTitle |
username, display_name, user_id, is_verified, followers_count, profile_pic_url | author.username, author.fullName, author.userId, author.verified, author.followers, author.profilePicUrl |
scraped_at | fetchedAt |
Extra here: a post with its replies in the same Actor (role shows target, thread or reply), and not_found rows that explain every missing input. Not here: view counts, bios, followers lists and recent search.
API, export and integrations
- API: start a run with
POST https://api.apify.com/v2/acts/cliqtomedia~threads-scraper/runsand the JSON input above. Read rows from the run's default Dataset, for example with?view=posts. - Export: the Dataset downloads as JSON, CSV, Excel, XML or RSS from the Output tab or the API.
- Scheduling: use Apify Schedules, for example a daily run over your list of profiles with
postedAfter: "1 day". - Integrations: Make, Zapier, n8n, Google Sheets and webhooks connect through the Apify integrations tab. Slack and Notion also work through
deliveryConnector.
Use with AI agents (MCP)
- MCP server with only this Actor:
https://mcp.apify.com?tools=cliqtomedia/threads-scraper. - Smallest useful input:
{"mode": "profile", "usernames": ["zuck"], "maxItems": 20}. - Key fields:
text(post text),author.username(who posted),createdAt(when),likes(engagement),url(link to cite). - A 20-post profile run takes under 10 seconds and costs $0.032 on the Free plan. At most 50 inputs and 500 posts per input; search gives the top ~25โ30 posts per keyword.
not_foundanderrorrows explain missing data.
Troubleshooting
A well-known profile returns not_found
Symptom: a row with status: not_found and errorCode: NOT_FOUND for a profile that exists.
Reason: threads.com shows some profiles only to logged-in visitors. The Actor works without login, so it cannot read them.
Action: check the profile in a private browser window. If it asks you to log in, this Actor cannot read it.
Search returns fewer posts than maxItems
Threads shows logged-out visitors only the top ~25โ30 posts per keyword. Add more specific keywords, for example coffee roasting and #coffeeshop next to coffee.
A profile with postedAfter returns few posts
Reading stops at the first post older than Posted after (pinned posts are skipped), so a quiet profile gives few or no posts. A profile with no posts in the period returns one not_found row with NO_POSTS.
The run ends as PARTIAL
Check partialReason in RUN_SUMMARY. CHARGE_LIMIT: raise Maximum cost per run or max paid results. SAFETY_LIMIT: the request is larger than one run can do; split it into several runs. SOURCE_ERROR: Threads failed for some inputs; run them again later.
Rows with error status, RATE_LIMITED or BLOCKED
Threads refused the requests after retries. These rows are free. Keep the default Apify Proxy on, or switch the proxy to Residential in Advanced, and run the failed inputs again.
Rows with INVALID_INPUT, or the run fails with INVALID_INPUT
A username, post URL or keyword cannot be read. The row names the value and the reason; fix it and run again. The whole run fails with INVALID_INPUT only when no usable value is left, and the message shows an example of a valid input. Only the start was charged.
Nothing arrived in Slack or Notion
Check delivery in RUN_SUMMARY and the run log. For Notion, deliveryTarget must be a page link that the connector can open; for Slack, a channel ID. The run itself is not affected.
FAQ
Do I need a Threads or Instagram account?
No. The Actor reads only public pages without login, cookies or tokens.
Why not use the official Threads API?
The official Threads API needs an app, an access token and review, and reads mainly your own account. This Actor reads public posts of any public profile, post or keyword without setup.
Why is there no view count?
Threads does not show view counts to logged-out visitors. The Actor never guesses a value.
How do I get only new posts?
Set Posted after to a period such as 1 day and schedule the Actor daily. Older posts are skipped and not charged.
Are the counts live?
They are a snapshot at fetchedAt. Run again to refresh them.
Is the data complete?
It is what threads.com shows a logged-out visitor at fetch time. Deleted posts, private profiles and profiles that need login are not included, and some counts can be null.
Is scraping Threads allowed?
The Actor reads only public pages at a modest rate and does not log in or bypass any wall. Meta's terms limit automated collection. You are responsible for how you collect, store and reuse the content, including personal data and authors' rights.
Related Actors
- Bluesky Scraper โ the same kind of public posts and profiles from Bluesky.
- Truth Social Scraper โ public posts of Truth Social accounts for the same monitoring.
- Telegram Channel Scraper โ posts of public Telegram channels.
- Reddit Scraper โ Reddit posts and comments for topic research.
- YouTube Comments Scraper โ comments under videos for sentiment analysis.
Support
Use the Issues tab of this Actor for bugs, questions and feature requests. Include the run ID, the mode and the error text. Never post tokens, cookies or private data.
โญ Found it useful? Rate this Actor. Something wrong? Report a problem โ we fix reported problems.

