DEV Community

Devil Scrapes
Devil Scrapes

Posted on

Scrape public Telegram channels without a bot token or phone number

Quick answer

Public Telegram channels expose a preview surface at t.me/s/<channel> that renders without a login, an API key, or a phone number. The Telegram Channel Scraper parses that surface into 12 typed fields per post — including view counts, media classification, outbound links, and forward provenance — at $0.002 per post ($2.00 per 1,000) plus a $0.01 run start. No Bot API token, no MTProto session, no account required.

The thing most people miss about Telegram 📡

Ask how to scrape Telegram and you'll be told to use the Bot API or MTProto. Both are real options and both come with real costs: the Bot API only sees channels your bot has been added to, and MTProto means registering an application, holding a session, and tying collection to a phone number you control.

For public channels, neither is necessary. Telegram publishes a server-rendered preview at https://t.me/s/<channel> — the same page that renders when someone shares a channel link into a browser. It's plain HTML, it's public, and it contains substantially more structured data than people expect: post IDs, timestamps in machine-readable attributes, view counts, media containers, link previews, and forward attribution.

That's the surface this Actor targets. It's the difference between "I need to build an account-backed collection pipeline" and "I need a URL."

What the preview surface will and won't give you ⚖️

Being straight about the boundaries matters more than a feature list, so here they are.

It gives you: post ID, timestamp, clean text, view count, media type and URL, outbound links, forward source, and permalink. That's enough for monitoring, archival, link-graph analysis, and corpus building.

It does not give you: reply counts. The field exists on our schema as reply_count and it is documented as reserved — not exposed by the /s/ preview surface today. We ship it as a nullable field for forward-compatibility rather than fabricating a number or pretending the gap doesn't exist. If Telegram exposes it later, existing consumers don't have to change their schema.

It does not cover private or invite-only channels. If a channel isn't publicly previewable, no amount of scraping cleverness changes that, and we won't sell you a tool that claims otherwise.

The parsing work that isn't obvious 🔧

The page is public. That does not mean the extraction is trivial.

View counts are lossy strings. Telegram renders 12.4K and 1.1M, not integers. Every one of those has to be expanded back to a number, and the expansion has to be right — 1.1M is 1,100,000, and a naive float * 1_000_000 on a locale where the decimal separator differs gives you garbage.

Text is HTML with entities. Posts contain markup, emoji, and HTML entities that need decoding into clean text. Dumping innerHTML into a text field pushes that work onto every consumer of your dataset.

Media needs classifying, not just detecting. A post can carry a photo, a video, a document, or nothing. Each renders in a different container with a different URL-extraction path. We normalize this to a media_type enum (photo / video / document / none) plus a media_url, so downstream filtering is a single comparison instead of four selector checks.

Forwards carry provenance. forwarded_from captures the source channel when a post is a forward. For anyone doing narrative-propagation or coordinated-amplification analysis, this single field is often the entire reason to collect the data.

Outbound links live in two places. URLs appear both in the post body and in the rendered link preview. We collect from both into outbound_links, because a link-graph built from only one of them is a link-graph with holes.

Full output schema 📦

Twelve fields per post row, Pydantic-validated:

Field Type Notes
row_type "post" Discriminator; channel-meta rows are never billed
channel string Normalized channel username
post_id int Integer suffix of the data-post attribute
datetime datetime From time.time[datetime]
text string HTML stripped, entities decoded
view_count int | null K/M suffixes expanded
media_type enum photo / video / document / none
media_url string \ null
outbound_links list[string] From link preview and body text
forwarded_from string | null Source channel if the post is a forward
reply_count int \ null
permalink string https://t.me/{channel}/{post_id}

What people build with this

OSINT and information-operations research. forwarded_from plus datetime across a set of channels reconstructs how a message propagated and in what order. That's the core primitive for coordinated-amplification analysis.

Crypto and market-signal monitoring. A large share of early token discussion happens in public Telegram channels. view_count gives you a reach proxy; outbound_links gives you the contract addresses and site links being pushed.

LLM training corpora. Public channel text is conversational, multilingual, and topically clustered — useful characteristics for a training set, and the clean text field means no HTML cleanup pass on your side.

Brand and narrative monitoring. Track mentions across public channels without maintaining an account, a phone number, or a session that can be flagged.

Frequently asked questions

Do I need a Telegram account, bot token, or phone number?
No. The /s/ preview surface is public and unauthenticated. That's the whole design premise.

What does 5,000 posts cost?
$10.01 — 5,000 × $0.002 plus the $0.01 run start. Channel-metadata rows are emitted but never billed.

Can it read private channels?
No, and it shouldn't be able to. Only publicly previewable channels are in scope.

Why is reply_count always null?
Because the preview surface doesn't publish it. We'd rather ship a documented, honestly-null field than invent a number or quietly drop it from the schema later.

Is this rate-limited or blocked?
Telegram is far less adversarial here than most commercial targets, but it isn't unlimited. Runs pace their requests and handle transient failures with retries rather than dropping posts on the floor.

Try it

Live on the Apify Store: Telegram Channel Scraper.

Point it at a public channel username and it returns typed rows. Pay-per-event, no subscription.


Built by Devil Scrapes — we build scrapers for the targets that fight back.

Top comments (0)