# Threads Hashtag & Keyword Monitor - New Posts Only (`alom/threads-keyword-monitor`) Actor

Track Threads keywords and #hashtags on a schedule: each run returns only posts you have not received yet, newest first, with text, author, likes, replies, reposts, media and links. Optional keyword-match filter. No login needed. From $1.50 per 1,000 posts.

- **URL**: https://apify.com/alom/threads-keyword-monitor.md
- **Developed by:** [Alom Dev](https://apify.com/alom) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Threads Hashtag & Keyword Monitor

Track **Threads** (threads.com) **keywords and #hashtags** on a schedule. Every run returns **only posts you haven't
received yet**, newest first: text, author, likes, replies, reposts, quotes, images and videos, links, hashtags,
mentions, date and post URL. Built for brand mentions, competitor names, campaign hashtags and topic alerts.

- **Only new posts:** posts an earlier run already delivered are skipped and not charged. On by default.
- **Newest first** by default.
- **Only posts that contain the keyword** (on by default): Threads' logged-out search also returns brand-new posts that
  have nothing to do with the keyword. In one of our tests all 40 newest results for "matcha latte" were such posts;
  this filter drops them (not charged).
- **No login needed.** An own session cookie is optional (see below).
- Send results to Slack, Google Sheets, e-mail or a webhook with Apify integrations, or pull them through the API.

This Actor is a focused version of the [Threads Scraper](https://apify.com/alom/threads-scraper) (same code, same
prices). Use the Threads Scraper if you also need profiles, replies or account search.

### How to set up a monitor

1. Add your keywords and hashtags, e.g. `#yourbrand`, `your brand`, `competitor name`.
2. Leave **Only new posts since my last run** on and pick a **Monitor name** (or keep `default`).
3. Save it as a Task and add a **Schedule** in Apify Console (every hour, every morning...).
4. Optional: add an integration (Slack, webhook, Google Sheets) to get each run's new posts.

The first run returns what Threads shows now; the next runs return only what is new since then.

### Input

| Field | What it does |
|---|---|
| `searchQueries` | Keywords, `#hashtags`, or threads.com search / `/tag/` URLs. |
| `searchSort` | `recent` (default, newest first) or `top`. |
| `requireKeywordMatch` | Only posts whose text, topic tag or link title contain the keyword (the rest are not charged). Default on. |
| `onlyNewSinceLastRun` | Only posts no earlier run with the same monitor name returned. Default on. |
| `monitorName` | Monitor name. Each name has its own memory. Default `default`. |
| `maxPosts` | Max posts per keyword per run. Default 50. |
| `postedAfter` | Skip older posts: a date, ISO timestamp or `7 days`. |
| `sessionCookie` | Optional, your own logged-in cookie (see below). |

```json
{ "searchQueries": ["#coffee", "espresso machine"], "monitorName": "coffee-brand" }
```

The field names are the same as in the Threads Scraper, so an input works in both.

### Output example

```json
{
    "type": "post",
    "postId": "3994109866205639942",
    "code": "Ddt7cL5EfUG",
    "url": "https://www.threads.com/@someuser/post/Ddt7cL5EfUG",
    "text": "Finally dialed in my new espresso machine ☕",
    "username": "someuser",
    "fullName": "Some User",
    "isVerified": false,
    "likeCount": 42,
    "replyCount": 7,
    "repostCount": 1,
    "quoteCount": 0,
    "mediaType": "image",
    "media": [{ "type": "image", "url": "https://scontent…webp", "width": 1440, "height": 1800 }],
    "hashtags": [],
    "topicTag": "Coffee",
    "mentions": [],
    "urls": [],
    "isReply": false,
    "timestamp": 1790355021,
    "date": "2026-09-25T16:50:21.000Z",
    "searchKeyword": "espresso machine",
    "keywordMatch": true,
    "source": "search:espresso machine",
    "scrapedAt": "2026-09-25T17:02:10.000Z"
}
```

Posts carry the same fields as in the Threads Scraper (quoted post, link preview, mentioned accounts, AI label and
more). Values Threads doesn't show are `null`, never `0`. Each keyword's posts come newest first; with several
keywords, their blocks can interleave.

### How much does it cost?

Proposed: the same prices as the Threads Scraper, **from $1.50 per 1,000 posts**, plus $0.005 per run. One result =
one post.

| Apify plan | Price per 1,000 results |
|---|---|
| Free | $2.50 |
| Starter (Bronze) | $2.00 |
| Scale (Silver) | $1.75 |
| Business (Gold) and above | $1.50 |

You pay only for posts you receive. Posts a monitor already delivered, posts filtered out by `requireKeywordMatch` or
`postedAfter`, and duplicates are free. An hourly run that finds nothing new costs only the run fee. Platform usage
(compute, proxy) is included.

### Own session cookie (optional, at your own risk)

You never need it. If you paste the cookies of **your own** logged-in threads.com tab into `sessionCookie` (the
`Cookie` header or a JSON export with at least `sessionid`), searches use Threads' real **Recent** tab and page through
it like the app, which finds more recent posts per keyword. Requests are then made **as your account** (one IP, at most
2 in parallel). Meta may rate-limit, checkpoint or ban accounts that scrape: use a spare account, and you carry that
risk. The cookie is stored encrypted as a secret input and never logged or written to the results. If Threads
rejects it, the run says so in the log and continues logged out.

### Limitations

- **Logged-out search shows ~20 posts per result page and no "next page".** Each run opens several result pages
  (keyword search, tag search, tag page) from fresh IPs and merges them: **typically 60-85 unique posts per keyword
  per run**. A busy hashtag can get more new posts per hour than that; schedule more often, or use your own cookie.
- Without a cookie Threads has no "Recent" tab for logged-out visitors, so "newest first" sorts the posts that were
  found. A post can show up a few runs late if Threads didn't show it earlier.
- With the keyword filter on, a rare keyword can return few or no posts in a run: that is the honest answer, not an
  error (the run ends as `empty`).
- Monitor memory keeps the last 200,000 delivered posts per monitor name.
- Not available without login: view counts, polls.

### FAQ

**Do I need a Threads or Instagram account?** No. The cookie is optional.

**How do I start a monitor from scratch?** Use a new monitor name.

**Is it legal?** It collects publicly visible posts. You are responsible for how you use the data, including data
protection rules for personal data (usernames, post text).

### More scrapers from the same developer

- [Threads Scraper](https://apify.com/alom/threads-scraper): posts, profiles, replies, reposts, keyword and hashtag search, account search
- [Google Trends API & Scraper](https://apify.com/alom/google-trends-scraper): interest over time, regions, related queries and Trending Now, a pytrends alternative
- [YouTube Scraper](https://apify.com/alom/youtube-scraper): videos, channels, Shorts, comments, subtitles and community posts without the API quota
- [Bilibili Scraper](https://apify.com/alom/bilibili-scraper): videos, creators, full comment threads and danmaku from B站, no login
- [Google Hotels Scraper](https://apify.com/alom/google-hotels-scraper): hotel prices from every booking site across dates, room rates and reviews
- [Google Ads Transparency Scraper](https://apify.com/alom/google-ads-transparency-scraper): every Google ad a competitor runs, with the real ad copy
- [Threads Account Finder](https://apify.com/alom/threads-lead-finder): Threads accounts by keyword with followers, bio links and the contacts they list

### Feedback

Missing a field, found a bug, or need a feature? Open an issue in the **Issues** tab and I'll take a look. If this
Actor saved you time, a short review on the Store page helps other people find it.

# Actor input Schema

## `searchQueries` (type: `array`):

What to track: keywords, <code>#hashtags</code>, or threads.com search / <code>/tag/</code> URLs. Without a session cookie Threads shows logged-out visitors ~20 posts per result page, so each run opens several result pages and merges them: typically <b>60-85 unique posts per keyword</b> per run.

## `searchSort` (type: `string`):

<b>Newest first</b> (default) is what a monitor wants. Without a session cookie Threads shows logged-out visitors no "Recent" tab, so the posts found on several result pages are sorted newest first; with your cookie (below) Threads' real Recent tab is used.

## `requireKeywordMatch` (type: `boolean`):

Threads' logged-out search also returns brand-new posts that don't mention the keyword at all. On (default): only posts whose text, hashtags, topic tag or link title contain the keyword are returned; the rest are not charged. Every row has <code>keywordMatch</code> either way.

## `onlyNewSinceLastRun` (type: `boolean`):

Skip posts an earlier run with the same monitor name already returned (you are not charged for them). Pair with a Schedule in Apify Console. Turn off to get everything the search shows each time.

## `monitorName` (type: `string`):

Each name keeps its own memory of delivered posts, e.g. <code>brand-mentions</code> and <code>competitor-hashtags</code>. Use a new name to start from scratch.

## `maxPosts` (type: `integer`):

Upper limit per keyword and run. With monitoring on, only new posts count.

## `postedAfter` (type: `string`):

Date (UTC), ISO timestamp or relative like <code>7 days</code>. Older posts are skipped (not charged).

## `sessionCookie` (type: `string`):

<b>Optional and never required.</b> Paste the cookies of a threads.com tab where you are logged in (the <code>Cookie</code> header, or a JSON export; at least <code>sessionid</code>, ideally also <code>csrftoken</code> and <code>ds\_user\_id</code>). Searches then use Threads' real "Recent" tab and page through it like the app (about twice as many recent posts in our tests). Requests are then made <b>as your account</b> (one IP, at most 2 in parallel): Meta may rate-limit, checkpoint or ban accounts that scrape, so use a spare account. If the cookie is invalid or expired, the run says so and continues without it. Stored encrypted, never logged or returned.

## `maxConcurrency` (type: `integer`):

Keywords processed in parallel.

## `proxyConfiguration` (type: `object`):

The default Apify datacenter proxy works and is included in the price. If Threads starts blocking it, the run switches to residential proxies by itself (logged).

## Actor input object example

```json
{
  "searchQueries": [
    "#coffee",
    "espresso machine"
  ],
  "searchSort": "recent",
  "requireKeywordMatch": true,
  "onlyNewSinceLastRun": true,
  "monitorName": "default",
  "maxPosts": 50,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "#coffee",
        "espresso machine"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("alom/threads-keyword-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "searchQueries": [
        "#coffee",
        "espresso machine",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("alom/threads-keyword-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "#coffee",
    "espresso machine"
  ]
}' |
apify call alom/threads-keyword-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,alom/threads-keyword-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/XIqFUvoy3UKtuRhSO/builds/b09MfecItWuxQUHcf/openapi.json
