# Telegram Channel Search – Find Channels by Keyword (`ryumoon-data/telegram-channel-finder`) Actor

Find public Telegram channels and groups by keyword or from seed channels. Snowball via forwards, links and mentions. Subscribers, verified flag, last post, matching posts. No login. $3 per 1,000 channels.

- **URL**: https://apify.com/ryumoon-data/telegram-channel-finder.md
- **Developed by:** [Ryu Moon](https://apify.com/ryumoon-data) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 channel results

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 Channel Finder & Keyword Search (No Login)

**Find public Telegram channels and groups about any topic, with subscriber counts, verified badge, last post date and the posts that match your keywords.** Start from a few seed channels, a few keywords, or both. No Telegram account, phone number, bot token, API key or paid search API needed. Export to JSON, CSV, Excel or Google Sheets.

### Use with AI agents (MCP, Claude, ChatGPT, Cursor)

**Purpose:** Find public Telegram channels and groups about a topic or keyword, with subscriber counts, last post date and sample matching posts. No login.

**When to use it**

- The user wants a list of Telegram channels about a topic, coin, brand or niche (for outreach, research or monitoring).
- You have a few known channels and want related ones (snowball discovery through forwards, links and @mentions).

**When not to use it**

- To collect the full post history of known channels, use [Telegram Channel Scraper](https://apify.com/ryumoon-data/telegram-channel-keyword-scraper) instead.
- It does not search Google and will not find every channel on a topic; good `seedChannels` give the best results.

**Example input**

```json
{
  "keywords": [
    "solana"
  ],
  "seedChannels": [
    "tginfo"
  ],
  "minSubscribers": 1000,
  "maxChannels": 20
}
```

**Output fields (one dataset item per row):** `username`, `url`, `title`, `type` (channel/group), `subscribers`, `members`, `verified`, `lastPostDate`, `avgViewsRecentPosts`, `matchedKeywords`, `matchingPostsFound`, `sampleMatches` (link, date, views, snippet), `description`, `lastPostUrl`, `referencedTimes`, `discoveredVia`, `discoveredFrom`, `depth`.

**Cost:** $0.005 per run + $0.003 per channel returned (20 channels ≈ $0.07). Cap spend with `maxChannels`.

**Connect it to your agent:** add this remote MCP server (Apify's official MCP server, loaded with just this tool):

```json
{"mcpServers": {"telegram-channel-finder": {"url": "https://mcp.apify.com?tools=ryumoon-data/telegram-channel-finder"}}}
```

Or call it from any agent with the Apify API: `POST https://api.apify.com/v2/acts/ryumoon-data~telegram-channel-finder/run-sync-get-dataset-items` with the input above as the JSON body. Agents without an Apify account can pay per run with [agentic payments](https://docs.apify.com/agents/payments) (x402, MPP or Skyfire).

### How it finds channels (honest version)

Telegram has no public "search all channels" page, so this Actor uses three free, transparent methods:

1. **Snowball discovery from seed channels**: it reads each seed's recent posts (the public `t.me/s/<channel>` preview) and collects every channel they **forward from, link to or @mention**, plus channels listed in channel descriptions. With *Discovery depth* 2 it repeats this on the channels it found.
2. **Username probing from keywords**: for a keyword like `bitcoin` it checks likely usernames such as `bitcoin`, `bitcoinnews`, `bitcoinchannel`, `thebitcoin`, `bitcoin_news` and keeps the ones that exist.
3. **Keyword search inside each channel**: it uses Telegram's own public in-channel search to count matching posts and return sample matches, and (by default) keeps only channels that match.

It does **not** query Google or other search engines, so it will not find every channel on a topic. It finds channels connected to your seeds and to likely usernames. Good seeds (directories, news aggregators, the big channels in your niche) give the best results.

### What you get

- 📇 **Channel metadata**: username, link, title, description, subscriber count (members for groups), verified badge, scam/fake label, profile photo, media counts.
- 📅 **Activity**: last post date and link, average views of recent posts.
- 🔎 **Keyword matches**: which keywords matched, how many matching posts Telegram's search returned (first page, up to 20 per keyword) and sample posts with link, date, views and a snippet.
- 🧭 **Discovery trail**: how each channel was found (`seed`, `forward`, `link`, `mention`, `description`, `username-probe`), from which channel, at what depth and how often it was referenced.
- 🎯 **Filters**: minimum subscribers, posted within N days, only keyword matches, include or exclude groups.

### What it never does

- ❌ No member lists, no user profiles, no phone numbers, no private chats. Personal accounts and bots it comes across are skipped and nothing about them is saved.
- ❌ No login: it sees only what a logged-out visitor sees on t.me.

### Use cases

- **Influencer and ad outreach**: build a list of channels in your niche with audience size and activity.
- **Market and competitor research**: map the Telegram ecosystem around a brand, coin, product or topic.
- **Brand monitoring**: find channels that talk about your brand and see sample posts.
- **OSINT and journalism**: trace which channels forward and promote each other.
- **Feed it into our [Telegram Channel Scraper & Keyword Monitor](https://apify.com/ryumoon-data/telegram-channel-keyword-scraper)** to collect full post histories of the channels you found.

### How much does it cost?

This Actor uses pay-per-event pricing. You pay only for these events:

| Event | Price | When it is charged |
|---|---|---|
| Actor start | **$0.005** | Once per run (at the default 1 GB memory) |
| Channel result | **$0.003** | Each channel or group saved to the dataset ($3 per 1,000) |

Candidates that are checked but filtered out (no keyword match, too small, inactive, bots, personal accounts, non-existent usernames) are **not charged**.

Examples:

- 20 channels: $0.005 + 20 × $0.003 = **$0.065**
- 100 channels: $0.005 + 100 × $0.003 = **$0.305**

Use *Max channels to return* or a *maximum cost per run* in Apify Console to cap spend.

### Input

| Field | What it does | Default |
|---|---|---|
| `seedChannels` | Channels to start from | prefill `["tginfo", "tchannels"]` |
| `keywords` | Topic words | none |
| `probeUsernamesFromKeywords`, `maxProbesPerKeyword` | Try usernames like `<keyword>news` | `true`, `12` |
| `maxDepth` | 0 = seeds only, 1 = + referenced channels, 2 = + their references | `1` |
| `pagesPerChannel` | ~20 posts per page read for discovery | `1` |
| `searchPostsForKeywords` | Use Telegram's in-channel search for each keyword | `true` |
| `onlyMatchingChannels` | Keep only keyword matches (when keywords are set) | `true` |
| `includeSeedChannels`, `includeGroups` | Include seeds / public groups | `true`, `true` |
| `minSubscribers`, `activeWithinDays` | Size and activity filters | `0`, `0` |
| `maxSampleMatches` | Sample posts per channel | `3` |
| `maxChannels`, `maxCandidates` | Limits | `50`, `300` |
| `maxConcurrency`, `requestDelayMs`, `maxRequestRetries`, `proxyConfiguration` | Advanced | `3`, `500`, `4`, none |

Examples:

```json
{ "keywords": ["bitcoin"], "maxChannels": 30 }
```

```json
{ "seedChannels": ["tginfo"], "keywords": ["android"], "maxDepth": 2, "minSubscribers": 1000, "activeWithinDays": 30 }
```

### Output (one row per channel or group)

Real row from `sample-output.json` (photo URL trimmed):

```json
{
  "username": "android",
  "url": "https://t.me/android",
  "type": "channel",
  "title": "Android GameTech",
  "description": "We show and tell about technologies, games and equipment.",
  "subscribers": 6660,
  "members": null,
  "verified": false,
  "scamOrFakeLabel": false,
  "hasWebPreview": true,
  "photoUrl": "https://cdn4.telesco.pe/file/…",
  "mediaCounts": { "photos": 85, "videos": 27, "files": null, "links": 11 },
  "lastPostDate": "2026-06-01T08:44:21.000Z",
  "lastPostUrl": "https://t.me/android/130",
  "avgViewsRecentPosts": 26253,
  "isSeed": false,
  "depth": 0,
  "discoveredVia": ["username-probe"],
  "discoveredFrom": [],
  "referencedTimes": 0,
  "matchedKeywords": ["android"],
  "matchingPostsFound": 4,
  "sampleMatches": [
    {
      "keyword": "android",
      "url": "https://t.me/android/74",
      "date": "2025-05-30T11:43:32.000Z",
      "views": 4880,
      "text": "iOS 19 will allow you to transfer eSIM from iPhone to Android on your own."
    }
  ],
  "scrapedAt": "2026-11-02T09:14:50.427Z"
}
```

Groups and channels with the web preview turned off have `hasWebPreview: false` and only title, description and subscriber/member count (no posts, so no last post date or matches beyond the title and description).

### Limits (please read)

- **Not a full index of Telegram.** Discovery is limited to channels connected to your seeds and to likely usernames. Results depend heavily on the seeds you choose.
- **Matching post counts are capped** at one page of Telegram search results (up to about 20 per keyword per channel), so treat `matchingPostsFound` as "at least".
- **Subscriber counts** are as Telegram shows them (large numbers are rounded, e.g. 1.09M).
- **Username probing** can find unrelated channels that happen to use the name; with keywords set, only channels whose username, title, description or posts contain the keyword are kept.
- Photo URLs come from Telegram's CDN and may expire.

### FAQ

**Do I need a Telegram account?** No. Everything comes from public `t.me` pages anyone can open in a browser.

**Can I search with only keywords?** Yes. The Actor probes likely usernames and then snowballs from the ones that exist. Adding a couple of seed channels from your niche usually finds many more.

**Why wasn't a channel I know found?** Nothing in your seeds referenced it and its username doesn't match the probe patterns. Add it (or a channel that mentions it) as a seed, or raise *Discovery depth*.

**Can it export group members?** No, by design.

**Is it legal?** It only reads public pages and returns channel-level information. Sample posts are public posts from public channels. You are responsible for how you use the data, including GDPR where it applies. Defaults are deliberately polite.

### Changelog

- 0.1 – first release: snowball discovery (forwards, links, mentions, descriptions), username probing, in-channel keyword search, filters.

# Actor input Schema

## `seedChannels` (type: `array`):

Public channels to start from (usernames or t.me links). The Actor reads their recent posts and follows the channels they forward from, link to or @mention. Directory-style channels and news aggregators make the best seeds.

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

Topic words, brands or phrases, e.g. \["bitcoin", "remote jobs"]. You can use keywords alone (no seed channels). Used to (1) try matching channel usernames such as 'bitcoinnews', (2) search each found channel's posts with Telegram's in-channel search, and (3) keep only channels that match.

## `probeUsernamesFromKeywords` (type: `boolean`):

Try likely usernames built from each keyword (e.g. bitcoin, bitcoinnews, bitcoin_news, thebitcoin) as extra starting points. Lets you search with keywords only, no seeds.

## `maxProbesPerKeyword` (type: `integer`):

How many username guesses to try for each keyword.

## `maxDepth` (type: `integer`):

0 = only check the seeds and probed usernames. 1 = also check every channel they reference. 2 = also the channels those reference (grows fast).

## `pagesPerChannel` (type: `integer`):

Each page is about 20 recent posts. More pages find more referenced channels but take more requests.

## `searchPostsForKeywords` (type: `boolean`):

Uses Telegram's public in-channel search (one request per keyword per channel) to count matching posts and return sample matches. Off = only check title, description and the recent posts already read.

## `onlyMatchingChannels` (type: `boolean`):

When keywords are set, skip channels whose username, title, description and posts don't contain any keyword.

## `includeSeedChannels` (type: `boolean`):

Also return a row for each seed channel.

## `includeGroups` (type: `boolean`):

Return public groups too (title, description and member count only; never member lists).

## `minSubscribers` (type: `integer`):

Skip discovered channels smaller than this.

## `activeWithinDays` (type: `integer`):

Skip channels whose last visible post is older than this. 0 = no filter. Channels without a web preview have no post date and are skipped when this is set.

## `maxSampleMatches` (type: `integer`):

How many matching posts (link, date, views and a text snippet) to include per channel.

## `maxChannels` (type: `integer`):

Stops when this many channels/groups have been saved. Use it to cap cost.

## `maxCandidates` (type: `integer`):

Upper bound on usernames checked (including ones filtered out).

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

Channels checked at the same time.

## `requestDelayMs` (type: `integer`):

Keeps the request rate polite towards t.me.

## `maxRequestRetries` (type: `integer`):

Retries with back-off when t.me rate-limits or fails.

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

Usually not needed. Use a proxy for very large discovery runs.

## Actor input object example

```json
{
  "seedChannels": [
    "tginfo",
    "tchannels"
  ],
  "probeUsernamesFromKeywords": true,
  "maxProbesPerKeyword": 12,
  "maxDepth": 1,
  "pagesPerChannel": 1,
  "searchPostsForKeywords": true,
  "onlyMatchingChannels": true,
  "includeSeedChannels": true,
  "includeGroups": true,
  "minSubscribers": 0,
  "activeWithinDays": 0,
  "maxSampleMatches": 3,
  "maxChannels": 20,
  "maxCandidates": 300,
  "maxConcurrency": 3,
  "requestDelayMs": 500,
  "maxRequestRetries": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All channels and groups stored in the run's default dataset.

# 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 = {
    "seedChannels": [
        "tginfo",
        "tchannels"
    ],
    "maxChannels": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("ryumoon-data/telegram-channel-finder").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 = {
    "seedChannels": [
        "tginfo",
        "tchannels",
    ],
    "maxChannels": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("ryumoon-data/telegram-channel-finder").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 '{
  "seedChannels": [
    "tginfo",
    "tchannels"
  ],
  "maxChannels": 20
}' |
apify call ryumoon-data/telegram-channel-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ryumoon-data/telegram-channel-finder"
        }
    }
}
```

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/XAJlM1X8f8EGjskQp/builds/PD3NHDwwEX5eyrdZP/openapi.json
