# Telegram Channel Stats - Subscribers & Engagement Rate (`scrapersdelight/telegram-channel-stats-scraper`) Actor

From $0.90 per 1,000 channels, no start fee. Subscriber count, median and mean views, engagement rate, posts per week, and photo/video/link totals for any public Telegram channel. One request per channel, so a 500-channel audit is fast and cheap. No login.

- **URL**: https://apify.com/scrapersdelight/telegram-channel-stats-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Social media, Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.90 / 1,000 per channel auditeds

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 Stats — Subscribers, Reach & Engagement Rate

One audit row per public Telegram channel: **subscriber count, median / mean / max views,
engagement rate as a percentage of subscribers, posting frequency, last-post date** and the
channel's own photo/video/link totals.

Built for the job Telegram itself won't do — deciding whether a channel is worth buying a placement
on. A subscriber count alone is the number channel owners quote; **median views against subscribers**
is the number that says whether those subscribers are real and still reading.

No login. No phone number. No API key. No Telegram account of any kind.

***

### What one row looks like

Measured, not illustrative — this is a real row from `t.me/s/telegram`:

```json
{
  "channel": "telegram",
  "channelTitle": "Telegram News",
  "channelDescription": "The official Telegram on Telegram. Much recursion. Very Telegram. Wow.",
  "channelPhoto": "https://cdn1.telesco.pe/file/fzfcAqQE856VYhKMfz-3YI7W85c…",
  "subscribers": 9540000,
  "photosCount": 16,
  "videosCount": 228,
  "linksCount": 378,
  "sampledPosts": 20,
  "medianViews": 1370000,
  "meanViews": 1498850,
  "maxViews": 2860000,
  "engagementRatePct": 14.4,
  "postsPerWeek": 1.3,
  "newestPostAt": "2026-08-26T19:12:34.000Z",
  "oldestSampledPostAt": "2026-05-14T16:08:31.000Z",
  "withMediaInSample": 16,
  "forwardedInSample": 0
}
```

| field | what it is |
|---|---|
| `subscribers` | **A real number.** Telegram renders `"9.55M"`; this expands it to `9550000` |
| `medianViews` | Median views across the 20 most recent posts — **the headline reach number** |
| `meanViews` · `maxViews` | Mean and best post. A mean far above the median means one viral post is carrying the average |
| `engagementRatePct` | `medianViews ÷ subscribers × 100`. The audience-quality number |
| `postsPerWeek` | Cadence, derived from the real dates of the sampled posts |
| `newestPostAt` | **Is this channel still alive?** A last post six months ago is the single most useful disqualifier |
| `withMediaInSample` · `forwardedInSample` | Content mix — a channel that is 20/20 forwards is an aggregator, not an audience |
| `photosCount` · `videosCount` · `linksCount` | The channel's own lifetime totals |
| `recentPosts` | Optional: the 20 posts the maths was computed from, so you can check the numbers |

### Input

| input | default | what it does |
|---|---|---|
| `channels` | `["telegram","durov"]` | Handles or links — `telegram`, `@telegram`, `https://t.me/telegram` all work. Duplicates are removed, so a channel is never charged twice in a run |
| `includeRecentPosts` | `false` | Attaches the 20 sampled posts — id, date, views, length, media flag |
| `proxyConfiguration` | Apify Proxy | Datacenter is sufficient |

### One request per channel

The audit reads `t.me/s/<channel>` — a single page that carries both the channel header **and** its
20 most recent posts. Every figure above comes off that one page, with no second request and no
paging, which is why a 500-channel audit finishes in minutes and costs **$0.45**.

### Numbers are null when they are unknown, never zero

A channel that doesn't publish its subscriber count reports `engagementRatePct: null` — **not `0`**.
Zero would read as "dead channel" when the truth is "the number wasn't published," and a buyer
sorting a list by engagement would bury a good channel on a fabricated figure. Same for `medianViews`
on a channel with no visible posts, and `postsPerWeek` where a single post can't establish a cadence.
Every row carries `sampledPosts` so you can see what each figure was computed from.

### Channels with no public preview are reported, not billed

Telegram answers a normal page for a private channel, a preview its owner disabled, or a handle that
isn't a channel at all. Those are listed by name under `noPublicPreview` in the run summary and are
**not charged** — so a run against a list of 500 half-stale handles bills only for the ones that
produced an audit, and the run summary tells you which of your handles produced nothing rather than
leaving you to assume the channel is dead.

### Pricing

Pay per event: **$0.0009 per channel audited**, and **no run-start fee**.

Nobody in this category sells channel stats as a standalone row — the alternatives bundle it as a
surcharge on a message scrape, so auditing 500 channels means also paying to download messages you
don't want. Against the closest per-channel rates: **$0.0009** here versus $0.001 and $0.002 + a
$0.005 start fee. And no competitor derives median views, engagement rate or posts-per-week at all —
those are the reason to run this.

A 500-channel audit is **$0.45**. A run that finds nothing costs nothing.

### Source

`t.me/s/<channel>` is Telegram's own public web preview, served to any visitor. The host publishes no
`robots.txt` at all. Public channels are published by their owners to be read publicly; you are
responsible for what you do with the data, which for outreach means the marketing and data-protection
rules wherever you are sending from.

# Actor input Schema

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

Handles or links. `telegram`, `@telegram` and `https://t.me/telegram` all work. Duplicates are removed, so a channel is never charged twice in a run.

## `includeRecentPosts` (type: `boolean`):

Attaches the 20 recent posts the engagement figures were computed from — id, date, views, length and media flag — so you can check the numbers rather than trust them.

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

How many channels to audit at once. One channel is one independent request, so raising this cuts run time - and therefore what the run costs you - almost linearly. 8 is a good default; 16 is the ceiling.

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

Apify Proxy. Datacenter is sufficient.

## Actor input object example

```json
{
  "channels": [
    "telegram",
    "durov"
  ],
  "includeRecentPosts": false,
  "maxConcurrency": 8,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per channel: subscriber count, median/mean/max views over the 20 most recent posts, engagement rate as a percentage of subscribers, posting frequency, last-post date, and the channel's own photo/video/link totals.

## `runSummary` (type: `string`):

RUN\_SUMMARY: channels requested vs delivered vs charged, which had no public preview (and were not charged), and how many rows carry a subscriber count and an engagement rate.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/telegram-channel-stats-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/telegram-channel-stats-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 '{}' |
apify call scrapersdelight/telegram-channel-stats-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/telegram-channel-stats-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/0Z175e43ihBhjqIUK/builds/tWaNJPloiPfBzWVcg/openapi.json
