DEV Community

Alom Dev
Alom Dev

Posted on

How to scrape Threads posts without logging in (Python and JavaScript)

Disclosure first: I built the scraper this post uses (Threads Scraper on Apify),
so take my enthusiasm with a grain of salt. The code below is real and I ran every snippet before posting it. The
outputs are what I got on October 15.

Why not just use the official API?

Meta does have a Threads API, but it needs a Meta developer app and an access token, and it's built mostly around your
own account. If you want "the last 50 posts of some public account" or "what people posted about espresso this week",
you end up scraping the website. A logged-out visitor can see quite a lot on threads.com: profiles, posts, the replies
under a post, a keyword search. That's what the scraper collects, without any account, so there's nothing to get banned.

You need an Apify account (the free plan includes $5 of usage a month, which at $2.50 per 1,000 results is about 2,000
posts) and your API token from Settings → API & Integrations.

Python: an account's posts with view counts

pip install apify-client
export APIFY_TOKEN=your_token
Enter fullscreen mode Exit fullscreen mode
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("alom/threads-scraper").call(run_input={
    "usernames": ["mosseri"],
    "maxPosts": 20,
    "includeViews": True,
})

for item in client.dataset(run.default_dataset_id).iterate_items():
    if item["type"] == "post":
        print(item["likeCount"], item["viewCount"], item["text"][:60])
Enter fullscreen mode Exit fullscreen mode

Output (likes, views, text):

1018 320506 Big day for the Muse team with the introduction of Muse Wond
372 42105 As is often the case with features like this one, this post
216 31669 Once iOS27 reaches a majority of our users though I think we
156 14703 This is rad.
340 19718 Three DM settings worth knowing about. Let me know if there
Enter fullscreen mode Exit fullscreen mode

One gotcha if you copy older tutorials: apify-client 3.x returns a Run object, so it's run.default_dataset_id, not
run["defaultDatasetId"]. The dictionary version throws 'Run' object is not subscriptable. I hit that myself while
writing this.

About includeViews: Threads shows a view count ("320K views") on the post page, not in the feed, so the scraper has
to open each post. That adds roughly 1.5 seconds per post, which is why it's off by default. For a post you pass by URL
you get the view count anyway.

The first row of the dataset is the profile itself (type: "profile") with followers, bio, bio links and verification.
Set "mode": "profile" if that's all you want.

Python: keyword search, newest first, with filters

import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("alom/threads-scraper").call(run_input={
    "searchQueries": ["espresso", "#coffee"],
    "searchSort": "recent",
    "maxPosts": 50,
    "minLikes": 5,
    "languages": ["en"],
})

rows = list(client.dataset(run.default_dataset_id).iterate_items())
print(len(rows), "posts")
for row in rows[:5]:
    print(row["date"], row["username"], row["likeCount"], row["text"][:60])
Enter fullscreen mode Exit fullscreen mode
56 posts
2026-10-15T00:41:07.000Z riim__79 33 Espresso ☕️.
2026-10-14T22:55:40.000Z andi.vdb 10 Depresso in need of espresso
2026-10-14T17:25:44.000Z knowyourworthfl 295 Yeah a local coffee shop said they could not make me a doubl
2026-10-09T20:59:18.000Z j_x84 205 Espresso Alfredo❣️🪄.
2026-09-30T03:54:21.000Z alanchunggg 13 Espresso ☕️
Enter fullscreen mode Exit fullscreen mode

A few honest notes about search:

  • Logged out, Threads shows one page of about 20 results per search. The scraper samples it several times and merges, which usually gets 60 to 85 posts per keyword. It is not every post ever written about espresso.
  • searchSort: "recent" sorts what it found newest first. It isn't Threads' real "Recent" tab, which needs a login.
  • languages uses the language Threads reports when it has one, otherwise a guess from the text. A post like "Espresso ☕️." is too short to tell, and those are kept rather than dropped. That's why one slipped in above.
  • minLikes and minReplies are applied before the results are saved, and you only pay for the rows you get.

JavaScript: the replies under a post

npm install apify-client
Enter fullscreen mode Exit fullscreen mode
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('alom/threads-scraper').call({
    postUrls: ['https://www.threads.com/@mosseri/post/DdUI1VfG_42'],
    maxReplies: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const replies = items.filter((item) => item.type === 'reply');
console.log(`${replies.length} replies`);
for (const r of replies.slice(0, 5)) console.log(r.likeCount, r.username, r.text.slice(0, 60));
Enter fullscreen mode Exit fullscreen mode

That printed 100 replies, sorted the way Threads sorts them ("top"). Set repliesSort: "recent" for newest first.
Each reply has parentPostId, so you can rebuild the reply chains, and isLikedByAuthor tells you which replies the
original poster liked. If replies are all you need, there's a separate
Threads Replies Scraper with a simpler input. Logged out, Threads
shows roughly the first 1,000 replies of a post, so that's the ceiling.

Using it from Claude, ChatGPT or Cursor (MCP)

You don't have to write code at all if an AI assistant should do the asking. Apify runs an MCP server, and you can
limit it to one Actor with the tools parameter:

https://mcp.apify.com/?tools=alom/threads-scraper
Enter fullscreen mode Exit fullscreen mode
  • Claude (claude.ai or Desktop): Settings → Connectors → Add custom connector, paste the URL, then sign in to Apify when it asks.
  • Claude Code: claude mcp add --transport http threads "https://mcp.apify.com/?tools=alom/threads-scraper", then run /mcp to sign in.
  • Cursor and other clients with an mcpServers config:
{
  "mcpServers": {
    "threads": {
      "url": "https://mcp.apify.com/?tools=alom/threads-scraper"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The agent gets a tool called alom--threads-scraper with the same inputs as above. Then you just ask, for example:
"Get the last 20 posts of @mosseri with view counts and tell me which topics got the most views." Two tips: keep
maxPosts small (10 to 30) so the answer fits the model's context, and use minLikes and languages so it isn't
reading noise. Every call is a normal run on your Apify account, at the normal price.

Cost and limits

$2.50 per 1,000 results on Apify's free plan, $1.50 on the bigger plans, plus half a cent per run. Proxies and compute
are included in that. What it can't do logged out: follower and following lists, and post counts. A deleted post or an
unknown username fails only that input, not the whole run.

If something doesn't work for your case, open an issue on the Actor page. I read all of them.

Top comments (0)