Threads Scraper: Posts, Threads Replies, Profile Scraper, API avatar

Threads Scraper: Posts, Threads Replies, Profile Scraper, API

Pricing

from $1.00 / 1,000 posts

Go to Apify Store
Threads Scraper: Posts, Threads Replies, Profile Scraper, API

Threads Scraper: Posts, Threads Replies, Profile Scraper, API

Meta Threads scraper API, no login: threads posts by link, a profile's posts with exact counts and views, threads replies and comment trees, a threads search scraper for keywords and tags, threads profiles with the exact follower count. One row per post, reply or profile; free rows for misses.

Pricing

from $1.00 / 1,000 posts

Rating

0.0

(0)

Developer

Hyperbach

Hyperbach

Maintained by Community

Actor stats

0

Bookmarked

6

Total users

5

Monthly active users

3 days ago

Last modified

Share

Threads (Meta) data without a login: profiles with the exact follower count, a profile's posts to the end of its list, posts by link or code with their view count, replies to the end of the list with nested conversations, keyword and tag search — one row per post or profile, every count exact, and a free row that says why for an input that names nothing. Run it on a schedule with onlyNew and each run delivers only the posts no earlier run delivered (monitor accounts for new posts). No account, no cookie, no browser, proxies handled for you.

Why this Threads scraper

  • Exact counts, every row, null where Threads withholds. Likes, replies, reposts, quotes and shares are the exact integers Threads serves (1,841 / 318 / 153 / 32 / 89 on our reference post, equal to the lane leader's row); the view count comes free on every post you name and on request for list rows; a count Threads does not give is null, never 0. In our test of the lane, actors wrote zeros for missing counts, rounded followers, or delivered Instagram's profile as Threads'.
  • Profile history to the end of the list. A profile's Threads tab is read with Threads' own paging to the list's end (168 of the 168 shown threads in our test; 1,400+ for a busy account), newest first, with postedAfter to stop at a date; the profile row carries the exact follower count, the shown threads count, bio, links, tags and the linked Instagram account.
  • Replies as a conversation. Replies under a post you name are read to the end of what Threads serves a visitor (250 of a post's 318 in our test; the row says how many and why), in Top or Recent order, with includeNestedReplies for the conversations under each reply; every reply row carries its level, its parent and the root post. A walk Threads rate-limits says so on the post row and in the run summary.
  • One run, any mix, one row per entity. Usernames, profile links, post links and codes on threads.com or threads.net, keywords and #tags in one run; the same post given three ways is one row; a post that is also found on a profile tab or a search page is one row; every input that names nothing comes back as a free row that says why (not found, invalid, empty, duplicate).
  • Search that says what it matched. Threads shows a visitor one page per surface (about 20 posts on Top, 20 under the topic tag), no Recent sort and no date window: the rows say so, every search row is flagged keyword_match, and keywordMatchOnly leaves the off-keyword rows out before the charge. searchPasses loads a page again for the posts Threads rotates in (a second load added 43 posts to 58 for starbucks and #coffee in our test), and the accounts surface gives the accounts Threads' search box offers, as exact profiles.
  • Built for schedules. onlyNew delivers only posts no earlier run delivered; with the memory on, a profile row says how its followers moved since the last run and a post read again carries its engagement change. Filters before the charge: minLikes, verifiedOnly.
  • Light and fast. Plain HTTP, no browser: about 2 KB of traffic per profile post, 4.5 KB per reply; 131 rows in 14 seconds and 250 replies in 48 seconds on one exit in our test, from datacenter IPs.

Who it's for

  • Marketers and brand teams — what Threads says about a brand or a topic today: keyword and tag search with the match flag, the replies under a post with their authors, the exact engagement of a creator's recent posts.
  • Researchers and analysts — a profile's full post history with exact counts and times, replies as threaded conversations with levels and parents, media and link URLs, in one typed row shape.
  • Monitoring teams — a scheduled list of profiles or keywords that delivers only what is new, with the follower change and the engagement change since the last run.
  • Developers and agents — typed fields, one row per input in input order, a free labelled row for every miss, and a run summary with a stop reason per input to check a pipeline against.

Quick start

A profile with its newest posts

{
"profiles": [
"zuck"
],
"maxPostsPerProfile": 25
}

Posts by link or code, with views

{
"posts": [
"https://www.threads.com/@zuck/post/DdZ7sQvkTFn",
"Dd1MqfcG0aH"
]
}

A post with its replies and the conversations under them

{
"posts": [
"https://www.threads.com/@zuck/post/DdZ7sQvkTFn"
],
"maxRepliesPerPost": 300,
"includeNestedReplies": true
}

Keyword and tag search, matches only

{
"keywords": [
"superintelligence",
"#ai"
],
"searchSurfaces": [
"top",
"tags"
],
"keywordMatchOnly": true
}

A profile's whole history with views

{
"profiles": [
"threads"
],
"maxPostsPerProfile": 1000,
"includeViews": true
}

A daily monitor: new posts only

{
"profiles": [
"zuck",
"threads"
],
"maxPostsPerProfile": 50,
"onlyNew": true,
"stateStoreName": "my-threads-watch"
}

Output

Two row types share one dataset (row_type says which): post (a post, a reply, a search hit) and profile:

fieldmeaning
row_typeprofile or post: which columns of the row are filled.
inputThe value you gave that this row answers (the first form, when the same post or profile was given several ways); on a search row the keyword; on a reply row the post you named.
input_indexThe 1-based position of the input in its list (profiles, posts or keywords).
inputs_mergedOther forms of the same post or profile you gave (a code and a link of one post), merged into this row.
source_kindWhere the row came from: post (a post you named), replies / nested_replies (under a post you named), profile_threads / profile_replies / profile_reposts (a profile's tab), search_top / search_tags (a search surface), search_accounts (an account Threads' search box offered for a keyword), profile (a profile you named), author (an author's profile with includeAuthorProfiles).
statusok = a delivered record; not_found, invalid, empty, failed, duplicate = a free row that says why there is no record for this input.
status_detailWhy a row is not ok, in words.
scraped_atWhen this row was read (UTC).
post_codeThe post's 11-character code, as in its link (DdZ7sQvkTFn).
post_idThreads' numeric id of the post, as a string.
post_urlThe post's link on threads.com.
created_atWhen the post was published (UTC, to the second).
created_at_timestampThe same moment as a Unix timestamp (seconds).
textThe post's full text, as written (empty on a media-only post).
text_lengthCharacters in text.
media_typetext, image, video, carousel, or repost (a reposting item of the reposts tab: the post itself is in reposted_post).
mediaOne object per picture or video of the post (every carousel slide): type, url (the largest rendition), width, height; a video adds has_audio and cover_url (its cover image).
image_urlsThe post's pictures, largest rendition URLs.
video_urlsThe post's videos (MP4 URLs; signed, they expire).
media_countHow many pictures and videos the post has.
gif_urlThe GIF's own URL where Threads serves it; null when a visitor gets only Threads' placeholder image (every GIF of a logged-out read: accessibility_caption names the GIF).
has_audioThe post's video has an audio track.
accessibility_captionThe alt text Threads generated for the media.
linksThe links in the text (the real targets, unwrapped from Threads' redirector).
mentionsThe @users named in the text.
stickersThe inline stickers of the text, as image URLs.
topic_tagThe post's topic tag, when the author set one (Threads has one tag per post, not hashtags).
link_previewThe link card under the post: title, url (unwrapped), display_url, description, image_url, favicon_url.
like_countLikes, exact.
reply_countReplies, exact, as Threads counts them (more than a visitor is served; see replies_note).
repost_countReposts, exact.
quote_countQuotes, exact.
share_countShares (reshares), exact; null where Threads sends none: on every reply, and on posts it shows no share count for (22 of 40 profile posts in our test; the post's own page sends null for them too).
view_countViews, exact, when read: always on a post you named; on list rows with includeViews; else null (never 0).
views_sourcepage (the view count came with the post's own page, free) or fetched (read for this row with includeViews, charged as post_views); null when no count was read.
engagement_rate(likes + replies + reposts + quotes) ÷ the author's followers, when the followers are known (a profile's own posts; includeAuthorProfiles).
is_replyThe post is a reply to another post.
reply_to_usernameThe author the post replies to.
reply_to_user_idThat author's Threads id.
parent_post_codeOn a reply row: the code of the post it answers (the post you named for a direct reply, the parent reply for a nested one); on a post you named that is itself a reply: its parent.
root_post_codeOn a reply row: the code of the post you named; on a reply you named: the root of its conversation.
reply_level0 = a top-level post; 1 = a direct reply to the root; 2 and deeper = a nested reply.
is_quoteThe post quotes another post.
quoted_postThe quoted post, in short: code, url, author, text, created_at, the five counts.
is_repostThe row is a repost (the reposts tab).
reposted_postThe reposted post, in short: code, url, author, text, created_at, the five counts.
thread_idThe id of the thread the post belongs to on a tab or search page: the posts of one self-thread (a multi-post thread by one author) share it.
thread_positionThe post's position in its thread (0 = the first post).
self_thread_countHow many posts the author's self-thread has on a tab or search page (the posts sharing thread_id by the thread's author); null on a post read alone.
is_pinnedThe post is pinned to its author's profile.
is_editedThe post was edited.
is_paid_partnershipThe post is labelled a paid partnership.
is_spoilerThe media is marked as a spoiler.
reply_controlWho may reply, as the author set it (everyone, …).
ai_labelThreads' AI-generated label on the post, where it shows one.
author_usernameThe author's username.
author_idThe author's Threads id (not the Instagram id).
author_nameThe author's display name.
author_verifiedThe author is verified.
author_profile_pic_urlThe author's profile picture.
author_is_privateThe author's profile is private.
author_followersThe author's exact follower count, when read this run (a profile's own posts; includeAuthorProfiles).
queryOn a search row (a post or an account the search found): the keyword or tag this row was found for.
search_surfaceOn a search row: top, tags or accounts.
search_tagOn a Tags-surface row: the topic tag Threads matched, when the page named it.
keyword_matchOn a search row: the keyword appears in the text, the topic tag, the quoted post or the author's names (Threads' Top surface ranks related posts too).
list_noteWhat the list this row came from shows a visitor (one page per search surface; the first page of the replies and reposts tabs).
replies_requestedOn a post you named: maxRepliesPerPost.
replies_deliveredOn a post you named: how many reply rows were delivered under it.
replies_noteWhy fewer replies than requested were delivered (the list's end, with the post's own count; a stop).
related_postsOn a post you named: the posts Threads shows under it as related (code, username, url).
previous_read_atWith the memory on: when this post or profile was last delivered by an earlier run.
engagement_changeWith the memory on, on a post read again: the change of the five counts since the last delivered read (like_count, reply_count, repost_count, quote_count, view_count).
rawThreads' own object behind the row, with includeRaw.
run_tagYour runTag, copied onto every row.
usernameThe profile's username.
user_idThe profile's Threads id (not the Instagram id), as a string.
full_nameThe display name.
biographyThe bio, full.
bio_linksThe links in the bio (unwrapped).
is_verifiedThe profile is verified.
is_privateThe profile is private (its posts are not shown to a visitor).
profile_pic_urlThe profile picture.
profile_pic_hd_urlThe largest profile picture Threads serves.
follower_countFollowers, exact.
threads_countThe profile's threads count as the page shows it, when it is a plain number (168); null when Threads rounds it (1.2K, kept in threads_count_shown).
threads_count_shownThe threads count as shown (168, 1.2K).
profile_tagsThe profile's interest tags (AI, MMA, …).
profile_urlThe profile's link on threads.com.
instagram_urlThe linked Instagram account (a Threads account is its Instagram account of the same username).
podcast_nameThe podcast the profile links, where it shows one.
podcast_urlThe podcast's link.
tabThe tabs read for this profile's posts (threads, replies, reposts, comma-separated).
posts_requestedmaxPostsPerProfile × the tabs read.
posts_deliveredHow many post rows were delivered for this profile.
posts_noteWhy fewer posts than requested were delivered (the list's end with the shown count, postedAfter, onlyNew, the filters, a tab's first page); on an account-search row, what the search box offers.
last_post_atThe newest post's time among the Threads-tab posts read (pinned posts aside).
recent_posts_readHow many Threads-tab posts the averages below are over.
recent_avg_likesAverage likes over those posts.
recent_avg_repliesAverage replies over those posts.
followers_previousWith the memory on: the follower count as last delivered by an earlier run.
followers_changeWith the memory on: follower_count minus followers_previous.

Example record:

{
"row_type": "post",
"input": "https://www.threads.com/@zuck/post/DdZ7sQvkTFn",
"input_index": 1,
"inputs_merged": [
"https://www.threads.net/@zuck/post/DdZ7sQvkTFn",
"DdZ7sQvkTFn"
],
"source_kind": "post",
"status": "ok",
"status_detail": null,
"scraped_at": "2026-10-10T00:02:13Z",
"post_code": "DdZ7sQvkTFn",
"post_id": "3988481471392330087",
"post_url": "https://www.threads.com/@zuck/post/DdZ7sQvkTFn",
"created_at": "2026-09-17T22:27:44Z",
"created_at_timestamp": 1789684064,
"text": "Muse for Mac is out today! It works across apps, files, calendar, notes, and messages on your computer. You control what it can access. The team is shipping fast. Download at ai.meta.com/muse/download",
"text_length": 200,
"media_type": "text",
"media": null,
"image_urls": null,
"video_urls": null,
"media_count": 0,
"gif_url": null,
"has_audio": null,
"accessibility_caption": null,
"links": [
"http://ai.meta.com/muse/download"
],
"mentions": null,
"stickers": null,
"topic_tag": null,
"link_preview": null,
"like_count": 1841,
"reply_count": 318,
"repost_count": 153,
"quote_count": 32,
"share_count": 89,
"view_count": 157870,
"views_source": "page",
"engagement_rate": null,
"is_reply": false,
"reply_to_username": null,
"reply_to_user_id": null,
"parent_post_code": null,
"root_post_code": null,
"reply_level": 0,
"is_quote": false,
"quoted_post": null,
"is_repost": false,
"reposted_post": null,
"thread_id": null,
"thread_position": null,
"self_thread_count": 0,
"is_pinned": false,
"is_edited": false,
"is_paid_partnership": false,
"is_spoiler": false,
"reply_control": "everyone",
"ai_label": "{\"detection_method\": \"NONE\"}",
"author_username": "zuck",
"author_id": "63055343223",
"author_name": "Mark Zuckerberg",
"author_verified": true,
"author_profile_pic_url": "https://scontent-phl2-1.cdninstagram.com/v/t51.82787-19/825322135_17989325280103224_1252773933700107438_n.jpg?stp=dst-jpg_s206x206_tt6&_nc_cat=107&ccb=7-5&_nc_sid=30ff31&efg=eyJ2ZW5jb2RlX3RhZyI6InByb2ZpbGVfcGljLnd3dy4xMDgwLkMzIn0%3D&_nc_ohc=l7qeCXGmYHsQ7kNvwEFTVOn&_nc_oc=Adrf5-GaepFVtOPnPzNV5nLDWB-LX9x8r8A6YILbQNSGbvG-aisMvjRHoOvot8g78Ic&_nc_zt=24&_nc_ht=scontent-phl2-1.cdninstagram.com&_nc_gid=keE14ec5-x7nR_U-ifJgyQ&_nc_ss=7b289&oh=00_AQPz-ardxL9MjFDRJ_BfQjidx99SBVd3XeRT0kPhBq-J2w&oe=6ACF40EB",
"author_is_private": false,
"author_followers": null,
"query": null,
"search_surface": null,
"search_tag": null,
"keyword_match": null,
"list_note": null,
"replies_requested": 20,
"replies_delivered": 20,
"replies_note": null,
"related_posts": [
{
"code": "DdaWHdskkU3",
"username": "eastmel",
"url": "https://www.threads.com/@eastmel/post/DdaWHdskkU3"
},
{
"code": "DdaoIYUgrJS",
"username": "elek7817",
"url": "https://www.threads.com/@elek7817/post/DdaoIYUgrJS"
},
{
"code": "DdapXchIy7g",
"username": "thisiskhoagates",
"url": "https://www.threads.com/@thisiskhoagates/post/DdapXchIy7g"
},
{
"code": "DddicpMjx7A",
"username": "luxury_maldives_voyage",
"url": "https://www.threads.com/@luxury_maldives_voyage/post/DddicpMjx7A"
},
{
"code": "Ddg8OiciPQ0",
"username": "zurck53",
"url": "https://www.threads.com/@zurck53/post/Ddg8OiciPQ0"
}
],
"previous_read_at": null,
"engagement_change": null,
"raw": null,
"run_tag": null,
"username": null,
"user_id": null,
"full_name": null,
"biography": null,
"bio_links": null,
"is_verified": null,
"is_private": null,
"profile_pic_url": null,
"profile_pic_hd_url": null,
"follower_count": null,
"threads_count": null,
"threads_count_shown": null,
"profile_tags": null,
"profile_url": null,
"instagram_url": null,
"podcast_name": null,
"podcast_url": null,
"tab": null,
"posts_requested": null,
"posts_delivered": null,
"posts_note": null,
"last_post_at": null,
"recent_posts_read": null,
"recent_avg_likes": null,
"recent_avg_replies": null,
"followers_previous": null,
"followers_change": null
}

Pricing

Pay only for the rows a run delivers, with no start fee and no platform usage billed to you. Prices fall with your Apify plan.

per 1,000FreeStarterScaleBusiness
Post (post)$2.00$1.60$1.40$1.00
Profile (profile)$4.00$3.40$2.80$2.00
Reply (reply)$2.00$1.70$1.40$1.00
View count (list rows) (post_views)+$1.60+$1.36+$1.12+$0.80

A post is $2.00 per 1,000 on the Free plan and $1.00 on Business; a post you name by link or code carries its view count at no extra charge. The + fee is charged only on a list row that carries a view count read with includeViews ($1.60 per 1,000 on Free). A profile row is charged per profile, a reply row per reply (nested replies included). An input that names nothing (not found, invalid, empty, a duplicate) is a free row that says why; so is a run that failed. maxItems counts every charged row; before the first request the log says the most the run can cost at your plan's prices. Enterprise plans have their own rates — the Actor's Pricing tab shows the price for your plan.

Usage patterns

  • Profiles — Paste usernames or profile links in profiles. Each gives one profile row (the exact follower count, the shown threads count, bio, links, tags, picture, the linked Instagram account) and up to maxPostsPerProfile post rows from the tabs in profileTabs (threads by default; replies and reposts give their first page, about 30 posts, which is all Threads shows a visitor). The Threads tab is read newest first to the list's end; postedAfter stops it at a date. The replies tab gives the account's own replies, each with the code of the post it answers (parent_post_code). The profile row says how many posts were delivered and why fewer, when fewer (posts_note), and carries the newest post's time and the recent averages. With maxPostsPerProfile 0 a run gives the profile rows only (exact follower counts for a list of accounts); includeProfileRow off gives the posts only (image and video URLs from an account's posts).
  • Posts and replies — Paste post links or codes in posts. Each gives one post row with its five exact counts and its view count (free: the post's own page carries it), media, links, mentions, the quoted or reposted post, the related posts, and — for a post that is itself a reply — its parent and root. maxRepliesPerPost adds the replies under it as post rows (source_kind replies, reply_level 1), in Top or Recent order, to the end of what Threads serves a visitor; includeNestedReplies adds the conversations under each reply (reply_level 2 and deeper, parent_post_code the reply answered). The post row says how many replies were delivered and why fewer than Threads' count (replies_note), including when Threads rate-limited the walk (the post is then listed in the run summary's partial_inputs). Example: the replies to a post, with nested conversations.
  • Search — A keyword in keywords reads the Top surface (about 20 posts, the page a visitor sees); searchSurfaces adds the Tags surface and accounts (the accounts Threads' search box offers for the keyword, up to 10, each an exact profile row charged as a profile); a #tag reads the Tags surface for that tag. Every search row names its keyword (query) and surface (search_surface). Threads shows a visitor one page per surface, no Recent sort and no date window: list_note says so on every post row, keyword_match says whether the keyword is in the text, the topic tag, the quoted post or the author, and keywordMatchOnly leaves the rest out before the charge. The page changes between loads: searchPasses loads it again on fresh exits and adds the posts not seen yet, each charged once (a second load added 43 posts to 58 for starbucks and #coffee; 6 loads added 5 to 21 for a rare word). Examples: posts, tags and accounts for a keyword, popular posts that mention a keyword.
  • Views, authors, filters — includeViews reads the view count of every profile post, search hit and reply (one small request per row, charged as post_views); includeAuthorProfiles adds one exact profile row per author and puts author_followers and engagement_rate on the post rows; includeRaw keeps Threads' own object on each row. minLikes and verifiedOnly leave list rows out before the charge; a post you name is always delivered. Example: posts with view counts and engagement rate.
  • A schedule — Save a task with your profiles or keywords and onlyNew, and schedule it: each run reads the newest posts and delivers only those no earlier run of the task delivered; a post skipped this way is free, so a run with nothing new costs the profile rows only. The caps bound the depth read, so a daily run with maxPostsPerProfile 50 reads the same newest 50 every day. stateStoreName names the memory; with it, profile rows carry followers_previous and followers_change, and a post read again carries engagement_change. Example: monitor accounts for new posts.

Input configuration

fieldtypedefaultwhat it does
profilesarray[]Threads profiles, one per line, in any form: zuck, @zuck, https://www.threads.com/@zuck, https://www.threads.net/@zuck, or a tab link such as https://www.threads.com/@zuck/replies. Each gives one profile row (the exact follower count, bio, links, tags, the shown threads count) and up to maxPostsPerProfile post rows from the tabs in profileTabs. The same user given several ways is one row. A value that is not a Threads profile comes back as a free row that says why.
postsarray[]Threads posts, one per line: https://www.threads.com/@zuck/post/DdZ7sQvkTFn, the same on threads.net, https://www.threads.com/t/DdZ7sQvkTFn, or the 11-character code DdZ7sQvkTFn. Each gives one post row with its five exact counts and its view count, and up to maxRepliesPerPost post rows of replies. One row per post: the same post given several ways is one row. A post that does not exist is a free row that says so.
keywordsarray[]Search terms, one per line. A word or phrase reads the Top surface (about 20 posts, the page a visitor sees; searchSurfaces adds the Tags surface and the accounts the search box offers); #tag reads the Tags surface for that tag; a search link https://www.threads.com/search?q=…&serp_type=tags is read as given. Threads shows a visitor one page per surface, no Recent sort and no date window: every row says so (list_note) and whether it holds the keyword (keyword_match).
maxPostsPerProfileinteger25How many posts to read for each profile, newest first, from each tab in profileTabs; 0 = the profile row only. The Threads tab is read to the list's end (168 of 168 shown in our test, 1,400+ for a busy account); the replies and reposts tabs give their first page (about 30 posts, all Threads shows a visitor). With onlyNew or the filters, fewer rows are delivered than read: the cap bounds the depth, so a scheduled run reads the same newest posts every time.
profileTabsarray[]Which tabs of each profile to read for its posts; empty = threads. A tab given in a profile link (…/@zuck/replies) applies to that profile. Needs maxPostsPerProfile above 0.
postedAfterstring""A UTC date (2026-10-01) or time (2026-10-01T12:00:00Z): on the Threads tab, the walk stops at the first post older than this (pinned posts excepted), so a scheduled run reads only the recent posts. Empty = no date floor. Search has no date window (Threads shows a visitor none).
includeProfileRowbooleantrueDeliver each profile's own row (exact followers, bio, links; charged as a profile) with its posts. Off: the posts only, so 10 posts asked are 10 charged rows (the posts still carry the author's follower count from the profile page). A profile asked with maxPostsPerProfile 0 always gives its row.
maxRepliesPerPostinteger0How many replies to read under each post in posts, as post rows with reply_level, parent_post_code and root_post_code; 0 = the post row only. Read to the list's end: Threads serves a visitor most but not all of a post's replies (231 + 33 nested of 318 in our test), and the post row says how many (replies_note). With the filters, fewer rows are delivered than read.
repliesOrdertop / recent"top"The order Threads lists the replies in.
includeNestedRepliesbooleanfalseAlso read the conversation under each reply that has replies (one request per such reply), as post rows with reply_level 2 and deeper. Counted in maxRepliesPerPost.
searchSurfacesarray[]The surfaces read for each plain keyword, one page each; empty = top. A #tag keyword always reads the Tags surface. A post found on both surfaces is one row. accounts gives the accounts Threads' search box offers for the keyword (up to 10), each as an exact profile row charged as a profile.
searchPassesinteger1How many times to load each post surface of a keyword. Threads gives a visitor one page with no cursor, but the page changes between loads: each pass after the first loads it again on a fresh exit and adds the posts not seen yet, charged once (a second pass added 43 posts to 58 for starbucks and #coffee; 6 passes added 5 to 21 for a rare word). 1 = the page a visitor sees.
maxPostsPerKeywordinteger0The most posts read per keyword over its post surfaces (0 = every post of the pages read: about 20 a surface), and the most accounts from the accounts surface. With keywordMatchOnly, onlyNew or the filters, fewer rows are delivered than read.
keywordMatchOnlybooleanfalseDeliver only the search rows whose text, topic tag, quoted post or author holds the keyword (keyword_match true). Threads' Top surface ranks related posts too (about half hold the word in our test); off, every row is delivered with its flag.
includeViewsbooleanfalseRead the view count of every profile post, search hit and reply (one request per row: Threads serves views only on a post's own page). A post you name in posts carries its views at no extra cost. Charged per row that gets one (post_views).
includeAuthorProfilesbooleanfalseOne exact profile row per distinct author of the posts delivered (the exact follower count; one small request per author), charged as a profile; the post rows then carry author_followers and engagement_rate. Every post row already carries the author's username, id, name, verification and picture.
includeRawbooleanfalseKeep Threads' own JSON object on each row (raw), for fields this Actor does not name.
minLikesinteger0Leave out profile posts, search hits and replies with fewer likes than this; 0 = no floor. A post you name in posts is always delivered.
verifiedOnlybooleanfalseKeep only list rows whose author is verified.
onlyNewbooleanfalseDeliver only posts no earlier run of the same task delivered (profile tabs and search rows); turns the memory on. With the memory on, a profile row says how its followers moved since the last run and a post read again carries its engagement change.
stateStoreNamestring""Name of the memory between runs. Given, it turns the memory on: profile rows get followers_previous and followers_change, post rows read again get engagement_change; with onlyNew, posts delivered before are skipped. Empty = derived from the inputs when onlyNew is on.
maxItemsinteger0The most charged rows this run delivers, profiles, posts and replies together. 0 = no limit. Free rows (not found, invalid, empty, duplicate) do not count.
proxyUrlstringhttp://user:pass@host:port — a proxy of yours to read Threads through, tried before the Actor's own exits. Leave empty: the Actor brings its own.
runTagstring""A label copied onto every row (run_tag), to tell runs apart in a shared dataset.

Errors

A run does not fail because of your input. When an input cannot be used — a date that is not a date, an id in the wrong form, two settings that contradict each other — the run ends Succeeded, charges nothing, and says what to change:

  • the status message starts with INPUT REJECTED;
  • the dataset holds one row, {"error": true, "code": "…", "message": "…"}, and no results;
  • the key-value store record ERROR holds the same object.

From code, check error on the first row before reading results.

A search that matches nothing is not an error: the dataset is empty and nothing is charged. A run that ends Failed is a fault on our side, never your input; it charges nothing, and we are alerted.

FAQ

Can it read Recent search results, search by date, or the followers, following and likers of an account?

No: these need a login. Threads' own page router strips the Recent filter and the date parameters from a visitor's search, and the follower, following and liker lists open only to a logged-in account; the actors that sell them use a login, their own or your session. This Actor does not log in or take a cookie, and the rows say what the source served (list_note). What a visitor does get: one page of Top and Tags results per keyword (more with searchPasses), the accounts the search box offers (searchSurfaces accounts), a profile's posts to the end of its list, and the replies to a post with their conversations.

Are the counts exact?

Yes: every count on every row is the exact integer Threads serves, equal to the counts a logged-in user sees. The view count is on every post you name (free) and on list rows with includeViews; where no count was read it is null, never 0. The profile's threads count is exact when Threads shows a plain number and null (with the shown text) when Threads rounds it.

Why are fewer replies delivered than the post's reply count?

Threads serves a visitor most but not all of a post's replies: on our reference post it served 250 of 318 (the rest are hidden, deleted or from private accounts). The post row's replies_note says how many were served and that the list ended; includeNestedReplies adds the conversations under each reply. When Threads rate-limited calls of the run, the note says so and the run summary lists the post under partial_inputs: a quieter read may serve more (in our test the same walk on a second exit served no reply the first had missed, 148 of 148).

What is charged?

Delivered rows: a profile, a post (a post you named, a profile's post, a search hit), a reply, and the post_views add-on on a list row whose view count was read for it. Rows for inputs that name nothing (not found, invalid, empty, duplicate), posts onlyNew skips or the filters leave out, and a run that failed before its first row are free; a run that stops early (a timeout, an abort) is charged for the rows it delivered. maxItems caps every charged row together.

Does it need a proxy or a Threads account?

No. The Actor brings its own exits (datacenter IPs are served) and reads Threads as a visitor; proxyUrl lets you put a proxy of your own in front.

Can an AI agent use it (MCP)?

Yes. Apify's MCP server (https://mcp.apify.com) offers Store Actors as tools to Claude, Cursor and other MCP clients: add this Actor and the agent sends profiles, posts or keywords and gets the rows back. The synchronous API (run-sync-get-dataset-items) does the same in one HTTP call.

Integration

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/threads-scraper').call({"profiles": ["zuck"], "maxPostsPerProfile": 25});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Python

from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/threads-scraper').call(run_input={'profiles': ['zuck'], 'maxPostsPerProfile': 25})
items = client.dataset(run['defaultDatasetId']).list_items().items

CLI

$apify call hyperbach/threads-scraper --input '{"profiles": ["zuck"], "maxPostsPerProfile": 25}'

REST

curl -X POST "https://api.apify.com/v2/acts/hyperbach~threads-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' -d '{"profiles": ["zuck"], "maxPostsPerProfile": 25}'

Support

apify@hyperbach.com

This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.