A Telegram channel with 10.9 million subscribers and a channel with 172,000 subscribers. Which one would you advertise in?
Here are their actual numbers, from a run over the last 100 posts of each on 4 September 2026:
| Pavel Durov | Bloomberg | |
|---|---|---|
| Subscribers | 10,900,000 | 172,000 |
| Median views per post | 5,600,000 | 39,900 |
| Reach | 51.4% | 23.2% |
| Reach trend | −10.4% | +44.9% |
| Reactions per post | 1,030 | 144 |
| Posts per day | 0.3 | 0.2 |
Durov's channel reaches half its audience — extraordinary, and 140× more people in absolute terms. But its reach is falling, while Bloomberg's is up 45% over the same window. Depending on whether you're buying one placement or a six-month presence, those two facts point in different directions.
None of this is in the subscriber count, which is the only number most people look at. All of it is available without an API key.
The three ways into Telegram data, and why two of them don't work
Bot API. Requires your bot to be an administrator of the target channel. Perfect for channels you own, useless for every channel you don't.
MTProto / user API. Full access to everything a logged-in user sees. Requires a phone number, a 2FA session and careful rate limiting. Reading public channel history through it is relatively low-risk; bulk member-list exports and messaging strangers are what actually get accounts limited. Either way you are putting a real phone number behind the request, and a restriction lands on the number, not the IP.
The public preview page. Telegram serves t.me/s/<channel> to any visitor, no account required. It's the same HTML a search engine sees.
For monitoring channels you don't own, the third option is the only sane one. What it gives you:
- Channel header: title, subscriber count, description, photo, verified badge, media counts
- 20 posts per request, paged backwards with
?before=<message_id> - Per post: message id, ISO timestamp, full text, view count, reactions with their emoji and counts, media flags, forward source, reply flag, author signature
About 1.4 seconds per page, so 200 posts — ten pages — in roughly fifteen seconds. No token, no proxy required, no ban risk.
Why views are the number that matters
Telegram is unusual in how far it goes here: every post in a public channel carries a view count, visible to anyone, with no login and no API. X and Instagram both surface view counts too, but only inside their own apps, behind authentication and rate limits — which is the difference that matters if you want to compute anything across a list of channels.
That single number lets you compute the thing advertisers actually price against:
reach = median views per post / subscribers
Median, never average. One viral post drags an average up by a factor of three and tells you nothing about what a normal post does. On the Durov channel the average is 6.66M against a 5.6M median, and the best post did 24.2M — if you quoted the average to a client you would be promising reach that a typical post won't deliver.
Reach separates real audiences from bought ones instantly. A channel with 200,000 subscribers and 4% reach was inflated. A channel with 50,000 subscribers and 40% reach is worth more per post than the inflated one, and usually costs less.
What else the timestamps give you
Once you have a few hundred posts with timestamps and view counts, several useful things fall out of simple arithmetic:
Reach trend. Median views of the newer half of the window against the older half. Bloomberg's +44.9% and Durov's −10.4% above come from exactly this. It's a direction, not a forecast, but it's the difference between buying into a channel on the way up and one on the way down.
Best hours and weekdays. Group posts by hour, take the median views of each group, and require at least two posts per group so a single outlier can't win. This is a channel's own data, which beats every generic "best time to post" article ever written, because those articles are about a different audience in a different time zone.
Cadence. Posts per day and per week. A channel posting fifty times a day burns its audience differently from one posting twice.
Content mix. Share of posts with photos, videos, polls, links. Share that are forwards, and which channels they're forwarded from — run that across twenty channels in one niche and you have a map of who feeds whom.
Reaction rate. Reactions per post as a share of median views. Rough, but it separates channels that are read from channels that are merely delivered.
The mistakes worth avoiding
I got most of these wrong before the data corrected me.
A slow channel's 100 posts can span a year. Bloomberg's channel above posts 0.2 times a day, so 100 posts covers well over a year. Any "per day" figure computed over that window is an average across seasons. Always publish the number of days the window covers alongside anything rated per day.
Hashtag extraction needs a letter. A naive #\w+ regex turns #036 from a price into a top hashtag. Require at least one letter in the match.
Reach above 100% is not a bug. Telegram counts a view once per user, including people who saw the post forwarded into another channel or opened it on the web. A post that travels beyond its channel can be seen by more people than the channel has subscribers.
Timestamps are UTC. Convert to the channel's own time zone before concluding anything about a morning slot.
Minimal implementation
The page is server-rendered HTML with stable class names:
import requests, re
from bs4 import BeautifulSoup
def channel_page(name, before=None):
url = f"https://t.me/s/{name}" + (f"?before={before}" if before else "")
html = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}).text
soup = BeautifulSoup(html, "html.parser")
posts = []
for m in soup.select(".tgme_widget_message_wrap"):
views = m.select_one(".tgme_widget_message_views")
time_el = m.select_one(".tgme_widget_message_date time")
posts.append({
"id": int(m.select_one("[data-post]")["data-post"].split("/")[1]),
"date": time_el["datetime"] if time_el else None,
"views": views.get_text(strip=True) if views else None,
})
return posts
Two things this snippet doesn't do, and you need both: view counts arrive as 53K and 1.4M, so they need parsing into integers before any arithmetic; and paging requires passing the lowest message id you've seen back as before, then stopping when a page returns nothing new.
The finished version
I packaged the whole thing — paging, the K/M parser, all the metrics above, error rows for channels that have no public page — as Telegram Channel Analytics.
{
"channels": ["@durov", "@bloomberg"],
"postsPerChannel": 100,
"topPosts": 3
}
One row per channel, with a ready-made sentence at the end of it:
{
"channel": "bloomberg",
"subscribers": 172000,
"postsAnalysed": 100,
"daysCovered": 415.1,
"viewsMedian": 39900,
"engagementRate": 23.2,
"viewsTrendPercent": 44.9,
"reactionsPerPost": 143.9,
"bestHours": [{"hour": 8, "posts": 5, "medianViews": 62300}],
"summary": "Bloomberg has 172,000 subscribers, median post reaches 39,900 views (23.2% of the audience), posts 0.2 times a day, reach up 44.9% across the 100 posts analysed."
}
One naming wrinkle worth flagging: the field is called engagementRate, but it holds what this article calls reach — median views over subscribers. The reaction rate is a separate field.
Switch on includePosts and every post arrives as its own row too, with views, reach, reactions, media flags, hashtags and links.
What it can't do
Private channels and groups are invisible. No public page, no data. Those come back as an explicit error row with the reason, never as a silent zero. Reading private chats requires a real account and a session, which is a different product with different risks.
Subscriber history doesn't exist in the source. Telegram publishes the current count and nothing else. "How fast is this channel growing" can only be answered by running on a schedule and keeping the results — a daily run turns subscribers, viewsMedian and engagementRate into a time series. There is no way to recover last month's number today.
Comments aren't in the preview. Discussion-group replies live elsewhere.
Where this is worth the effort
Buying advertising is the obvious one: compare candidates on reach instead of subscribers and the shortlist reorders immediately.
Selling it is the mirror image. A channel owner quoting reach, reaction rate, cadence and best hours is quoting a media kit. Most don't, because nobody computes it for them.
And competitor tracking, which is the use that compounds: run the same twenty channels weekly, watch whose reach is climbing, then look at what they changed in the same window. The content mix and posting hours are right there in the same row.
Top comments (0)