Telegram Channel Scraper avatar

Telegram Channel Scraper

Pricing

from $1.00 / 1,000 message scrapeds

Go to Apify Store
Telegram Channel Scraper

Telegram Channel Scraper

Give it a public Telegram channel and get its profile plus messages: text, exact publish time, views, reactions, forwards, replies and media links. Subscriber counts are exact, not the rounded 11M the preview page shows. Channels with no web preview return the reason, not an empty file.

Pricing

from $1.00 / 1,000 message scrapeds

Rating

0.0

(0)

Developer

Mehmet

Mehmet

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Categories

Share

Export messages and channel profiles from public Telegram channels over plain HTTP.

No login. No API key. No phone number. No browser — which is why it is fast and cheap.

Give it a channel username and you get the channel profile plus its messages: text, exact publish time, view counts, reactions, forwards, replies, media links, link previews, hashtags, mentions and e-mail addresses found in the text.


What makes this one different

Three things this Actor does that the alternatives do not.

1. Subscriber counts are exact, not rounded

Telegram's public preview page shows 11M subscribers. Several scrapers parse that rounded label and hand you an integer, which looks precise but is not.

This Actor reads the channel profile page as well and returns the real number:

{ "subscriberCount": 11030508, "subscriberCountIsApproximate": false }

If the exact number cannot be read, the flag flips to true instead of quietly pretending.

2. View counts tell you when they are approximate

Telegram rounds view counts on the preview page (2.78M). You get all three pieces, so you can compute with the number and know how precise it is:

{ "viewCount": 2780000, "viewCountRaw": "2.78M", "viewCountIsApproximate": true }

Small counts arrive exact and the flag is false.

3. Channels without a web preview return a reason, not an empty file

About 14% of public channels turn their web preview off, and Telegram groups never had one. Most scrapers return an empty dataset and leave you guessing whether the channel is dead, private, misspelled — or whether the scraper broke.

This Actor always emits the channel record with the reason:

InputentityKindpreviewAvailableunavailableReasonsubscriberCount
durovchanneltruenull11030508
coindeskchannelfalsepreview_disabled11442
pythongroupfalseis_group97001
nasanot_foundfalsenot_foundnull

Note the second row: the channel's messages are unavailable, but you still get its subscriber count.


Input

{
"channels": ["durov", "@binance_announcements", "https://t.me/s/telegram"],
"maxMessagesPerChannel": 100
}

Channel names are accepted in every common form: durov, @durov, t.me/durov, https://t.me/s/durov, or a link to a single message.

OptionDefaultWhat it does
channels["durov"]Channel usernames or t.me links.
maxMessagesPerChannel100Messages per channel. Telegram serves 20 per page request.
searchQueryReturn only messages matching this keyword inside the given channels.
sinceDateISO date. Pagination stops once older messages are reached.
afterMessageIdManual watermark: only messages with a higher ID.
incrementalfalseRemember the last message ID per channel; the next run returns only what is new.
includeChannelInfotrueEmit one channel record per channel.
includeMediatrueReturn media URLs. Files are never downloaded.
extractEmailstrueFind e-mail addresses written in the message text.
verifyEmailstrueMX lookup per address, with provider, role, free and disposable flags.
maxRequestsPerChannel60Safety cap on page requests for a single channel.

Output

One dataset with two record types, told apart by recordType.

Channel record

{
"recordType": "channel",
"channelUsername": "durov",
"channelUrl": "https://t.me/durov",
"channelTitle": "Pavel Durov",
"channelDescription": "Founder of Telegram.",
"channelPhotoUrl": "https://cdn4.telesco.pe/file/...jpg",
"channelVerified": true,
"subscriberCount": 11030508,
"subscriberCountIsApproximate": false,
"previewAvailable": true,
"unavailableReason": null,
"entityKind": "channel",
"messagesScraped": 100,
"scrapedAt": "2026-08-27T18:00:00.000Z"
}

Message record

Channel identity is repeated on every message row, so a spreadsheet export works on its own.

