ThreadsAnswer

Threads API keyword search: approval, limits and like counts

Meta's Threads keyword search needs app approval, allows 2,200 queries a day and documents no like counts. What it covers, and a script that returns them.

6 min read

Short answer

  • Meta's Threads API has a keyword search endpoint. Until Meta approves your app for threads_keyword_search, it searches only your own posts.
  • It allows 2,200 queries per user per rolling 24 hours. None of its documented post fields counts likes, replies or reposts; those come from the Insights API, for your own posts only.
  • ToolzerHub's Threads search returns likes, replies, reposts and quotes per public post. 1 credit per call, $0.99 to $1.90 per 1,000 credits, 100 free credits a month.
On this page

Threads has an official keyword search API, but it searches only your own posts until Meta approves your app for the threads_keyword_search permission. After approval it searches public posts, up to 2,200 queries per user per rolling 24 hours. We read Meta's documentation on 2026-10-12.

What Meta's keyword search returns

The endpoint is GET graph.threads.com/v1.0/keyword_search. The parameters, from Meta's page (updated January 21, 2026):

Parameter Values
q The keyword, required
search_type TOP (default) or RECENT
search_mode KEYWORD (default) or TAG for topic tags
media_type TEXT, IMAGE or VIDEO
since, until Unix time or a date; since no earlier than July 2023
limit Default 25, maximum 100
author_username Only posts by this exact username

Each result is a media object. The fields Meta lists for posts include text, username, permalink, timestamp, media_type, media_url, topic_tag and is_verified. For search results, Meta's page says: "The owner field is excluded and will not be returned."

Limits to plan for

  • You need threads_basic and threads_keyword_search. In Meta's words: "If your app has not been approved for the threads_keyword_search permission, the search will be performed only on posts owned by the authenticated user."
  • The limit is 2,200 queries per user per rolling 24 hours, across all apps. Repeating a keyword counts again; queries with no results don't.
  • Keywords Meta deems sensitive return nothing: "The API will return an empty array for any requests that include keywords that we have deemed sensitive or offensive."

None of the post fields Meta documents (retrieve posts page, updated April 14, 2026) is a count of likes, replies or reposts; has_replies is a true or false flag. Those numbers come from the Threads Insights API, which Meta says "allows you to read the insights from users' own Threads" and which needs threads_manage_insights. So the official search can find a public post that mentions your brand, but its documented fields don't say how many people liked or replied to it.

If you need counts on other people's posts, the search endpoint doesn't provide them. Meta's other limits, such as profile lookups, are in our Threads API overview.

Search Threads with likes and replies

We make ToolzerHub. Our Threads API searches public Threads posts and returns each post with its engagement counts, using one API key and no Meta app. This script runs one search per keyword and writes the posts to CSV.

python
import csv
import os
import sys
import time
from datetime import datetime, timezone

import requests

URL = "https://api.toolzerhub.com/v1/threads/search"
HEADERS = {"x-api-key": os.environ["TOOLZERHUB_API_KEY"]}
FIELDS = ["query", "post_id", "username", "verified", "posted", "likes", "replies",
          "reposts", "quotes", "text", "url"]


def cell(value):
    # Spreadsheets run text that starts with = + - or @ as a formula; an apostrophe keeps it text.
    if isinstance(value, str) and value[:1] in ("=", "+", "-", "@", "\t", "\r"):
        return "'" + value
    return value


def get(params, tries=5):
    # 429 and 503 are retryable; the error body says how long to wait.
    for attempt in range(tries):
        r = requests.get(URL, headers=HEADERS, params=params, timeout=90)
        if r.status_code not in (429, 503) or attempt == tries - 1:
            r.raise_for_status()
            return r.json()["data"]
        wait = r.json().get("error", {}).get("details", {}).get("retryAfter", 5 * (attempt + 1))
        time.sleep(min(wait, 60))


def row(query, post):
    info = post.get("text_post_app_info") or {}
    user = post["user"]
    return {
        "query": query,
        "post_id": post["pk"],
        "username": user["username"],
        "verified": user.get("is_verified"),
        "posted": datetime.fromtimestamp(post["taken_at"], timezone.utc).isoformat(timespec="minutes"),
        "likes": post.get("like_count"),
        "replies": info.get("direct_reply_count"),
        "reposts": info.get("repost_count"),
        "quotes": info.get("quote_count"),
        "text": ((post.get("caption") or {}).get("text") or "").replace("\n", " "),
        "url": f"https://www.threads.com/@{user['username']}/post/{post['code']}",
    }


def main(queries, out="threads.csv"):
    seen = set()
    with open(out, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        writer.writeheader()
        for query in queries.split(","):
            data = get({"q": query})
            for edge in data["edges"]:
                for item in edge["node"]["thread"]["thread_items"]:
                    post = item["post"]
                    if post["pk"] not in seen:
                        seen.add(post["pk"])
                        writer.writerow({k: cell(v) for k, v in row(query, post).items()})
    print(f"wrote {len(seen)} posts to {out}")


if __name__ == "__main__":
    main(*sys.argv[1:])

Each result is a thread, and a thread can hold several posts by its author, so the script writes every post in it. Like counts sit on the post (like_count); replies, reposts and quotes sit in text_post_app_info. If you open the CSV in Excel, import post_id as Text: Excel's number precision is 15 digits, and these IDs have 19.

cell() puts an apostrophe in front of any text that starts with =, +, - or @, so a spreadsheet shows it as text instead of running it as a formula.

Run the script

bash
export TOOLZERHUB_API_KEY=your_key
python threads_search.py "notion,notion ai,notion calendar" threads.csv

Output on 2026-10-12: wrote 60 posts to threads.csv, in 27.5 seconds for three calls. The first three rows, without usernames and text:

query posted verified likes replies reposts quotes
notion 2026-04-26 no 114 1 14 0
notion 2025-07-24 yes 1,242 114 183 2
notion 2024-04-30 no 644 52 100 1

We opened one url from the file logged out. Threads showed the post behind a login prompt, with the same counts as the CSV row: 360 likes, 4 replies, 39 reposts.

What the 60 posts looked like

  • 23 posts for "notion", 20 for "notion ai" and 17 for "notion calendar".
  • Post dates ran from 2024-01-17 to 2026-10-12; 17 were from 2026-09-12 or later. Results are not sorted by date.
  • 13 came from verified accounts. The median post had 129 likes.
  • 54 of the 60 posts contain the query's first word, "notion".
  • The first three posts are in English, Indonesian and Vietnamese: search is not limited to one language.

Differences from Meta's endpoint

  • Each query returns one page with no cursor for more: 17 to 23 posts in our three calls. Meta's endpoint returns up to 100 per call.
  • There is no RECENT sort, date range or media type filter. For "every post about us this week", Meta's endpoint with search_type=RECENT and since fits better, if your app is approved.
  • serp_type=tags returns topic tag results instead of posts.
  • Public posts only; nothing that needs a login.

If you would rather run your own code, our free script covers logged-out search too: scrape Threads search with Python.

What it costs

Each search call is 1 credit, so this run cost 3 credits. At about 20 posts per call, 1,000 posts is about 50 calls across different keywords: 50 credits, or 5 to 10 cents at our rates of $0.99 to $1.90 per 1,000 credits. The free tier gives 100 credits a month.

We are not affiliated with Meta Platforms, Inc.

Sources

Get this data from your own code

The Threads API returns it as JSON with one x-api-key header. 100 free credits monthly.