Telegram Public Channel Scraper
Pricing
from $1.00 / 1,000 channel posts
Telegram Public Channel Scraper
Scrape posts from public Telegram channels: text, views, reactions, media, polls, forwards and replies. Monitor many channels with only-new-posts mode, date ranges, in-channel search and a webhook.
Pricing
from $1.00 / 1,000 channel posts
Rating
0.0
(0)
Developer
Juddroid
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Turn any public Telegram channel into clean rows of posts with views, reactions, media, polls, forwards and replies, and keep watching it with an only-new-posts mode that never charges you twice for the same post.
What does Telegram Public Channel Scraper do?
You give it a list of public channels (durov, @cointelegraph, t.me/s/bbcrussian or a post link). For each channel it reads Telegram's own public web preview at t.me/s/<channel>, the same page anyone can open in a browser without logging in, and pages back through the history newest first. Every post becomes one row with the full text, publish date, view count, the breakdown of emoji reactions, photos, videos, documents, audio, stickers, polls with percentages, link previews, links, hashtags, cashtags and mentions. Turn on Only new posts, schedule it, and each run returns just what was published since the last run, optionally pinging Slack or any webhook.
Why use it
- Monitoring built in, not bolted on. Incremental sync stores the last post ID per channel in a named key-value store. Scheduled runs return only new posts, and runs cut short by your spending limit resume where they stopped, without charging again for posts you already received.
- Full engagement metrics. Views parsed to numbers (
1.57M→1570000, raw value kept), every reaction with its count (standard emoji, custom emoji by ID, and paid Telegram Stars counted separately), poll voters and option percentages. - Many channels in one run. Paste hundreds of channels. They run in parallel, each channel's rows stay together, newest first, and every channel gets one free info row with the exact subscriber count (
10,360,902, not just10.4M). - Search inside channels, deeper than Telegram. Use Telegram's own in-channel search for keywords,
#hashtagsor$cashtags. Telegram's web search stops at about 20 results per channel; turn on Deep search and the Actor pages through the history and matches the text itself, so you get every matching post in your date range (82 matches for one query in a test where Telegram's search returned 22). - Date ranges that don't waste time.
dateFromstops paging as soon as older posts appear. For an olddateTo, the Actor jumps straight to that date with a binary search over post IDs (about 10–15 requests even on channels with 100,000 posts) instead of paging through everything newer. - Clear answers instead of empty runs. Channels that don't exist, have the web preview disabled, are invite-only, or are actually groups, bots or users get a free status row that says exactly why.
- Albums handled properly. A grouped album (several photos, videos or files sent together) is one post with all its media, not five half-empty rows.
- Fair pricing. $1 per 1,000 posts. Channel rows, status rows, service messages ("Channel created", pinned notices), filtered-out posts, already-seen posts and posts the web preview cannot render are free.
What data you get
One row per post (type: "post"), plus one free channel row per channel (type: "channel") and free status rows (type: "status") for channels without readable posts.
Post rows
| Field | Description |
|---|---|
channel, channelTitle | Channel username and display name |
postId, url | Post number in the channel and its https://t.me/<channel>/<id> link |
date | Publish time, ISO 8601 UTC |
text | Plain text with line breaks (emoji kept). textHtml adds the formatted HTML when enabled |
views, viewsRaw | View count as a number, and as Telegram shows it (41.2K) |
reactions[] | { emoji, type, count }. type is emoji, custom_emoji (with customEmojiId; emoji is custom:<id> when Telegram shows no fallback) or paid (Telegram Stars, ⭐) |
totalReactions, paidStars | Sum of all emoji reactions, and Stars sent as paid reactions |
isForwarded, forwardedFrom | { name, url, channel, postId } of the original source when Telegram shows it |
replyTo, replyToUrl | ID and link of the post this one replies to |
authorSignature | Admin signature, on channels that sign posts |
isEdited, viaBot | Edited flag, and the inline bot used to send the post |
media[], mediaTypes | Each item: type (photo, video, document, audio, voice, sticker, poll, link_preview, location), plus url, thumbnail, duration (seconds), fileName, fileSize, title/performer for audio, animated for GIF-style videos and animated stickers, and postId for album items |
albumPostIds | All post IDs that belong to a grouped album |
linkPreview | { url, siteName, title, description, imageUrl } |
links[], hashtags[], cashtags[], mentions[] | Extracted from the post text |
poll | { question, type, voters, options: [{ text, percent }] } |
contentUnavailable | true when the web preview shows only "open in Telegram" (stories, giveaways, paid media). Such rows keep date, views and reactions and are not charged |
isNew | true in only-new-posts mode |
searchQuery, input, scrapedAt | The search used (Telegram search or deep search), the input line, and the scrape time |
Channel rows: channel, title, description, subscribers (exact count from the channel's profile page), subscribersRaw, photosCount, videosCount, filesCount, linksCount (as shown in the channel header), avatarUrl, isVerified, isScam, isFake, lastPostId, lastPostDate, url, previewUrl.
Status rows: status is not_found, preview_disabled, private (invite links and t.me/c/...), not_a_channel (groups, bots, user accounts) or failed, with a human-readable message.
The run's key-value store also has a SUMMARY record with one entry per channel: posts pushed, newest and oldest post ID, pages fetched, why paging stopped (max_posts, date_from, last_seen_post, end_of_channel, charge_limit) and whether it was a baseline run.
How to use
- Paste channels into Channels, one per line:
durov,@durov,t.me/durov,https://t.me/s/durovor a post link liket.me/durov/123. - Set Max posts per channel. It caps both the result size and the cost per channel.
- Optionally set Posts from / Posts until (dates or relative values like
7 days) and a Search within each channel query. Turn on Deep search when you need more than Telegram's ~20 most recent matches per channel. - For monitoring, turn on Only new posts since the last run (see the recipe below).
- Click Start, then open the Posts view and export it as CSV, Excel or JSON.
{"channels": ["durov", "@cointelegraph", "https://t.me/s/WatcherGuru", "t.me/bbcrussian"],"maxPostsPerChannel": 100,"dateFrom": "2026-10-01","searchQuery": "","includeChannelInfo": true,"onlyNewPosts": false}
Output example
Real rows from a local test run (long text and CDN URLs trimmed):
{"type": "post","channel": "durov","channelTitle": "Pavel Durov","postId": 531,"url": "https://t.me/durov/531","date": "2026-07-16T20:45:25.000Z","text": "🩵 Telegram now has a Rich Text Editor.\n\nIt supports tables, headings, inline media, lists, AI content, formulas, and more.\n\n😈 Messaging apps will never be the same!","views": 5810000,"viewsRaw": "5.81M","reactions": [{ "emoji": "⭐", "type": "paid", "count": 6300 },{ "emoji": "custom:5265077361648368841", "type": "custom_emoji", "customEmojiId": "5265077361648368841", "count": 54600 },{ "emoji": "custom:5465587407350942612", "type": "custom_emoji", "customEmojiId": "5465587407350942612", "count": 16400 }],"totalReactions": 88790,"paidStars": 6300,"isForwarded": false,"forwardedFrom": null,"replyTo": null,"authorSignature": "Pavel Durov","isEdited": false,"mediaTypes": ["video"],"media": [{ "type": "video", "url": "https://cdn4.telesco.pe/file/b231b9b161.mp4?token=...", "thumbnail": "https://cdn4.telesco.pe/file/...", "duration": null, "animated": true }],"linkPreview": null,"links": [],"hashtags": [],"cashtags": [],"mentions": [],"poll": null,"input": "durov","scrapedAt": "2026-10-16T11:34:10.176Z"}
A poll post (Telegram X channel):
{"type": "post","channel": "tgx_android","postId": 311,"date": "2024-06-13T15:25:56.000Z","views": 360000,"totalReactions": 7070,"paidStars": 3,"mediaTypes": ["poll"],"poll": {"question": "Choose only one feature you wish to see in the next Telegram X update if nothing else would be added. Which one is it going to be?","type": "Anonymous Poll","voters": 48700,"options": [{ "text": "Bot Platform update / Support for new bots", "percent": 29 },{ "text": "Topics in group chats", "percent": 12 },{ "text": "Stories", "percent": 8 }]}}
A free channel row and a free status row:
{"type": "channel","channel": "cointelegraph","title": "Cointelegraph","subscribers": 330486,"subscribersRaw": "330K","photosCount": 20800,"videosCount": 1290,"filesCount": 4,"linksCount": 58400,"isVerified": false,"lastPostDate": "2026-10-16T09:23:50.000Z","url": "https://t.me/cointelegraph"}
{"type": "status","status": "not_a_channel","message": "@tdlibchat is a group, not a channel. Only public channels have a web preview with posts.","channel": "tdlibchat","title": "TDLib chat","input": "tdlibchat"}
Monitoring recipe: schedule + only new posts + webhook
- Turn on Only new posts since the last run. Keep the default State store name (
telegram-channel-state) or pick one per watchlist, for examplecrypto-watchlist. - Run it once. This is the baseline: it returns up to Max posts per channel recent posts per channel and remembers the newest post ID.
- In Apify Console, add a Schedule (for example every hour or every morning) with the same input.
- Optional: paste a Slack incoming-webhook URL, or a Zapier, Make or n8n URL, into Webhook URL for new posts. After every run with at least one new post, the Actor POSTs a JSON summary with a Slack-ready
textline per channel and up to 20 latest posts per channel (URL, date, views, text snippet). Runs without new posts don't call the webhook.
Each later run pages back only until it reaches the last post it already saw, so a quiet channel costs one request and nothing else. Posts outside your date range or search still advance the baseline. A search query keeps its own baseline, so you can watch durov and durov + #update side by side. If more than Max posts per channel new posts appeared between two runs, you get the newest ones and the baseline moves on; raise the limit or run more often for very busy channels. If a run stops at your spending limit, the posts it could not deliver are returned by the next run, and the ones it did deliver are not repeated.
Pricing
$0.001 per post row (event channel-post), which is $1.00 per 1,000 posts.
| Run | Posts returned | Cost |
|---|---|---|
| 10 channels × 100 latest posts | 1,000 | $1.00 |
| Research dataset: 5 channels × 2,000 posts | 10,000 | $10.00 |
Search $TON in 20 crypto channels, about 20 matches each | 400 | $0.40 |
| Hourly monitoring of 50 channels, about 8 new posts per channel per day | 400 per day | $0.40 per day (about $12 per month, after the baseline run) |
Not charged: channel info rows, status rows (not found, preview disabled, private, not a channel, failed), service messages, posts outside your date range or search, posts already returned in only-new-posts mode, posts the web preview cannot display (contentUnavailable), and the webhook. Max posts per channel caps the spend per channel. If you set a maximum cost per run, the Actor stops cleanly when it is reached and the next monitoring run continues from there.
Integrations / scheduling
- Schedules: hourly or daily runs with only-new-posts mode give you a channel feed for alerts, dashboards or archives.
- Webhooks: the built-in webhook posts new-post summaries to Slack, Zapier, Make or n8n. Apify's own run webhooks can trigger any downstream job when a run finishes.
- API: start runs and read results from any language with the Apify API or the JavaScript and Python clients. Use
?view=postsfor the compact table. - MCP and AI agents: call the Actor as a tool through the Apify MCP server, so an agent can answer "what did these 20 crypto channels say about ETF approvals this week?" with real posts and engagement numbers.
Use cases
- Crypto and trading signals: track announcement and alpha channels,
$cashtagsand reaction spikes within minutes of posting. - News monitoring: follow newsrooms and regional outlets that publish on Telegram first, in any language.
- Brand and product monitoring: search public channels for your brand, product or competitors and get alerted when they are mentioned.
- OSINT and research: build timestamped datasets of public channel activity with forward chains (
forwardedFrom) and reply threads for analysis. - Research datasets and AI: export clean text plus engagement for NLP, sentiment analysis or RAG pipelines.
FAQ & limitations
Which channels work? Public channels that have a username and a web preview at t.me/s/<name>. Private channels, invite-only links, groups, bots and user accounts are not readable this way and get a free status row. Some channels have the web preview turned off (by the owner or restricted by Telegram); they get a preview_disabled row with the title and subscriber count.
Are views and reactions exact? Telegram's web preview rounds large numbers (41.2K, 5.81M). The Actor converts them to numbers and also keeps the original text in viewsRaw. Channel subscriber counts are exact because they come from the channel's profile page.
What about custom emoji reactions? Telegram doesn't show the picture of premium custom emoji in the web preview, so they are reported by their stable customEmojiId. Standard emoji are returned as Unicode.
Forwarded posts. isForwarded is always reliable. The source name and link are returned when Telegram includes them in the preview; occasionally the preview leaves the source name blank, and then forwardedFrom fields are null.
Media URLs. Photo, video, sticker and audio links point to Telegram's CDN and are signed, temporary URLs. Download the files soon after the run if you need to keep them. Very large videos have only a thumbnail in the preview, and documents link to the post rather than the file.
How deep does search go? Telegram's web search returns only about the 20 most recent matching posts per channel and query. Deep search removes that limit by scanning the channel page by page (about 1 second per 20 posts, up to 10,000 posts per channel) and matching locally: every word or "quoted phrase" must appear in the text, link preview, poll or file name, case-insensitive. Set Posts from to bound the scan. Only matching posts are charged.
Comments. Discussion-group comments under channel posts are not part of the public web preview and are not collected.
Speed. About 20 posts per request. Each channel is paged at most once per second and requests to Telegram are spaced out, so 1,000 posts from one channel take about a minute, and many channels run in parallel.
Responsible use. The Actor reads only public channel content that Telegram itself publishes on the open web, without logging in. It does not collect members, subscribers' identities, private messages or data from private chats or groups. Respect Telegram's Terms of Service, the channel owners' rights and your local law (including the GDPR when posts mention people) when you store, analyze or republish posts, and link back to the original post URL when you show content to others.