Scrape Threads Posts for Social Listening: 2026 Python Guide
Pull a Meta Threads profile's recent posts and like counts for social listening and creator research. Python recipes with Thirdwatch's Threads Scraper.

Thirdwatch's Threads Scraper turns any public Meta Threads handle into clean dataset rows — post text, like counts, timestamps, author identity, media URLs, and direct thread links — without a login, Instagram session, or API key. Built for social-listening, comms, and creator-research teams who need a profile's recent posts and like signal in JSON, not a browser tab to babysit. Pass one or more handles, get structured rows back.
Why scrape Threads for social listening
Threads is now a primary publishing channel, not an experiment. Meta reported Threads passed 275 million monthly active users in 2024 and has kept growing since, with founders, brands, journalists, and creators posting there daily. For social-listening and comms teams, that means a new surface to monitor: executive accounts, competitor brand handles, and the creators in your category all leave a public trail of posts with visible like counts.
The blocker is plumbing, not access. Threads has no general-purpose public data API for third-party post monitoring, and its web app renders posts through a guest GraphQL layer that most teams do not want to reverse-engineer. Yet every public profile serves its recent posts to logged-out visitors — text, like counts, timestamps, and a stable post URL are all right there. The actor is a thin, maintained extraction layer on top of that public feed.
The job-to-be-done is narrow and repeatable: "given this list of handles, return each profile's recent posts with like counts." A comms team watching an executive's account, a brand analyst ranking a competitor's recent posts by likes, a creator-research team shortlisting voices in a niche — all reduce to the same handle-in, posts-out contract.
How does this compare to the alternatives?
Three realistic ways to get Threads post data into a pipeline:
| Approach | Reliability | Setup time | Maintenance |
|---|---|---|---|
| DIY Python + requests | Brittle — you reverse-engineer the guest GraphQL and token flow yourself | 1-2 weeks (token handling + parser + anti-bot) | Ongoing whenever Threads shifts the feed |
| Generic scraping API (proxy-as-service) | Reliable for the raw fetch, you still write the GraphQL parser | 2-3 days for the parser alone | You own the parser drift |
| Thirdwatch Threads Scraper | Production-tested, fields stay stable across feed changes | 5 minutes | Thirdwatch tracks Threads changes |
For a one-off look at a single account the DIY path is tempting. The moment your watchlist grows past a handful of handles you want to re-scrape on a schedule, the actor's transparent pay-per-result pricing usually beats engineering time spent chasing token and parser regressions. See the Threads Scraper actor page for the live spec.
How to scrape Threads for social listening in 4 steps
Step 1: How do I authenticate against Apify?
Sign in at apify.com (free tier, no credit card required), open Settings → Integrations, and copy your personal API token. Every example below assumes the token is in APIFY_TOKEN.
export APIFY_TOKEN="apify_api_xxxxxxxxxxxxxxxx"Step 2: How do I scrape a single profile's recent posts?
The actor accepts a handle in the username field, with or without the leading @. maxResults caps how many recent posts to return per profile (default 25, which is about what the public feed exposes).
import os, requests
ACTOR = "thirdwatch~threads-scraper"
TOKEN = os.environ["APIFY_TOKEN"]
resp = requests.post(
f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
params={"token": TOKEN},
json={"username": "mosseri", "maxResults": 25},
timeout=120,
)
posts = resp.json()
for p in posts[:5]:
print(p["like_count"], "likes —", p["text"][:80])run-sync-get-dataset-items returns the dataset rows directly in the response body, so a single-profile pull comes back in one call without polling a run.
Step 3: How do I monitor a watchlist of handles in one run?
Pass the usernames array instead of the single username field. Each handle returns up to maxResults recent posts. This is the core social-listening loop: a fixed list of accounts you re-scrape on a schedule.
import os, requests, pandas as pd
ACTOR = "thirdwatch~threads-scraper"
TOKEN = os.environ["APIFY_TOKEN"]
WATCHLIST = ["zuck", "mosseri", "natashalyonne", "washingtonpost"]
resp = requests.post(
f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items",
params={"token": TOKEN},
json={"usernames": WATCHLIST, "maxResults": 25},
timeout=900,
)
df = pd.DataFrame(resp.json())
print(f"Pulled {len(df)} posts across {df.username.nunique()} profiles")
# Rank each profile's recent posts by likes — the core listening view
top = (df.sort_values("like_count", ascending=False)
.groupby("username")
.head(3)[["username", "like_count", "text", "thread_url"]])
print(top)Because reply, repost, and quote counts are not exposed on the public guest feed, like_count is the engagement axis you rank on. It is the reliable signal for "which of this account's recent posts landed."
Step 4: How do I persist snapshots and track like growth over time?
Threads serves only a recent window per profile and has no historical pagination, so continuous coverage means re-scraping on a schedule and accumulating your own snapshots. Key on code (the Threads short code) so re-runs refresh like counts in place rather than duplicating posts.
import sqlite3
conn = sqlite3.connect("threads_listening.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS snapshots (
code TEXT, username TEXT, scraped_at TEXT,
text TEXT, like_count INTEGER,
taken_at_iso TEXT, thread_url TEXT,
PRIMARY KEY (code, scraped_at)
)
""")
for _, p in df.iterrows():
conn.execute(
"INSERT OR REPLACE INTO snapshots VALUES (?, ?, datetime('now'), ?, ?, ?, ?)",
(p["code"], p["username"], p["text"],
int(p.get("like_count") or 0),
p.get("taken_at_iso"), p["thread_url"]),
)
conn.commit()Schedule the actor on Apify's scheduler — a daily run at 06:00 UTC suits most monitoring loops, hourly for breaking-news handles. Keying on (code, scraped_at) gives you a like-count time series per post, so you can watch how a post accumulates likes between snapshots.
Sample output
A single post record looks like this. Identifying detail is reduced to placeholders. Note that reply_count, repost_count, and quote_count are null by design — Threads does not expose them on the public guest feed, and the actor returns null rather than guessing.
[
{
"username": "mosseri",
"text": "We're rolling out a few changes to how the feed ranks replies. The goal is to surface more of the conversations you actually care about.",
"like_count": 4821,
"reply_count": null,
"repost_count": null,
"quote_count": null,
"taken_at": 1718870400,
"taken_at_iso": "2026-06-20T08:00:00Z",
"author": {"username": "mosseri", "full_name": "[REDACTED]", "is_verified": true, "pk": "1234567890"},
"media": [{"type": "image", "url": "https://scontent.cdninstagram.com/.../post.jpg"}],
"code": "C8aBcD1EfGh",
"pk": "33445566778899",
"thread_url": "https://www.threads.com/@mosseri/post/C8aBcD1EfGh"
}
]like_count is your engagement axis — rank a profile's recent posts on it to see what resonated. taken_at_iso gives you an absolute UTC timestamp (taken_at is the same moment as Unix epoch seconds) so you can window posts by recency without parsing relative strings. code is the natural key for de-duplication across re-runs, and thread_url links straight back to the post for manual review. media is always an array — text-only posts return an empty list.
Common pitfalls
Four things trip up Threads listening pipelines. Reply, repost, and quote counts are null — the public guest feed does not include them, so do not build dashboards that rank by reply depth or repost virality; rank by like_count instead. No keyword or hashtag search — Threads gates search behind login, so topic monitoring means curating a handle watchlist, not querying a term. Only the recent window is available — about 25 posts per profile with no historical pagination; for older posts you need to have captured them in an earlier snapshot, which is why scheduled accumulation matters. Like counts are a moving target — a fresh post keeps gaining likes for hours, so always store scraped_at and treat each row as a snapshot, not a final figure.
Thirdwatch's actor handles the guest token flow and the GraphQL parsing for you, so a multi-handle watchlist run completes without you tracking how Threads shifts its feed internals. Two more notes worth flagging: a profile that is private or has no public posts returns an empty result for that handle, which you should treat as legitimate absence rather than a failure; and because the feed window is fixed, setting maxResults above ~25 simply returns whatever the feed exposes rather than reaching deeper into history.
Related use cases
Frequently asked questions
Do I need a Threads or Instagram login to use the actor?
No. The Threads Scraper reads only the public guest profile feed — the same posts Threads serves to logged-out visitors. You do not need a Threads account, an Instagram login, cookies, or an API key. You only need an Apify API token to call the actor. Pass a handle, get JSON back.
How many posts does the actor return per profile?
Roughly the 25 most recent public posts per profile. The public guest feed exposes a fixed recent window with no pagination cursor, so maxResults is effectively capped at what the feed shows. Setting maxResults above 25 returns whatever the feed exposes rather than a deeper history.
Which engagement metrics are included?
Per post you reliably get like_count, plus text, taken_at, the author block, media URLs, and a direct thread_url. reply_count, repost_count, and quote_count come back null because Threads does not include them in the public guest selection set. Build your monitoring around like counts, not reply or repost depth.
Can I search Threads by keyword or hashtag?
No. Keyword and hashtag search on Threads is gated behind authentication and is intentionally out of scope. The actor scrapes public profile feeds only. To monitor a topic, build a watchlist of the handles that post about it and scrape those profiles on a schedule rather than searching.
Can I get a profile's full post history?
No. The public guest feed returns only the recent window — about 25 posts — with no historical pagination. The actor is a recent-activity monitor, not an archive tool. For continuous coverage, scrape each handle on a schedule and accumulate snapshots in your own store keyed on the post code.
Related
100 free credits, no credit card.
About 30 real searches. Add the MCP to Claude or Cursor in two minutes.