Telegram Channel Scraper: Messages, Views, Media
Pricing
Pay per event
Telegram Channel Scraper: Messages, Views, Media
Telegram channel scraper: messages, views, reactions, photos, videos, forwards, replies, polls and channel info from public channels. No login, no API key, no phone number. $0.50 per 1,000 messages.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Data Gleaner
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Telegram Channel Scraper: messages, views, reactions and media, no login
Scrape public Telegram channels without a Telegram account: every message with date, text, views, reactions, photos and videos, forwards, replies, polls and links, plus one info row per channel. Export to JSON, CSV or Excel. No phone number, no API key, no browser.
Price: $0.50 per 1,000 messages ($0.0005 per message), and $0.001 per channel info row. 100 messages cost 5 cents.
Why use this one
- No login, so nothing to get logged out of. It reads the public web preview Telegram serves at
t.me/s/<channel>; no session of yours is used. - Dates that stop the run.
onlyNewerThan(a date or "7 days") ends paging when messages get older, so you do not pay for history you did not ask for.searchQueryuses Telegram's own in-channel search. - Failures are not billed. A channel that is private, missing or has its preview off is reported in the log and skipped, with no charge. Rate limits are waited out with backoff.
- Rich rows. Reactions with counts, forwarded-from, reply-to, media list, polls, link previews, hashtags and mentions.
- Honest limits: public channels with the web preview only. No groups, no member lists, no comments from linked discussion groups; counts are rounded by Telegram.
Minimal input
{"channels": ["durov", "telegram"], "maxMessagesPerChannel": 100, "onlyNewerThan": "7 days"}
Sample output row (real platform run, long text shortened)
{"type": "message","messageId": 551,"channel": "durov","channelTitle": "Pavel Durov","channelSubscribers": 10400000,"url": "https://t.me/durov/551","date": "2026-10-10T14:22:43+00:00","isEdited": false,"text": "💸 If your wallet doesn’t look like this, don’t even bother sending me money for my birthday! 🎂","textHtml": "<tg-emoji emoji-id=\"5447458260200214425\"><i class=\"emoji\" style=\"background-image:url('//telegram.org/img/emoj...","views": 689000,"reactions": [{"emoji": "⭐","count": 56800,"paid": true},{"emoji": null,"count": 27100,"customEmojiId": "5936157098181135162"},{"emoji": null,"count": 9460,"customEmojiId": "5373223594484587136"}],"forwardedFrom": null,"replyTo": null,"author": "Pavel Durov","media": [{"thumbnailUrl": "https://cdn4.telesco.pe/file/c0ZSQM3RKhQYvFGHRk_MEGdS8C0SZ8Sg5x92lzag1BuxjccjWyYLXjqRMMgGhOy2woVtla0GI-n2BBd75...","duration": null,"fileName": null,"fileSize": null,"type": "video","url": "https://cdn4.telesco.pe/file/f4b10a0c75.mp4?token=fCbX8zIif7OOvVrEXrP2J28QYGwMkltCkPtc4hqOaW9JNyTv0nUoJancuQ3o..."}],"poll": null,"linkPreview": null,"links": [],"hashtags": [],"mentions": [],"serviceMessage": false,"scrapedAt": "2026-10-11T06:37:27+00:00","input": "durov"}
What does Telegram Channel Scraper do?
It pages through a channel from the newest message backwards (about 20 messages per request) and returns each message as one row in the Apify dataset, exportable as JSON, CSV or Excel or readable through the API. Typical uses:
- News and media monitoring. Track what outlets, governments, brands or crypto projects post, and how many views each post gets.
- Brand and competitor tracking. Watch the channels of competitors, with views and reactions as an engagement signal.
- Crypto and market research. Collect announcement channels and filter by hashtag, link or mention.
- OSINT and journalism. Archive posts with their date, links, forwarded-from source and media URLs before they are edited or deleted.
- Datasets for AI and RAG. Get clean message text (and the original HTML) in many languages for analysis, summarising or training pipelines.
- Recurring feeds. Use
onlyNewerThanto pull only the last day or week on a schedule.
Input
| Field | Type | What it does |
|---|---|---|
channels | list of text | Channel usernames, @usernames or t.me links (https://t.me/durov, https://t.me/s/durov). Prefilled with durov, telegram. |
searchQuery | text | Optional. Return only messages containing this word or phrase, using Telegram's own in-channel search. Applies to every channel in the list; paging, limits and dates work as usual. |
maxMessagesPerChannel | number | Cap per channel, newest first. Default 100. It is also your message cost cap per channel. Set 0 for profile only: one channel row per channel and no messages. |
onlyNewerThan | text | Optional. Stop paging back when messages are older. A date (2026-10-01, 2026-10-01T12:00:00Z) or relative time (7 days, 12 hours, 2 weeks). |
afterMessageId | number | Optional. Return only messages with an ID greater than this and stop paging once it is reached. With several channels the same number applies to each. |
includeChannelInfo | true/false | Also return one row per channel. Default true. Message rows carry channelSubscribers either way. |
requestDelaySeconds | number | Pause between page requests, default 1.0 s (randomised up to 1.4x). Raise it if Telegram rate-limits you. |
proxyConfiguration | object | Optional Apify Proxy. Default is no proxy. |
{"channels": ["durov", "@nexta_live", "https://t.me/s/bbcrussian"],"maxMessagesPerChannel": 300,"onlyNewerThan": "7 days","includeChannelInfo": true}
Messages are deduplicated per channel and returned newest first. An empty input runs a built-in example: 5 messages from durov, no channel row.
Recipes
Profile only (subscriber count, counters, description). Set maxMessagesPerChannel to 0. You get one channel row per channel and no message rows.
{"channels": ["durov", "telegram"], "maxMessagesPerChannel": 0}
Search inside channels. Set searchQuery to a keyword to get only the messages that contain it.
Scheduled monitoring. Each run saves a SUMMARY record in its key-value store with newestMessageId per channel. Pass that number as afterMessageId on the next run and you pay only for messages posted since. A channel with no new messages has no newestMessageId in that run's SUMMARY, so keep the previous number. For several channels, run one input per channel, because the same afterMessageId applies to all channels in a run. Alternatively use onlyNewerThan (for example 1 day) on a schedule.
Output
Two row types share the dataset, told apart by type.
Message row (type: "message")
| Field | Meaning |
|---|---|
messageId, channel, channelTitle, url | Message number, channel username and title, link https://t.me/<channel>/<id>. |
date | Publication time, ISO 8601 UTC. |
isEdited | true if Telegram marks the message as edited (the preview does not show when). |
text, textHtml | Plain text with line breaks, and the original HTML (bold, links, emoji). text strips leading indentation inside code blocks; textHtml keeps it. |
channelSubscribers | Subscriber count of the channel at scrape time, repeated on every message row. |
views | Number (Telegram's 1.2K and 3.4M are converted). Empty for service messages. |
reactions | List of {emoji, count}. Custom emoji carry customEmojiId; paid star reactions carry paid: true. |
forwardedFrom | {name, url, author} when the message is a forward. |
replyTo | {messageId, url, author, text} with a snippet of the replied-to message. |
author | Post signature when the channel shows one. |
media | List of {type, url, thumbnailUrl, duration, fileName, fileSize} with type photo, video, voice, sticker or document. Albums give several entries. |
poll | {question, type, options: [{text, percent}]} for polls. |
linkPreview | {url, siteName, title, description, imageUrl} for the link card under a message. |
links, hashtags, mentions | Every link in the text, hashtags (#Bitcoin), and @mentions without the @. |
serviceMessage | true for system messages such as "Channel created". |
scrapedAt, input | ISO 8601 time of the scrape and the input value the row came from. |
{"type": "message","messageId": 124665,"channel": "nexta_live","channelTitle": "NEXTA Live","url": "https://t.me/nexta_live/124665","date": "2026-10-09T09:28:30+00:00","isEdited": true,"text": "«Рыбарь» ещё вчера наслаждался черногорским дождиком, а спецслужбы уже во всю готовили ему сюрприз ...","textHtml": "<b>«Рыбарь» ещё вчера наслаждался черногорским дождиком ...</b>","channelSubscribers": 772000,"views": 24500,"reactions": [{"emoji": "👍", "count": 482}, {"emoji": "🔥", "count": 173}],"forwardedFrom": null,"replyTo": {"messageId": 124662, "url": "https://t.me/nexta_live/124662", "author": "NEXTA Live", "text": "🎣 «Рыбарь» попался ..."},"author": null,"media": [{"type": "video", "url": null, "thumbnailUrl": "https://cdn4.telesco.pe/file/...", "duration": 37, "fileName": null, "fileSize": null}],"poll": null,"linkPreview": null,"links": [],"hashtags": [],"mentions": [],"serviceMessage": false,"scrapedAt": "2026-10-09T09:49:31+00:00","input": "nexta_live"}
Channel row (type: "channel")
{"type": "channel","url": "https://t.me/nexta_live","title": "NEXTA Live","username": "nexta_live","description": "Медиа №1 из Беларуси ...","subscribers": 772000,"photoCount": 69400,"videoCount": 43100,"fileCount": 3,"linkCount": 8570,"avatarUrl": "https://cdn4.telesco.pe/file/...","verified": true,"scrapedAt": "2026-10-09T09:49:31+00:00","input": "nexta_live"}
Telegram shows large numbers rounded (for example 772K subscribers), so counts are as precise as Telegram's page. Avatar and media URLs are signed Telegram CDN links that expire, so download them soon. Videos that Telegram's preview does not embed (large files) come back with url: null and a thumbnail.
Pricing
Pay per event, with the prices final:
- $0.0005 per message ($0.50 per 1,000 messages), from 21 October 2026
- $0.001 per channel info row
You pay only for rows you receive: nothing for channels that are unavailable, failed requests or retries, and the run stops by itself when your maximum total charge is reached. Platform usage is included.
Example: 10 channels with 500 messages each and channel info on is 5,000 messages and 10 channel rows, so 5,000 x $0.0005 + 10 x $0.001 = $2.51. A daily run of 1 channel with onlyNewerThan: "1 day" and about 30 posts costs about $0.015 per day.
Use it from the API
curl -X POST "https://api.apify.com/v2/acts/datagleaner~telegram-channel-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"channels": ["durov"], "maxMessagesPerChannel": 50}'
It also works from n8n, Make or Zapier through the Apify integrations.
Use with Python
Install the client with pip install apify-client and set APIFY_TOKEN. This run returns at most 10 messages and 1 channel row, so it costs at most $0.006.
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("datagleaner/telegram-channel-scraper").call(run_input={"channels": ["durov"],"maxMessagesPerChannel": 10,})for item in client.dataset(run.default_dataset_id).iterate_items():if item["type"] == "message":print(item["date"], item["views"], (item["text"] or "")[:80])
Use with JavaScript / Node.js
Install the client with npm install apify-client and set APIFY_TOKEN. Save as run.mjs and run node run.mjs.
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('datagleaner/telegram-channel-scraper').call({channels: ['durov'],maxMessagesPerChannel: 10,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const item of items) {if (item.type === 'message') console.log(item.date, item.views, (item.text || '').slice(0, 80));}
Use it from n8n, Make, Zapier or an AI agent
Actor ID: datagleaner/telegram-channel-scraper
Minimal input:
{"channels": ["durov"], "maxMessagesPerChannel": 10}
Each tool below runs this Actor with your own Apify API token.
-
n8n: add the Apify node (
@apify/n8n-nodes-apify). On n8n Cloud you install it from the community node registry. Choose Run an Actor and get dataset, set Actor todatagleaner/telegram-channel-scraperand paste the input above. -
Make: use the Apify app's Run an Actor module, then Get Dataset Items to read the results. Watch Actor Runs can trigger a scenario when a run finishes.
-
Zapier: use the Apify action Run Actor, then the search Fetch dataset items. The trigger Finished Actor run starts a Zap when a run ends.
-
AI agents (MCP): connect to
https://mcp.apify.com?tools=datagleaner/telegram-channel-scraper. In Claude Code:claude mcp add --transport http apify "https://mcp.apify.com?tools=datagleaner/telegram-channel-scraper"Then run
/mcpto sign in to Apify in your browser. To use a token instead, add--header "Authorization: Bearer YOUR_APIFY_TOKEN". Then ask in plain words, for example:Use datagleaner/telegram-channel-scraper to get the last 7 days of posts from the Telegram channels durov and telegram, then list the 5 posts with the most views and summarise each in one line.
-
LangChain (Python):
# pip install langchain-apify, then set APIFY_TOKEN in your environmentimport jsonfrom langchain_apify import ApifyActorsTooltool = ApifyActorsTool("datagleaner/telegram-channel-scraper")result = tool.invoke({"run_input": json.loads('{"channels": ["durov"], "maxMessagesPerChannel": 10}')})
Limits
- Public channels with the web preview enabled only. If
t.me/s/<channel>redirects to the plaint.me/<channel>page, the channel has no preview (or does not exist) and is skipped with a warning. Private channels, groups, invite links (t.me/+...,joinchat) and bots are not supported. - No member lists, no comments, no private data. The Actor returns what any visitor of the preview sees: posts, counters and media links. It cannot list subscribers or read replies posted in a linked discussion group.
- Documents and some media are not shown by Telegram's preview. Messages carrying only such media come back with their text, date and views but may have an empty
medialist; the unsupported media notice is not exposed as data. - Counts are rounded by Telegram (
1.2K), and channels can hide view counts. - History depth is whatever Telegram serves by paging back; very long histories take many requests (about 20 messages per request at 1 second each).
- Telegram rate limits are handled with backoff. If a channel keeps answering 429, raise
requestDelaySecondsor enableproxyConfiguration.
FAQ
Do I need a Telegram account, API key or phone number? No. The Actor reads the public web preview page and needs no login of any kind.
Can it scrape Telegram channel members or subscribers? No. Telegram's public preview does not list members. The channel row gives the subscriber count, rounded as Telegram shows it.
Can it scrape private channels or groups? No. Only public channels whose web preview is on. A channel that does not exist, is private or has its preview off is skipped, and the log and the SUMMARY record name it with the reason.
Can I scrape only new posts on a schedule? Yes: set onlyNewerThan (for example 1 day), or pass the newestMessageId from the last run's SUMMARY as afterMessageId, and schedule the Actor.
Can I download the photos and videos, or export to CSV? The rows hold Telegram CDN URLs (media[].url, thumbnailUrl) that you download with your own tooling; the links are signed and expire. The dataset exports as JSON, CSV or Excel from the run page or the API.
Responsible use
This Actor reads only publicly visible channel previews and paces its requests politely. Messages can contain personal data (names, usernames, phone numbers in text): you are responsible for a lawful basis, for respecting Telegram's terms and channel owners' rights, and for your obligations under GDPR and any other law that applies. Do not use the output to harass, profile private individuals or evade platform rules. This is not legal advice.