Telegram's official APIs are great, but both come with strings attached: the Bot API needs a bot that channel owners must add, and the MTProto client API wants your phone number and an app registration. If all you need is the public message history of public channels, there is a third door with no strings at all.
The t.me preview pages
Every public channel has a web preview:
GET https://t.me/s/durov
That returns the latest ~20 messages as server-rendered HTML. No key, no cookie, no login. And it paginates:
GET https://t.me/s/durov?before=510
gives you the 20 messages older than message id 510. Walk before= down and you can read a channel's history as far back as you care to go.
What is in the HTML
Each message sits in a .tgme_widget_message block with a data-post="channel/id" attribute. Inside:
-
.tgme_widget_message_textholds the message body, links included -
time[datetime]gives a clean ISO 8601 timestamp -
.tgme_widget_message_viewsshows the view count, abbreviated (3.4M) -
.tgme_widget_message_photo_wraphides photo URLs in its inlinebackground-imagestyle -
.tgme_widget_message_forwarded_from_namenames the source channel of a forward, with link
The channel card at t.me/<channel> (without /s/) adds the title, bio and subscriber count via ordinary OpenGraph meta tags plus a .tgme_page_extra element.
Two parsing gotchas
Abbreviated and spaced numbers. View counts come as 3.4M or 12.3K, and subscriber counts as 11 862 837 with narrow no-break spaces (U+202F) between groups, which will break a naive parseInt. Normalize both:
function parseCount(raw) {
const s = raw.replace(/[\s ]/g, '').toLowerCase();
const m = s.match(/^([\d.]+)([km]?)$/);
if (!m) return null;
return Math.round(parseFloat(m[1]) * (m[2] === 'm' ? 1e6 : m[2] === 'k' ? 1e3 : 1));
}
Not every channel has a preview. Private channels, most groups, and channels that disabled previews return a page with no message blocks. Detect the empty case and report it honestly instead of retrying.
What the preview does not have
Reactions, comment counts and poll results are not in the public preview, so no keyless scraper can offer them truthfully. If a tool claims them without asking for API credentials, be suspicious.
Be polite and it just works
The preview pages are served to every browser and link previewer on the internet, so they are tolerant, even from datacenter IPs. Pace your requests a few hundred milliseconds apart and paginate only as deep as you need.
If you want it as a service
I packaged this as a pay per use actor: public channel usernames in, one JSON row per message out (text, date, parsed views, links, media, forward source), plus a free channel info row with the subscriber count: https://apify.com/scrapemint/telegram-channel-scraper
Same theme as the rest of my actors: find the public endpoint a platform already serves, skip the browser, and the data costs almost nothing.
United States
NORTH AMERICA
Related News
Secret Claude Tracker Shocks Users After Anthropic's Anti-Surveillance Stance
12h ago
EV Batteries Defy Expectations, Last Hundreds of Thousands of Miles
1d ago
GBase 8a Performance Anomaly Case Study: How a Single Parameter Change Sparked a Chain Reaction
1d ago
Who Else Has Inherited a Codebase With Zero Comments and a Prayer?
1d ago
完美的平庸
4h ago