Telegram Channel Scraper — Posts, Views & Media
Pricing
from $0.85 / 1,000 posts
Telegram Channel Scraper — Posts, Views & Media
Export what public Telegram channels post: the message text, when it went up, the view count, photos and video, and what each post forwarded or replied to. Many channels in one run, and a later run picks up only what is new. No login and no phone number; channel names come from the input list.
Pricing
from $0.85 / 1,000 posts
Rating
0.0
(0)
Developer
The Netaji
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 days ago
Last modified
Categories
Share
Telegram Channel Scraper
Export what a public Telegram channel has posted, one row per post: the message text with its line breaks intact, the permalink, when it went up, how many views it has, any photo or video, and what the post was forwarded from or replying to. A run reads a whole list of channels into one dataset, and a later run can continue from where the previous one stopped rather than re-reading a channel from the top.
Nothing is signed into. No bot token, no phone number and no API credentials are involved at any
point, because the source is the public channel preview that any browser can open at
t.me/s/<channel>. The channel names come from the run's input; Telegram publishes no directory,
so there is nothing to search through here.
What a run returns
{"channel": "telegram","channel_title": "Telegram News","channel_url": "https://t.me/s/telegram","id": "telegram/435","message_number": 435,"url": "https://t.me/telegram/435","published_at": "2026-04-09T07:01:44+00:00","views_display": "1.56M","text": "Hello world","photo_url": null,"video_url": null,"forwarded_from_name": null,"reply_to_text": null}
Every row carries the same thirteen fields: channel, channel_title, channel_url, id,
message_number, url, published_at, views_display, text, photo_url, video_url,
forwarded_from_name and reply_to_text. A field the post does not have is returned as null
rather than omitted, so every row has the same shape whatever mix of posts a run collected.
Each post names the channel it came from, because a run collects several channels into a single
dataset and a post that cannot say where it came from cannot be grouped or joined afterwards. The
channel's own title and address are published as channel_title and channel_url; the plain url
is always the post's own permalink. id is Telegram's own key for a post, <channel>/<number>,
and message_number is the second half of it.
Accepted input
channels is required and takes one or more channels, one per line, in whatever form is closest to
hand. telegram, @telegram, t.me/telegram, https://t.me/s/telegram and
https://t.me/telegram/435 all resolve to the same channel, and a channel listed twice is read
once.
maxItems caps how many posts are saved from each channel and defaults to 100. A value of 0
removes the cap and lets a run continue to the channel's first post.
before and after are optional and are how a run resumes. before starts just older than a given
post number and works backwards through the history; after collects only posts newer than a given
post number, which is how a run picks up what has been published since the last one. A run that
supplies neither starts at the channel's newest post.
{"channels": ["telegram", "durov"],"maxItems": 40}
Questions
Why did a run save fewer posts than maxItems?
Because the channel ran out first. Telegram publishes no post total and no page count, so a run
cannot compare its position against anything and instead stops on the signals the channel actually
gives: a page that comes back with no posts on it, no position to continue from, or a position that
fails to advance. A short page part-way through is deliberately not one of those signals. Walking a
real channel backwards, the final populated page carried four posts, numbers 1 to 4, and the empty
call after it marked the end; treating a short page as the end would cut every such run off a page
early.
What number does before want, and why is it the lowest on the page rather than the highest?
It is a position in the stream, and the posts returned are the twenty older than that position, so
sending the highest number just read would return the same twenty again. Measured on one channel:
no cursor returned posts 435 to 454, before=435 returned 414 to 433, and before=414 returned
394 to 413, which is 80 posts across four calls with zero overlap. before=5 returned posts 1 to
4, so the beginning of a channel is reachable. The value is the message_number on the oldest row
of the previous run, and a run stopped by maxItems prints it in the log, so nobody has to work it
out.
Why did a cursor apply to the first channel only?
Post numbers count from 1 inside each channel separately, so post 435 of one channel has nothing to
do with post 435 of another. A before or after value applies to the first channel in the list;
every channel after it starts at its own newest post. Resuming several channels at their own
positions means one run per channel.
Can before and after be combined to select a range?
No, and a run supplying both is refused before it spends a single request. Telegram honours one of
the two and silently ignores the other, so such a run would return twenty posts from one end of the
history while appearing to have asked for the span between the two bounds; a wrong answer that
looks exactly like a right one is worse than a rejection. A range is collected by walking one
direction and stopping at the bound.
What happens to a channel that is private, misspelled, or actually a group? It is skipped, and the run carries on with the rest of the list. Telegram serves the same join page, with HTTP 200, for a channel that does not exist, for a private channel, for a group and for a chat, so those four cases are one answer from outside and no amount of work on this side can separate them. The run logs a warning naming the channel, and a channel that produced no rows is not charged for. An entry that is not a Telegram name at all is dropped the same way, before any request: a name is 5 to 32 characters, starts with a letter, and then uses letters, digits or underscores.
Why is views_display text such as 1.56M rather than a number?
Because that is what the page states. 1.56M places the true figure somewhere between 1,555,000
and 1,565,000, and there is no exact view count anywhere a public preview can reach. Publishing
1560000 would invent three digits Telegram never gave, and nothing downstream could tell an
invented digit from a measured one, so the string is passed through as it arrived and the
_display suffix states the type before anyone parses it. It is null on posts too recent to have
a view counter yet.
Are reactions or the comment thread under a post available? No. Neither appears on the public preview page, so neither is reachable without an account, and I would rather say so here than ship a column that is null on every row. What the preview does carry is listed in full under What a run returns.
Why is text null on a post that clearly has a caption in the app?
text is null rather than an empty string when a media post carries no text node on the preview
page at all, which is the honest answer for a photo posted without a caption; an empty string would
claim the author published an empty message. Where a caption is rendered, it arrives in text with
its line breaks preserved.
Can channels be found by keyword or topic rather than named? No. Telegram publishes no public directory of channels and no list this Actor could walk, so the names have to come from somewhere else: a newsletter, a competitor's link list, a spreadsheet already in hand. This is a property of Telegram rather than a gap I intend to close later.
Why is forwarded_from_name not something this Actor accepts back as input?
Because the preview renders a forwarded post's source as a display title rather than as a handle,
and a channel's title and its username are set independently by its owner. The field reports what
the page states; it is not promised to be a name that resolves.
Related Actors
Telegram Channel Info Scraper returns a channel's title, subscriber count and header counters as a single row and saves no posts. It is the better first step for a long list of channels: it says how big each one is and whether anything has been posted since last time, so only the channels worth exporting are exported.