Telegram Channel Scraper — Posts, Views & Media avatar

Telegram Channel Scraper — Posts, Views & Media

Pricing

from $0.85 / 1,000 posts

Go to Apify Store
Telegram Channel Scraper — Posts, Views & Media

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

The Netaji

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

14 days ago

Last modified

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.

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.