# Telegram Channel Finder (`apt_marble/telegram-channel-finder`) Actor

Turn a few Telegram channels you know into hundreds you do not. It follows the channels your seeds link to, forward from and name, then returns each one with its exact subscriber count, name, description and alphabet, filtered to the size you want. Every result says which channel led to it.

- **URL**: https://apify.com/apt\_marble/telegram-channel-finder.md
- **Developed by:** [Hamza](https://apify.com/apt_marble) (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

$3.00 / 1,000 channel discovereds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Telegram Channel Finder & Discovery Tool

Give it a handful of Telegram channels you already know and it maps the network around them. It reads what those channels publish, follows every channel they link to, forward from or name in their description, and works out what each one actually is — most turn out to be people, bots or group chats, and those are quietly dropped. What comes back is a clean list of real channels, each with its exact subscriber count, name, description, verified badge and the alphabet it writes in, filtered to the size range you want, and each one labelled with which of your channels led to it and how. Built for anyone who needs more channels in a niche than they can find by hand.

### What you can do with it

- **Build a media list for a niche.** Start with three channels you already advertise on and come back with two hundred more in the same space, each with its subscriber count so you can sort and shortlist them yourself.
- **Find reachable channels, not just the giants.** Set a maximum subscriber count and get the mid-size channels that still answer their messages.
- **Map a competitor's network.** See which channels a rival promotes, quotes and forwards from — and which channels several of them point at, which is usually where the audience actually is.
- **Research one language at a time.** Narrow the results to a single alphabet so a Russian-market list does not fill up with English channels, or the other way round.
- **Feed a bigger pipeline.** Every result carries the channel's name and link, and almost all of them carry its stable numeric identifier too, so you can hand the list straight to a posts, comments or analytics job without cleaning it first.
- **Watch a space grow.** Run it on a schedule with the same seeds and each run gives you a fresh snapshot of the network to compare against the last one.

### What you get

One row per channel found. An abridged real row:

```json
{
  "handle": "contest",
  "canonicalHandle": "telegram",
  "channelId": -1005640892,
  "title": "Telegram News",
  "description": "The official Telegram on Telegram. Much recursion. Very Telegram. Wow.",
  "isVerified": true,
  "subscriberCount": 9907724,
  "subscriberCountIsExact": true,
  "subscriberCountRaw": "9 907 724 subscribers",
  "writingSystem": "latin",
  "depth": 1,
  "isSeed": false,
  "discoveredFrom": "durov",
  "discoveredVia": "post_link",
  "mentionCount": 2,
  "referencedByCount": 1,
  "hasPublicPosts": true,
  "latestPostDate": "2026-07-19T17:58:20.000Z",
  "postsScanned": 20,
  "linkedHandles": ["tginfo", "betainfo"],
  "linkedHandleCount": 2,
  "scrapedAt": "2026-08-05T09:14:02.118Z"
}
```

A channel that keeps its posts private still comes back as a full row, with `hasPublicPosts` set to `false` and its subscriber count intact.

### Input reference

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `seedChannels` | array of strings | — (required) | The channels to start from, one per line. Accepts `@name`, a plain name, or a full Telegram link; a link to a single post is accepted too and resolves to the channel it belongs to. Private invite links are skipped, because nothing about them is public. |
| `depth` | integer | `1` | How many rounds of exploring to do, from 1 to 3. Round 1 returns the channels your seeds point at; each extra round explores what round before it found. More rounds widen the net a lot and drift further from your topic. |
| `maxChannels` | integer | `200` | Stop once this many channels have been returned. This is also your bill. Maximum 5,000. |
| `minSubscribers` | integer | `0` | Skip channels smaller than this. |
| `maxSubscribers` | integer | none | Skip channels larger than this. Leave empty for no upper limit. |
| `language` | string | `any` | Keep only channels writing in one alphabet — Latin, Cyrillic, Arabic, Hebrew, Greek, Chinese, Japanese, Korean, Thai, Devanagari, Armenian or Georgian. Matched against the `writingSystem` column. |
| `postsToScan` | integer | `50` | How many recent posts to read on each explored channel while looking for the channels it points at. More posts find more channels and take longer. Maximum 500. |
| `includeSeedChannels` | boolean | `true` | Whether your own seed channels get a row of their own. Turn it off when you only want channels you did not already know about; they are still explored either way. |
| `country` | string | `us` | Which country to browse from if Telegram cannot be reached the usual way. Most runs never need it and are unaffected by it; on the ones that do, a few channels are restricted in some places, so it can change what is found. |
| `channelsAtOnce` | integer | `16` | How many channels to work through simultaneously. Higher finishes a big job sooner; 32 is the ceiling. |

### Output fields

| Field | Type | Description |
| --- | --- | --- |
| `handle` | string | The channel's name, as the link that led to it wrote it. |
| `canonicalHandle` | string | null | The name Telegram itself publishes for the channel, which can differ from the one above. |
| `url` | string | Link to the channel on Telegram. |
| `channelId` | number | null | The channel's own stable numeric identifier — the only one that survives a rename. Empty for channels that keep their posts private. |
| `title` | string | null | Display name. |
| `description` | string | null | The channel description, as written. |
| `avatarUrl` | string | null | Direct address of the channel picture. Empty when there is no real picture. |
| `isVerified` | boolean | Whether Telegram shows the verified badge. |
| `isScam` | boolean | null | Whether Telegram flags the channel as a scam. Empty on the rare row whose channel page could not be read, because the warning is published there and nowhere else — an empty value means "not known", never "not flagged". |
| `subscriberCount` | number | null | Subscribers at the moment of collection. |
| `subscriberCountIsExact` | boolean | `true` when the number is precise, `false` when it is the rounded figure Telegram published. |
| `subscriberCountRaw` | string | null | The audience line exactly as Telegram published it. |
| `writingSystem` | string | The alphabet the channel writes in, worked out from its name and description: `latin`, `cyrillic`, `arabic`, `hebrew`, `greek`, `chinese`, `japanese`, `korean`, `thai`, `devanagari`, `armenian`, `georgian` or `unknown`. |
| `depth` | number | Which round found it. `0` is a seed you supplied, `1` is a channel your seeds point at, and so on. |
| `isSeed` | boolean | Whether this is one of the channels you supplied. |
| `discoveredFrom` | string | null | The channel that led here. |
| `discoveredVia` | string | null | How it led here: `post_link`, `post_mention`, `forward`, `link_card`, `description` or `seed`. |
| `sourceUrl` | string | null | The exact post the reference appeared in, so every result is auditable. |
| `mentionCount` | number | How many times this channel was referenced in total. |
| `referencedByCount` | number | How many *different* channels referenced it — the better signal of the two, and what the results are prioritised by. |
| `hasPublicPosts` | boolean | Whether this channel publishes its posts publicly. |
| `latestPostId` | number | null | Identifier of the most recent public post. |
| `latestPostDate` | string | null | When that post went out (ISO 8601). |
| `postsScanned` | number | How many of its posts were read while looking for further channels. |
| `linkedHandles` | array | null | The Telegram names this channel points at, up to 50 of them, exactly as it wrote them. These are **names, not verified channels** — a good share of them are people, bots and group chats, and none of them has been looked up. An empty list means it named nobody; `null` means its posts could not be read, so nothing can be said either way. |
| `linkedHandleCount` | number | null | How many names it points at in total, on the same terms as above. |
| `scrapedAt` | string | When this row was collected (ISO 8601). |

### Pricing

You pay per channel returned, and nothing else.

| What you pay for | Price |
| --- | --- |
| Channel discovered — one row per channel returned | **$3.00 per 1,000** |

Worked example: a run that returns **200 channels** costs **$0.60**. Set `maxChannels` and you have capped your bill before you start — nothing else is billable, however many names the run had to look at along the way to find them. Names that turn out to be people, bots, group chats or nothing at all are never charged, and neither is the same channel arriving twice under two different names.

### Limits & what this actor cannot do

- **Discovery grows from your seeds, and only from your seeds.** Telegram publishes no directory of channels and no "channels like this one" list, so there is nothing global to search. Everything returned is a channel that one of your seeds — or something they led to — actually points at. Better seeds are worth more than more rounds.
- Only **public** Telegram channels are covered. Private channels, invite-only groups and one-to-one chats are not publicly viewable and are reported as unavailable rather than returned empty.
- Group chats, people, bots and sticker sets are found constantly and are **not** returned as channels. They are counted and named in the run log — bots, people, group chats and sticker sets each on their own line — so you can see what was skipped and why. The same is true of the `linkedHandles` on each row: those are the names a channel points at, before any of them has been looked up, so expect people, bots and group chats among them.
- Subscriber counts are a snapshot at the moment of collection and keep changing afterwards.
- Telegram publishes very large counts in rounded form in some places. Where that is what was available, the row says so through `subscriberCountIsExact`, so a rounded figure is never presented as a precise one — and if you set a size limit that such a figure rounds across, that channel is skipped and named in the run log rather than admitted or rejected on a number that could be wrong either way.
- The alphabet a channel writes in is worked out from its own name and description. A channel that publishes neither is reported as `unknown`, and is skipped whenever an alphabet filter is set.
- Some channels keep their posts private while remaining public channels. They are returned with their subscriber count and description, but they have no posts to read, so they contribute nothing to further exploring.
- Some channels are restricted in some countries. The country setting only comes into play on the runs where Telegram cannot be reached the usual way, so on most runs it changes nothing.
- Results are written in the order they are found, which is by how many different channels point at each one — not by size. Sort by `subscriberCount` afterwards if that is the order you want.
- Speed depends on the size of the job and on Telegram's own response times; no fixed throughput is promised.
- Telegram's terms govern automated access. You are responsible for using the data lawfully and in line with the source site's terms, and for handling any personal data in line with applicable privacy law.

### FAQ

**Do I need a Telegram account?**
No. Nothing is needed but the channels you want to start from.

**Does it need my phone number or login?**
No. There is no sign-in step of any kind, and you are never asked for a phone number, a password or anything else personal.

**Can I schedule it?**
Yes. Set it to run daily, weekly or monthly on Apify with the same seeds, and each run appends what it finds — which is how you watch a niche grow over time.

**Is the data complete?**
It is everything Telegram publishes publicly about the channels your seeds point at, and nothing more. There is no complete list of Telegram channels anywhere, and this actor does not pretend to have one: it returns the network around what you gave it, which is why the seeds matter so much.

**Why did I get fewer channels than I asked for?**
Because a lot of what channels link to is not a channel. People, bots, group chats, sticker sets and Telegram's own links all show up in posts, and none of them belongs in a channel list. Add more seeds, raise `depth`, raise `postsToScan`, or widen the subscriber range.

**How do I get more results?**
In this order: more seed channels, then more posts read per channel, then one more round of exploring. Extra rounds grow fastest but drift furthest from your topic.

**Why do two rows sometimes describe the same channel?**
They should not. One channel can answer on several different names, and this actor groups by the channel's own identifier rather than by name, so those collapse into one row before you are billed for them. Channels that keep their posts private do not publish that identifier, and are the one case where two names could both appear.

**Can it give me the posts as well?**
Not in this actor — it answers "which channels exist around these ones?". Once you have the list, collecting posts, comments or engagement statistics is a separate job.

# Actor input Schema

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

The channels to start from — one per line, as @name, a plain name, or a full Telegram link. Pick channels close to the topic you care about: everything the actor finds is a channel that your seeds link to, forward from, or name, so better seeds mean better results. Private invite links are skipped, because nothing about them is public.

## `depth` (type: `integer`):

How many rounds of exploring to do. 1 returns the channels your seeds point at. 2 also explores the channels found in round one, and 3 goes one step further again. Each extra round widens the net a lot and drifts further from your original topic, so start at 1.

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

Stop once this many channels have been returned. This is also your bill: you pay per channel returned.

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

Skip channels smaller than this. Leave at 0 to keep every size. Channels that do not publish an audience size at all are skipped whenever any size limit is set, because the filter cannot be checked against them — and so are the few whose only published figure is a rounded one that rounds across the limit you set.

## `maxSubscribers` (type: `integer`):

Skip channels larger than this — useful when you want reachable mid-size channels rather than the giants everybody already knows. Leave empty for no upper limit; 0 is not a limit, it would leave nothing at all.

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

Keep only channels that write in a given alphabet, worked out from the channel's own name and description. Choose "Any" to keep them all. Channels with no readable name or description are reported as unknown and are skipped whenever this is set.

## `postsToScan` (type: `integer`):

How many recent posts to read on each channel while looking for the channels it points at. More posts find more channels and take longer; 50 is a good balance.

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

Keep this on to get a full row for each seed channel as well, with its exact subscriber count and description. Turn it off when you only want the channels you did not already know about.

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

Which country to browse from if Telegram cannot be reached the usual way. A few channels are restricted in some places, so it can change what is found on the runs where it is used — but most runs never need it and are unaffected by this setting.

## `channelsAtOnce` (type: `integer`):

How many channels to work through simultaneously. Higher finishes a big job sooner; 16 is a good balance and 32 is the ceiling.

## Actor input object example

```json
{
  "seedChannels": [
    "@durov",
    "@tginfo"
  ],
  "depth": 1,
  "maxChannels": 100,
  "minSubscribers": 0,
  "language": "any",
  "postsToScan": 50,
  "includeSeedChannels": true,
  "country": "us",
  "channelsAtOnce": 6
}
```

# Actor output Schema

## `dataset` (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 = {
    "seedChannels": [
        "@durov",
        "@tginfo"
    ],
    "depth": 1,
    "maxChannels": 100,
    "minSubscribers": 0,
    "language": "any",
    "postsToScan": 50,
    "includeSeedChannels": true,
    "country": "us",
    "channelsAtOnce": 6
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/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": [
        "@durov",
        "@tginfo",
    ],
    "depth": 1,
    "maxChannels": 100,
    "minSubscribers": 0,
    "language": "any",
    "postsToScan": 50,
    "includeSeedChannels": True,
    "country": "us",
    "channelsAtOnce": 6,
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/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": [
    "@durov",
    "@tginfo"
  ],
  "depth": 1,
  "maxChannels": 100,
  "minSubscribers": 0,
  "language": "any",
  "postsToScan": 50,
  "includeSeedChannels": true,
  "country": "us",
  "channelsAtOnce": 6
}' |
apify call apt_marble/telegram-channel-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apt_marble/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/AGs2fTObsLblAfhP7/builds/KdOen4Qpf7Uroc0mM/openapi.json