{
"recordType": "message",
"channelUsername": "durov",
"channelTitle": "Pavel Durov",
"subscriberCount": 11030508,
"messageId": 542,
"messageUrl": "https://t.me/durov/542",
"postedAt": "2026-08-15T13:29:20+00:00",
"text": "The International Olympiad in Informatics ends today...",
"viewCount": 2780000,
"viewCountRaw": "2.78M",
"viewCountIsApproximate": true,
"authorSignature": null,
"isEdited": false,
"isForwarded": false,
"forwardedFrom": null,
"forwardedFromUrl": null,
"isReply": false,
"replyToMessageId": null,
"reactions": [
{ "kind": "paid", "emoji": null, "emojiId": null, "count": 5460, "countRaw": "5.46K", "countIsApproximate": true },
{ "kind": "custom", "emoji": null, "emojiId": "5465587407350942612", "count": 38500, "countRaw": "38.5K", "countIsApproximate": true }
],
"reactionCount": 78700,
"links": ["https://stats.ioinformatics.org/results/2026"],
"hashtags": [],
"mentions": [],
"linkPreview": null,
"media": [],
"hasPhoto": false,
"hasVideo": false,
"hasVoice": false,
"emails": [],
"contentHash": "38bcfd6b9dc4d22b...",
"sourceUrl": "https://t.me/s/durov",
"scrapedAt": "2026-08-27T18:00:00.000Z"
}

Messages arrive oldest first, so an incremental feed reads in chronological order.

A field that is not on the page comes back as null — never invented. A channel with no reactions returns reactions: null, not an empty list, because "none" and "zero" are different answers.

Reactions

kind tells you what the reaction actually is:

  • standard — a plain emoji; the character is in emoji
  • custom — a custom emoji; Telegram serves only an ID, so emoji is null and emojiId is set
  • paid — a Telegram Stars reaction, which carries no emoji at all

Counts follow the same honesty rule as views: count, countRaw and countIsApproximate.

E-mail addresses

Every address found in the message text is checked against DNS and returned with what is known about it:

{
"address": "team@acme-labs.io",
"status": "deliverable_domain",
"mxProvider": "Google Workspace",
"isFreeProvider": false,
"isRoleAccount": false,
"isDisposable": false,
"mxChecked": true,
"smtpChecked": false,
"mailboxVerified": null
}

Mailbox existence is never claimed. smtpChecked is always false and mailboxVerified is always null, because the SMTP handshake that would prove it is not possible from this platform. The strongest honest answer is deliverable_domain: the domain accepts mail, the individual mailbox is unknown.


Incremental mode

Turn on incremental and the Actor remembers the highest message ID it delivered for each channel. The next run returns only newer messages.

The watermark advances only past messages that were actually delivered, and it is written per channel as soon as that channel finishes. If a run stops halfway, nothing is lost and nothing is billed twice.

Pair it with a schedule to keep a channel mirrored with one small run per hour.


Limits, stated plainly

  • Public channels only. Private channels, groups and direct messages are out of reach without an account, and this Actor never asks for one.
  • About 14% of public channels disable their web preview. Those return a channel record with previewAvailable: false and no messages. There is no way around it without an account.
  • Comments and discussion threads are not included.
  • Cross-channel keyword search is not supported. Telegram has no public endpoint for it. searchQuery searches inside the channels you name.
  • Media files are not downloaded. You get URLs; fetch what you need yourself.
  • Polls and documents are not parsed. They were not found in any channel sampled while building this, and untested extraction is not shipped.
  • View and reaction counts are rounded by Telegram above a few thousand. The *IsApproximate flags tell you exactly when.

Frequently asked questions

Is this Actor affiliated with Telegram? No. It reads the same public pages your browser gets at t.me/<channel>, with no account and no API key.

Why did a channel return a profile but no messages? About 14% of public channels turn their web preview off, and Telegram groups never had one. Those return previewAvailable: false with the reason — and the subscriber count is still filled in, which is the part most scrapers drop.

Can it read private channels or groups? No. There is no login and no account, so anything behind Telegram's sign-in wall is out of reach by design.

Why is the subscriber count sometimes marked approximate? The message page shows a rounded figure (11M). This Actor fetches the profile page for the exact number (11 031 634) and only falls back to the rounded one when the profile cannot be read — and then it says so with subscriberCountIsApproximate: true instead of passing a rounded number off as exact.

Is this legal? It reads public pages only, with no login. t.me publishes no robots.txt, and no private data is accessed.

How it works

Three public pages, nothing else:

PageWhat it gives
t.me/<channel>exact subscriber count, title, description, photo, verified badge
t.me/s/<channel>20 messages per request
t.me/s/<channel>?before=<id>the previous 20 messages
t.me/s/<channel>?q=<keyword>in-channel search, also paginated

No headless browser, no proxy, no account. That is where the low run cost comes from.