One keyword in. Clean JSON out.
Search public Threads posts by keyword and get back a list of matching posts as JSON — text, author handle, engagement counts and timestamps. Results are sorted recent-first, not by an opaque relevance score, so this reads as a keyword feed rather than a ranked page. One thing to know up front: Threads does not publish view counts for most posts, so there is no views field in the response.
Availability: Threads does not publish view counts for most posts, so `views` is omitted from responses. Reply threads and post transcripts are planned for v2.
The difference
On the left, what a headless browser hands you. On the right, what the endpoint hands you.
<div class="x1a2b3c x9k2m1 xdt5ytf" data-pressable-container="true"><div class="x78zum5 xdt5ytf"><div class="x9f619 x1n2onr6"><span class="x1lliihq x1plvlek"><span dir="auto" class="x1lliihq xjkvuk6">Everyone building ai agents keeps rediscovering the same three problems.</span></span></div><div class="x6s0dn4 x78zum5 xl56j7k"><div role="button" tabindex="0" class="x1i10hfl x1qjc9v5"><svg aria-label="Like" role="img" viewBox="0 0 24 24"><path d="M16.792 3.904..."></path></svg><span class="x17qophe">88.4K</span></div><div role="button" tabindex="0" class="x1i10hfl"><svg aria-label="Reply"></svg><span>2,314</span></div></div><time datetime="2026-07-07T09:40:00.000Z" class="x1rg5ohu">2h</time></div><script>requireLazy(["ServerJS"],function(f){f().handle({"__bbox":{"complete":true,"require":[["ScheduledServerJS"... {"data": [{"id": "C2isac0P7c7","url": "https://www.threads.com/@zuck/post/C2isac0P7c7","text": "Everyone building ai agents keeps rediscovering the same three problems.","posted_at": "2026-07-07T09:40:00Z","metrics": {"likes": 88410,"replies": 2314,"reposts": 1290,"quotes": 214}}],"meta": {"count": 2,"limit": 20,"item": "result"},"request_id": "req_01JZX4M8Q2TE9W"}
The payload
An array of post objects, one per match. Each carries the post text, the handle that wrote it, four engagement counters (likes, replies, reposts, quotes), the publish timestamp, and two booleans telling you whether the post is itself a reply or a repost of something else. That last pair matters more than it sounds — a keyword feed on Threads is heavily diluted by replies, and you will usually want to filter on them.
The search runs in recent mode. You are getting posts that recently matched your keyword, ordered by time, which is the right shape for monitoring and the wrong shape for finding the all-time best post about a topic. If you need the latter, sort the results yourself on likes after you have them.
Set limit to control how many posts come back; the default is 20. Billing is per returned post, not per call, so a limit of 100 is a 100-post decision, not a free one. Ask for what you will actually read. One call returns one batch of up to limit posts — there is no cursor, no page token and no way to ask for the next page, so limit is the whole depth control you get.
The poster's handle as a bare string, no leading @. This is a handle, not a nested object — follower counts and bio live on the Threads Profile API.
Pricing
Each returned post is 4 credits — about 2¢ on the monthly plan. A default 20-post search runs ~40¢, and an empty search costs just 1 credit.
Estimate only, on a full-year basis. 4 credits per returned result. Monthly: $5 per 1,000 credits with $4/1,000 top-ups. Annual: $54 per 12,000 credits with $3/1,000 top-ups. We show whichever plan is cheaper for your annual volume, divided by 12. Failed calls are never charged; cache hits cost the same as fresh calls.
Entry price, side by side
Credits are not equivalent units across vendors — each meters differently. Price your own workload on both before deciding on entry price alone. At high volume, several of these are cheaper per credit than we are.
Engineering notes
We document exactly how every endpoint behaves — nulls, caching, billing — so what you ship on day one is still running in month six. No surprises, no support tickets.
Threads does not publish view counts on most posts the way X or TikTok do. Rather than return a column that is null nine times out of ten, we leave it out of the response entirely. If your model needs impressions, Threads is not the platform to source them from.
Likes, replies, reposts and quotes are what you get. For reach-shaped analysis, quotes plus reposts is the closest available proxy, and it is a proxy, not a measurement.
Threads conversation is reply-heavy, and a keyword search returns replies alongside original posts. Both is_reply and is_repost are on every item so you can filter. A mention count that does not drop reposts will overstate volume, sometimes badly, because one viral post can reappear a dozen times under different handles.
Filtering happens after billing. You pay for the posts returned, including the ones you throw away, so a narrow keyword is cheaper than a broad one plus client-side filtering.
There are no boolean operators, no field prefixes, no exclusions. Pass a phrase and Threads decides what matches it. Multi-word queries behave loosely. If you need AND-style precision, run two narrow searches and intersect the shortcodes yourself.
Very rare keywords can legitimately return an empty array. That is a successful call with zero results, not an error — but it is still billed, because every successful call has a one-credit floor regardless of how many items came back. An empty search costs 1 credit, not 4 and not 0.
Responses are cached in the fast class, so re-running the same keyword inside the window returns from cache in milliseconds with x-cache: hit. Cache hits cost the same as fresh calls. For a monitoring loop that must not miss anything, pass fresh=true on the poll itself and let the incidental repeats hit cache.
id, url, media_type and the counters are all nullable — the upstream listing is thinner on some posts than others. text is null only on media-only posts. Write your parser to tolerate nulls rather than assuming a full row; a missing counter is a normal response, not a failed one.
Questions
Send a GET to /v1/threads/search with a query parameter and your API key. You get back a JSON array of recent public posts matching that keyword. There is no Threads developer account to apply for and no OAuth step, because the endpoint reads public content logged out. Meta's own Threads API only reaches posts on accounts that have authorised your app.
4 credits per post returned. Credits are $0.005 each on monthly and $0.0045 on annual, so 20 posts costs about 40 cents at monthly pricing. Because billing is per result, the limit parameter is your cost control — set it to the number of posts you will actually process rather than leaving headroom.
Threads does not publish view counts publicly for most posts, so we do not return a views field. Documenting a column that would be null almost every time would be worse than leaving it out. Likes, replies, reposts and quotes are the four counters Threads does expose, and all four are in every response.
No. The query is a plain keyword or phrase, matched by Threads' own search, with no AND, OR, NOT or field prefixes. For an intersection, run two searches and match the shortcodes in your own code. For an exclusion, filter the returned text yourself — remembering that you are billed for posts before you discard them.
No, results come back recent-first. That is deliberate for a search API — the common job is monitoring, where a post from ten minutes ago matters more than a popular one from last month. If you want the highest-engagement posts for a keyword, request a larger limit and sort on likes or quotes after the response lands.
Not from this endpoint. Search returns posts that match your keyword, and some of those happen to be replies, flagged with is_reply. It does not return the reply tree beneath a post you specify. Reply threads and post transcripts are planned for v2. Until then, the reply count on each item tells you how much conversation exists without giving you the text.
Store the shortcode of every post you have processed and skip repeats. Shortcodes are stable per post, unlike the id field, which falls back to the shortcode when the upstream listing omits a numeric id. Also drop items where is_repost is true before counting; the same underlying post can surface repeatedly through different accounts.
Next
100 trial credits on signup — no card, key on screen immediately.