All-in-one Threads.net scraper: search posts by keyword, extract user profiles with followers, and collect reply threads. Full engagement data, contact extraction, media URLs. 20 parallel workers, auto-retry, proxy rotation.
Posts whose text is a collapsed snippet are no longer returned empty. Threads can carry
a post's text in a separate attachment — the app shows it as a collapsed snippet under the
post — and for those posts caption and text_fragments are both empty, so the item came
back with no text at all even though the whole post was in there. The snippet is now read
into text and text_fragments like any other post text.
The author's own thread continuation now appears in a post's replies. Threads serves it
in its own connection, separate from the replies, and it was being skipped — so a
continuation written by the post's author could not be reached from the parent post at all.
It is now returned first, the way the app shows it, both in post and in post_replies.
On the reported thread this closed the gap completely: 34 of 34 replies instead of 30.
One input field instead of three. Search queries, usernames and post URLs used to live
in three separate lists, even though the Action above already says which one applies. They
are now a single What to scrape field, read according to the selected Action. The old
queries, usernames and posts fields are still accepted, so saved tasks, scheduled runs
and API integrations keep working untouched.
Posts no longer come back empty on authenticated runs. Opening a post while logged in
was answered with a redirect to the home feed (/?injected_media_ids=[...]) instead of the
post page, because the request carried no referrer and Threads treats that as arriving from
outside the site. Every post then had to be re-read anonymously — two requests instead of
one — and posts that Threads does not serve anonymously came back as "not found" at all.
Requests now carry a same-origin referrer, like a browser navigating the site does, so the
logged-in read returns the post directly, with more replies than the anonymous one.
Short share links now work.https://www.threads.com/share/... links — the ones the
Threads "Share" button gives you — were rejected as invalid post URLs. They are now resolved
to the real post automatically, in parallel, one cheap request each.
Invisible characters no longer break input. URLs and usernames copied from Notion,
Telegram, Google Docs or Excel often carry a zero-width character in front. It is invisible
in the input field but made a perfectly valid post URL fail as "Invalid post URL". Such
characters are now stripped from every entry before it is read.
Skipped entries say what was wrong and what the Actor expected, instead of repeating the
same generic message for every cause.
The HTTP API (Standby mode) now documents, per endpoint, what you pass in and what comes
back, and accepts targets as a synonym for query / username / url everywhere.
0.7.1 (2026-09-17)
post_replies returns the whole thread, not its first page. Three things cut it
short. The cursor was picked by searching the response for one, and a reply that has
replies of its own carries a cursor belonging to that inner thread — Threads answers a
request made with it by erroring out, so paging stopped at page two. Each page was also
fetched from a different exit IP, and the cursor belongs to the session that issued it:
from another address the same thread comes back sliced differently, pages overlap and
the walk ends in the middle. Finally, replies to replies live in a separate connection
and were never fetched at all. The Actor now pages with the thread's own cursor over one
pinned exit, follows the sub-thread of every reply that has one, and treats an empty page
as a gap rather than the end. Measured on a post with 60 replies: 10 before, 58 now —
the rest are deleted or hidden.
0.7 (2026-09-16)
Fixed: a post link returned nothing. Threads split the post page into separate
queries — the post itself, the reply thread and the parent posts now arrive as three
different blocks instead of one — so the Actor found the page but no thread in it, and
the post mode came back empty. It reads the new layout, and still understands the old
one: Meta rolls these changes out in waves, and some sessions are still served the
previous shape.
Replies pagination works again — and no longer needs a login. The reply thread
moved onto its own query, and the one the Actor was paging with has no cursor at all:
deeper pages came back empty, so post_replies returned the ten replies the page
carries and stopped. It now pages with the query the site itself uses, which answers
logged-out readers as readily as members — a run walks the whole thread whether or not
a session is available (measured on a post with ~100 replies: 10 before, all of them
now).
0.6 (2026-09-05)
Fixed profiles, posts, replies and search coming back empty. Threads stopped shipping those results inside the page it serves — the HTML now arrives as a shell whose embedded result is literally {"user": null}, and the browser fetches the content afterwards. The Actor now takes the same second step, reusing the page's own query id, parameters and tokens, so every mode returns data again.
The query ids are read from the page on each run instead of being pinned in the code: Threads rotates them with every release, and a stale id fails silently — the request succeeds and simply returns nothing.
user_posts no longer stops when the profile itself cannot be read. Threads serves profile details to logged-in sessions only, and an anonymous run was throwing away the posts it had already fetched.
0.5 (2026-08-28)
New Standby mode: the Actor can now run as a real-time HTTP API. Call /search, /profile, /post, /user-posts, /post-replies, /user-followers or /user-following (or /?action=...) and get the scraped items back in the response instead of starting a run and polling its dataset. Both GET with query parameters and POST with a JSON body are accepted.
The endpoints are described by an OpenAPI schema, so Apify Console shows an interactive Standby tab where every parameter is documented and requests can be sent from the browser.
Items returned over the API are still written to the run's dataset, so nothing is lost if a request times out and existing dataset-based workflows keep working.
In API mode one logged-in Threads account is leased for the whole standby run instead of per request, and requests no longer share session state: cookies rejected for one request cannot disable authentication for another.
0.4 (2026-08-05)
Fixed runs finishing with zero results when the account they authenticated with had been suspended by Threads. A suspended session is indistinguishable from a healthy one by page content — same HTTP 200, same viewer data — and was accepted as valid, after which every request was silently answered with the block screen instead of data. The suspension is now detected by the URL Threads redirects to, the account is taken out of rotation with that reason, and the run continues on the next account in the pool.
The run now survives more dead accounts before giving up (5 instead of 3), because accounts get suspended in batches.
An account that gets suspended in the middle of a run no longer breaks the whole run: it is flagged and dropped, server-rendered pages continue without a session, and the modes that genuinely require one (followers, following) say so explicitly instead of reporting an empty list.
Fixed Expecting value: line 1 column 1 (char 0) being the only thing in the log when the API answered with something other than JSON. The status, content-type, URL and start of the body are now logged, and responses that cannot be fixed by retrying (a blocked or logged-out session) fail immediately instead of burning five attempts per request — which is what turned one bad account into hundreds of pointless requests.
Added backoff for HTTP 429: Retry-After is honoured, the whole run slows down together and speeds back up as requests succeed. Rate limits are counted per session, so retrying on a new IP alone never cleared them.
Search queries containing &, # or + are now escaped and no longer search for a truncated phrase.
0.3 (2026-07-30)
New input useOurAccounts (on by default): the run authenticates itself with a logged-in Threads session from our own account pool, so user_followers, user_following and pagination work without you supplying any cookies.
An account is leased to one run at a time and returned to the pool when the run ends — two runs never share the same Threads session. The lease is renewed while the run works, so a crashed run releases its account instead of holding it.
Accounts whose cookies Threads no longer accepts are flagged with the reason and taken out of rotation automatically, and the run continues on the next available account.
Your own threadsCookies still take priority: when that field is filled, the shared pool is not touched.
0.2.1 (2026-07-30)
Fixed posts and replies being reported as "not found" on pages that were actually fine: a truncated HTTP response (HTTP 200 with an incomplete body) left the last embedded JSON block broken, and the parser silently returned nothing. Truncated responses are now detected and retried on a fresh IP, and broken JSON blocks are logged instead of ignored.
Fixed the post parser picking the wrong edges list: the thread is not always the first one on the page, so a valid post could be missed. All candidate lists are now scanned.
All requests now go to www.threads.com directly instead of www.threads.net. The .net → .com redirect made curl drop the session cookies mid-redirect, so authenticated runs were silently anonymous, and the extra hop through a residential proxy sometimes collapsed into a redirect loop (curl 47).
Cookie verification now checks that Threads actually accepted the session (the viewer id is present in the page), instead of assuming success whenever a session cookie and an LSD token exist.
A network failure during cookie verification no longer discards the session: cookies are kept for SSR requests and only GraphQL pagination is disabled for that run.
post_replies with cookies no longer returns fewer replies than an anonymous run: replies from the server-rendered page are kept and merged with the GraphQL pages, deduplicated by post id.
Fixed a crash in paginated user_posts when a profile page came back without the expected posts block.
0.2 (2026-07-28)
New actions user_followers and user_following: scrape a profile's followers or the accounts it follows, each user as a separate dataset result, capped by maxItems.
Follower / following pagination via the private /api/v1/friendships/ web API, with deduplication across pages (Threads re-ranks the list between requests, so raw pages overlap).
Both actions require threadsCookies — Threads serves these lists only to logged-in sessions. Without cookies the run logs an explicit error and returns no items instead of failing.
Profile output now includes profile_url, instagram_url, instagram_user_id and recent_views_count. Follower / following results carry the same Threads and Instagram links.
New "Followers / Following" dataset view; the Profiles view now shows the linked Instagram account.
0.1 (2026-07-07)
Scrape Threads.net by search keyword, user profile, single post, user posts, and post replies.
Full pagination for search results, user posts, and post replies via the authenticated GraphQL API (SSR first page + cursor paging).
Rich post output: post_id, code, text, taken_at, media_type, like_count, reply_count, repost_count, quote_count, reshare_count, images, video_versions, carousel_media, text_fragments, post_url, and more.
Profile output includes biography, follower_count, is_verified, bio_links, bio_link_urls, profile_tags, and HD profile pictures.
Automatic extraction of emails and URLs from post text and profile bios.
Three dataset views (Overview, Profiles, Engagement) for quick browsing in the Output tab.