Threads API & Scraper
The Threads API returns public Threads data as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary profile endpoint returns a username's full name, biography, bio links, follower count, profile picture and user id, and you can pull posts, replies, reposts, a post, its replies and search. It is built for social analytics and brand monitoring that need public Threads data without a login. One ReefAPI key, one shared credit pool, the standard envelope.
Threads id shapes, and what logged-out access cannot reach
Threads reuses Instagram's id conventions, and the same post is addressable three different ways depending on which action you call. The shapes below are from live responses. The gated rows are the ones that answer with an error no matter what you pass.
| Field or action | Measured behavior | Notes |
|---|---|---|
| post id | Numeric post key, underscore, the author's numeric user id: 3971525888530486450_63269174602 | The suffix equals that author's profile.user_id. zuck's own posts end in _63055343223, which matches his profile exactly. |
| code | 11 characters, Instagram-style shortcode: DcdscDqIJSy | The /post/<code> segment. Pass code together with username, or pass the full url instead. |
| taken_at | Unix epoch in SECONDS: 1787662820 | Not ISO-8601 and not milliseconds. Multiply by 1000 before feeding a JS Date. |
| search rows | id equals the code, not the numeric key, and rows carry username, text, url and timestamp_label only | No counts and no taken_at in search output. Re-fetch a hit with the post action if you need either. |
| followers, following, likes | ok false, error code DISABLED, retryable false — a refusal in under a second, and not charged | Logged-out Threads does not serve these lists at all. The aggregate follower_count and like_count are still available. |
| user_search | Exact or near-exact handle resolve. NOT_FOUND with 'no public Threads account resolves for ...' when nothing matches | Fuzzy people search is login-gated by Threads, so this resolves a handle rather than searching people. |
profile returns username, full_name, biography, follower_count, profile_pic_url, user_id, is_verified, is_private and, when the account has any, bio_links. There is no post count and no following count on a logged-out profile.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/threads/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"q": "reef",
"limit": 10
}
}{
"ok": true,
"meta": {
"api": "threads",
"endpoint": "search",
"mode": "live",
"latency_ms": 21238.6,
"record_count": 10,
"bytes": 1894172,
"cache_hit": false,
"method": "browser_service_search",
"doc_id": "[redacted-phone]",
"final_url": "https://www.threads.com/search?q=reef&serp_type=default"
},
"data": {
"results": [
{
"id": "Da6IYpZnARk",
"code": "Da6IYpZnARk",
"username": "swearymichigander",
"text": "Also, she was the coolest. No question.Like2.2KComment294Repost15Share7",
"timestamp_label": "swearymichigander20hMoreI made this look my whole personality in the 90's. I still love tights with jean shorts and would 100% rock that look if it came back. 😂",
"url": "https://www.threads.com/@swearymichigander/post/Da6IYpZnARk"
},
{
"id": "Da6Wub3n4wL",
"code": "Da6Wub3n4wL",
"username": "dalinwestonart",
"text": "Floral Reef\n9\"x12\"\nOriginal abstract painting",
"timestamp_label": "18h",
"url": "https://www.threads.com/@dalinwestonart/post/Da6Wub3n4wL"
},
{
"id": "DapuCLcDziW",
"code": "DapuCLcDziW",
"username": "sprout_in_adversity",
"text": "Moonshine make PE more thicker 🪸\n#coralreef #coral #reeftank #allmymoneygoestocoral #reef",
"timestamp_label": "07/11/26",
"url": "https://www.threads.com/@sprout_in_adversity/post/DapuCLcDziW"
}
]
}
}What the Threads API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| profile | Public Threads profile by username — name, bio, bio links, follower count, verification, profile picture and user id. | Social-listening tools call profile to get public Threads profile by username. | username |
| posts | Public posts from a Threads profile (most-recent first). Returns full text, media, engagement counts, location and music per post. Set `limit` for how many to return. | Creator and influencer platforms call posts to get public posts from a Threads profile (most-recent first). | username, limit |
| replies | Public replies from a Threads profile (the Replies tab). Set `limit` for how many to return. | Brand-monitoring teams call replies to get public replies from a Threads profile (the Replies tab). | username, limit |
| reposts | Posts a Threads user has reposted — the Reposts tab on their profile. Returns the original reposted posts (authored by other accounts). | Audience analysts call reposts to get posts a Threads user has reposted. | username, limit |
| post | Maximally-complete public post detail. Provide url, or username + code, or post_id. Returns text, media (image/video/carousel), all engagement counts, location, music, tagged users, link preview, language and paid-partnership flag. | Social-listening tools call post to get maximally-complete public post detail. | url, username, code, post_id |
| post_replies | The reply conversation for a public post (thread_items). Provide post_id, or url, or username + code. | Creator and influencer platforms call post_replies to get the reply conversation for a public post (thread_items). | post_id, url, username, code, limit |
| search | Search public Threads posts by keyword. Returns matching public posts with author, text and permalink. | Brand-monitoring teams call search to search public Threads posts by keyword. | q, query, limit |
| user_search | Look up the public Threads account whose handle matches a query (exact / near-exact). NOTE: full fuzzy people search is login-gated by Threads; logged-out resolves the matching handle, like instagram's users_search. | Audience analysts call user_search to look up the public Threads account whose handle matches a query (exact / near-exact). | q, query |
| followers | Account-gated: the followers list requires a logged-in Threads session and is not available for public (logged-out) use. | Social-listening tools call followers to get account-gated. | username |
| following | Account-gated: the following list requires a logged-in Threads session and is not available for public (logged-out) use. | Creator and influencer platforms call following to get account-gated. | username |
| likes | Account-gated: the list of accounts that liked a post (who-liked) is not exposed to logged-out clients by Threads — it requires a logged-in session. | Brand-monitoring teams call likes to get account-gated. | url, username, code, post_id |
Call search from your stack
curl -X POST https://api.reefapi.com/threads/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"q":"reef","limit":10}'import requests
r = requests.post(
"https://api.reefapi.com/threads/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"q": "reef",
"limit": 10
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/threads/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"q": "reef",
"limit": 10
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.threads.search with {"q":"reef","limit":10}.Who uses this API and why
- Social analytics call profile and posts to measure a handle's reach.
- Brand monitoring uses search and replies to track mentions.
- Research uses reposts to gauge what spreads.
Questions developers ask before integrating
Why does the replies action return posts written by other accounts?
Because the Threads Replies tab renders the conversation rather than the reply alone, so the parent post travels with it. A measured replies call for zuck with limit 25 returned 25 rows, of which 12 were authored by zuck and 13 by other handles. There are two clean ways to filter: compare the row's username, or read the numeric suffix of id, which is always the author's user id.
Should I check for null or for a missing key?
For a missing key. This engine omits what it did not find rather than emitting null. Measured: zuck's profile has no bio_links key at all while natgeo's and instagram's do; a text-only post carries neither image_url nor video_url; and some posts arrive with no reshare_count key even though quote_count is present and set to 0. Code that reads post.reshare_count directly will throw on perfectly valid posts.
The like count changed between two calls. Which one is right?
Both. These are live counters read at request time. The same natgeo post returned like_count 9413 and repost_count 244 from the posts action, then 9414 and 245 from the post action seconds later. The full set is like_count, reply_count, repost_count, quote_count, plus reshare_count where Threads exposes it. Treat them as a sample with a timestamp, not as a stable key.
What comes back for a video post versus an image post?
media_type tells you which, and the media fields follow from it. A video post returns media_type 'video' with video_url (the mp4) and image_url (the poster frame) plus width and height; an image post returns media_type 'image' with image_url only. Carousels arrive in carousel_media[]. All media points at Meta's CDN with query parameters attached, so download the bytes if you need them later rather than storing the URL.
Why does search return posts in languages I did not ask for?
Because search hands back Threads' own default results page and there is no language parameter. A measured q=reef returned Japanese, Indonesian and English posts inside the same five rows. Search rows are also thinner than profile rows: no counts, no taken_at, and timestamp_label is a display string like '4d' or '03/24/25' which on one row arrived with the post header text glued onto it. Use search to find codes, then call post for anything you plan to store.
What do I get for a handle that does not exist?
user_search answers cleanly: ok false, NOT_FOUND, retryable false. profile is less tidy on the same input and answers PARSE_ERROR with retryable true, because it cannot distinguish 'no such user' from 'the page shape changed'. Treat a repeated PARSE_ERROR on one specific handle as 'no such account' and stop retrying. A PARSE_ERROR that hits every handle at once is the other case.
Can I see who follows an account or who liked a post?
No. followers, following and likes all return DISABLED with retryable false, because Threads serves those lists only to a logged-in session and this engine is public-content-only. The aggregate numbers are unrestricted: profile returned follower_count 5720868 for zuck and 18281334 for natgeo, and every post carries its own like_count.
What is the Threads API?
Threads API is a ReefAPI endpoint group for threads It returns live JSON through POST requests under /threads/v1.
Is the Threads API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Threads calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Threads login or account?
No login to Threads is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Threads data?
The page example is captured from a live profile call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Threads API use?
Threads actions currently cost 1-2 credits per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Threads from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call threads actions with the same key, credit pool and JSON envelope used by normal REST requests.
Is the Threads API a Threads scraper?
It is the managed alternative to a DIY Threads scraper. Instead of building and maintaining your own scraper — proxies, headless browsers, captcha and constant breakage — you call one ReefAPI endpoint and get the same threads back as clean JSON.