# Telegram Keyword Search Scraper — Posts From Public Channels (`memo23/telegram-keyword-search-scraper`) Actor

Search Telegram by keyword. Finds public channels about your topic and returns the posts in them that contain it: text, date, views, reactions, photos, videos, links, hashtags and the channel's subscribers. Filter by date. No login, no API key, no phone number. $2 per 1,000 posts.

- **URL**: https://apify.com/memo23/telegram-keyword-search-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Telegram Keyword Search Scraper: Posts From Public Channels

Search **Telegram by keyword**. The actor finds public channels that talk about your topic and returns the **posts in them that contain your keyword**, newest first, with **text, date, views, reactions, photos, videos, links, hashtags and mentions**, plus each channel's **name, subscribers and description**. **$2 per 1,000 posts.** No login, no API key, no phone number.

![How Telegram Keyword Search Scraper works](https://api.apify.com/v2/key-value-stores/dPYOieasrgsczIYZN/records/how-it-works-telegram-keyword-search.png)

```json
{
  "keywords": ["bitcoin", "remote jobs"],
  "maxItems": 200,
  "postedAfter": "30 days"
}
```

### Why Use This Actor?

- **Posts that actually match.** Every row is a post Telegram itself found for your keyword inside that channel, not just the channel's latest post. `keywordInText` tells you when the match came from the post's own text.
- **Channels found for you.** Give a keyword; the actor finds the channels. Or give your own channel list and search only those.
- **Channel context on every row.** Subscribers, name and description, so you can sort by reach.
- **Date filters** such as `7 days` or `2026-10-01`, applied before billing.
- **Fair billing.** Each post is billed once, even when several keywords find it. Channels that no longer exist cost nothing.
- **$2 per 1,000 posts.**

### Use Cases

- **Brand and product monitoring:** who mentions your brand, token or app on Telegram this week.
- **Crypto research:** airdrops, listings and project news across hundreds of channels.
- **Job and lead sourcing:** remote job posts, hiring announcements and contacts by role or skill.
- **Market and OSINT research:** follow a topic, a place or an event across public channels.
- **Influencer discovery:** find the channels with the biggest reach for a niche.

### How It Works

1. For each keyword, the actor finds public Telegram channels about it.
2. It searches each channel for the keyword and takes the newest matching posts (up to 20 a channel).
3. Your date filters are applied and posts found by more than one keyword are delivered once.
4. The run stops at your post limit, which is shared evenly between your keywords.

### Input Configuration

| Field | Type | Description |
|---|---|---|
| `keywords` | array | What to search for. Any language. |
| `channels` | array | Optional. Search only these channels (`durov`, `@cointelegraph`, `https://t.me/cointelegraph`). |
| `maxItems` | integer | Posts for the whole run, split evenly between keywords. Default 100. Free-plan runs return up to 50. |
| `postedAfter` / `postedBefore` | string | A date (`2026-10-01`) or a range back from today (`7 days`, `2 weeks`, `1 month`). |
| `maxChannelsPerKeyword` | integer | 1-200. Default 30. |
| `maxPostsPerChannel` | integer | 1-20. Default 10, so results come from more channels. |
| `searchDepth` | integer | 1-10. How hard to look for channels; the first step finds the most. Default 2. |
| `useChannelDirectory` | boolean | Also take channels from a public channel directory. Default on. |
| `country`, `language` | string | Two-letter codes that bias which channels are found. Default `us`, `en`. |

### Output Sample

A post for `bitcoin` (2026-11-02):

```json
{
  "keyword": "bitcoin",
  "keywordInText": true,
  "messageUrl": "https://t.me/bitcoinnews/16366",
  "date": "2026-11-01T17:14:02+00:00",
  "text": "₿ STRATEGY REPORTS $20.9B BITCOIN GAIN IN ONE QUARTER\n\nStrategy has reported one of the largest quarterly accounting gains …",
  "views": 94,
  "reactionsTotal": 1,
  "channelUsername": "bitcoinnews",
  "channelName": "Bitcoin ™️ | BTC | News",
  "channelUrl": "https://t.me/bitcoinnews",
  "channelSubscribers": 12500,
  "channelDescription": "Bitcoin news for serious holders.\nMarkets, ETFs, institutions & on-chain.",
  "messageId": 16366,
  "photos": [],
  "videos": [],
  "linkPreview": null,
  "outboundLinks": ["https://t.me/BitcoinNews"],
  "hashtags": [],
  "mentions": ["@BitcoinNews"],
  "emails": [],
  "forwardedFrom": null,
  "replyTo": null,
  "scrapedAt": "2026-11-02T07:31:30.964Z"
}
```

### Key Output Fields

| Field | Meaning |
|---|---|
| `keyword`, `keywordInText` | The keyword that found the post, and whether every word of it (or its singular) is in the post's own text |
| `messageUrl`, `date`, `text` | The post's link, time (UTC) and text |
| `views`, `reactions`, `reactionsTotal` | Engagement at the time of the run |
| `photos`, `videos`, `documents`, `poll`, `linkPreview` | Media and attachments |
| `outboundLinks`, `hashtags`, `mentions`, `emails` | Pulled out of the post text |
| `forwardedFrom`, `replyTo` | Where a forwarded post came from, and the post it replies to |
| `channelUsername`, `channelName`, `channelSubscribers`, `channelDescription` | The channel the post is in |

### FAQ

**Why is `keywordInText` false on some posts?**
Telegram's search also matches a post's link preview and word forms, so a post can match without your exact words in its text. Filter on `keywordInText: true` when you need the words in the text.

**How many posts can I get per keyword?**
Up to 20 of the newest matching posts from each channel, from as many channels as you allow. Raise `maxChannelsPerKeyword` and `searchDepth` for more.

**Does it search groups and private channels?**
No. Only public channels can be searched without an account. Private invite links and groups are skipped.

**How far back does it go?**
The newest matching posts in each channel, which for busy channels means days or weeks and for quiet ones months or years. Use `postedAfter` to keep only recent posts.

**Do I need a Telegram account?**
No.

### Pricing

| Event | When | Price |
|---|---|---|
| Actor start | Once per run | $0.001 |
| Post | Each post delivered | $0.002 |

Example: 3 keywords × 100 posts = $0.60 + $0.001. Posts removed by your date filters and duplicates are not charged.

### Support

Found a bug or need a field? Open an issue on the actor's Issues tab and include the run link.

### ⚠️ Disclaimer

This actor reads public Telegram channels. It is not affiliated with or endorsed by Telegram. Posts belong to their authors. You are responsible for using the data in line with Telegram's terms and the laws that apply to you, including data protection rules for any personal data in posts.

### SEO Keywords

telegram keyword search, telegram search scraper, search telegram posts, telegram scraper, telegram channel search, telegram message search, telegram monitoring, telegram posts by keyword, telegram crypto monitoring, telegram brand monitoring, telegram osint, telegram api alternative

# Actor input Schema

## `keywords` (type: `array`):

Words or phrases to search for, one per line, e.g. `bitcoin`, `remote jobs`, `airdrop`. Each keyword is searched separately. Any language works.

## `channels` (type: `array`):

Leave empty to find channels automatically. Or list public channels to search only those: `durov`, `@cointelegraph` or `https://t.me/cointelegraph`. Private invite links and groups cannot be searched.

## `maxItems` (type: `integer`):

Stop after this many posts in total, across all keywords. You pay only for the posts returned.

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

Only posts from this date on: a date like `2026-10-01`, or a range back from today like `7 days`, `2 weeks`, `1 month`. Leave empty for no limit.

## `postedBefore` (type: `string`):

Only posts before this date, same formats as above. Leave empty for no limit.

## `maxChannelsPerKeyword` (type: `integer`):

How many channels to search for each keyword. More channels give more posts.

## `maxPostsPerChannel` (type: `integer`):

How many of the newest matching posts to take from each channel, up to 20. A lower number spreads your results over more channels.

## `searchDepth` (type: `integer`):

How hard to look for channels for each keyword, from 1 to 10. The first step finds the most; each further step adds a few more. Free-plan runs use 1.

## `useChannelDirectory` (type: `boolean`):

Adds channels listed in a public Telegram channel directory. It finds many more channels for common topics, though some listed channels no longer exist; those are skipped and not charged.

## `country` (type: `string`):

Two-letter country code that biases which channels are found, e.g. `us`, `gb`, `de`, `in`, `br`.

## `language` (type: `string`):

Two-letter language code that biases which channels are found, e.g. `en`, `es`, `ru`, `ar`.

## Actor input object example

```json
{
  "keywords": [
    "bitcoin"
  ],
  "maxItems": 100,
  "maxChannelsPerKeyword": 30,
  "maxPostsPerChannel": 10,
  "searchDepth": 2,
  "useChannelDirectory": true,
  "country": "us",
  "language": "en"
}
```

# Actor output Schema

## `posts` (type: `string`):

One row per post: keyword, post link, date, text, views, reactions, media, links, hashtags, mentions, and the channel's name, subscribers and 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 = {
    "keywords": [
        "bitcoin"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/telegram-keyword-search-scraper").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 = { "keywords": ["bitcoin"] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/telegram-keyword-search-scraper").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 '{
  "keywords": [
    "bitcoin"
  ]
}' |
apify call memo23/telegram-keyword-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/telegram-keyword-search-scraper"
        }
    }
}
```

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/fXOhdKp8VD5eYduCV/builds/0QPSBFhIgQ8cFHrKh/openapi.json
