Telegram Channel Scraper — Posts, Views, Media
Pricing
from $0.90 / 1,000 posts
Telegram Channel Scraper — Posts, Views, Media
Public Telegram channel posts with date, text, views, reactions, forwards, replies, media links and channel info. Date range, Arabic-safe keyword filter and a new-posts-only alert mode. No login or API key. Status per channel; channel rows are free.
Pricing
from $0.90 / 1,000 posts
Rating
0.0
(0)
Developer
Dottti
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Telegram Channel Scraper: Posts, Views, Media
Get posts from public Telegram channels in one run. Each post comes with its date, full text, views, reactions, links, hashtags, mentions, forward and reply details, and media links. Each channel also gets one free row with its title, description, exact subscriber count and photo.
The actor reads the public web preview of a channel (t.me/s/<channel>). This is the same page anyone can open in a browser. You need no Telegram account, no phone number and no API key.
Who uses it
- OSINT and research analysts who archive what public channels post, with dates and post links they can cite.
- News and crisis monitoring teams who follow Arabic and English news channels and filter by keyword.
- Crypto and market watchers who track announcement channels and want an alert when a new post appears.
- Marketers who compare views, reactions and posting rhythm across channels.
- AI and data pipelines that need clean post text in JSON, with Arabic and other right-to-left text kept exactly as posted.
What you can do
- Read one or many channels. Paste usernames (
durov),@namesor links (https://t.me/bbcarabic,https://t.me/s/durov). - Limit each channel to the newest N posts.
- Pick a date range. When the end date is in the past, the actor jumps straight to it with a few requests.
- Keep only posts with your keywords. You choose whether a post needs any keyword or all of them.
- Turn on alert mode. Later runs then return only posts published since the last run.
- Leave media links on or off. Media type and count are always included.
Input examples
The newest 50 posts from two channels:
{"channels": ["durov", "https://t.me/bbcarabic"],"maxPostsPerChannel": 50}
Arabic news about two topics in October 2026:
{"channels": ["ajanews", "alarabiya", "bbcarabic"],"keywords": ["غزة", "إسرائيل"],"keywordMode": "any","dateFrom": "2026-10-01","dateTo": "2026-10-31","maxPostsPerChannel": 500}
Alert mode for a schedule:
{"channels": ["insiderpaper", "ajanews"],"onlyNew": true,"stateName": "news-watch","maxPostsPerChannel": 100}
Output
Every row has a type field.
| type | One row is | Charged |
|---|---|---|
post | One channel post | Yes |
channel | One input channel: its info and its status | No |
Post row (real output, 2026-10-21)
{"type": "post","channel": "AjaNews","id": 517920,"url": "https://t.me/AjaNews/517920","date": "2026-10-21T14:15:31.000Z","text": "عاجل | المتحدث باسم الحكومة العراقية: ننفي وجود مقرات للحوثيين في #العراق ولن نسمح باستخدام أراضينا لتهديد دول الجوار","textLength": 117,"hashtags": ["#العراق"],"mentions": [],"links": [],"views": 1320,"viewsText": "1.32K","author": null,"edited": false,"forwardedFrom": null,"replyTo": null,"mediaType": "none","media": [],"mediaCount": 0,"linkPreview": null,"poll": null,"reactions": [],"isService": false}
A post with a photo and reactions (real output, some fields left out, media URL shortened):
{"type": "post","channel": "insiderpaper","id": 45415,"url": "https://t.me/insiderpaper/45415","date": "2026-10-21T13:42:13.000Z","text": "ALERT - Multiple websites worldwide could go dark as critical PHP flaws spark global cyber alert\n\nREAD: https://insiderpaper.com/critical-php-flaws-websites-down/","links": ["https://insiderpaper.com/critical-php-flaws-websites-down/"],"views": 2260,"viewsText": "2.26K","mediaType": "photo","media": [{ "type": "photo", "url": "https://cdn4.telesco.pe/file/jdJIaVWXKbJd4UNbfy5K5zmLkC4OJOg...", "postUrl": "https://t.me/insiderpaper/45415" }],"mediaCount": 1,"reactions": [{ "emoji": "🔥", "count": 22, "countText": "22" },{ "emoji": "🤡", "count": 13, "countText": "13" }]}
An album of 10 photos is one post row with mediaType: "album_photo" and 10 items in media. Each item has its own postUrl.
Paid star reactions look like {"emoji": "⭐", "paid": true, "count": 6000}. Custom emoji reactions have no character on the page, so they carry customEmojiId instead of emoji.
Channel row (real output, free)
{"type": "channel","input": "ajanews","status": "ok","username": "AjaNews","url": "https://t.me/AjaNews","title": "قناة الجزيرة","description": "الحساب الرسمي لقناة الجزيرة على تيليغرام","subscribers": 1308166,"subscribersExact": true,"subscribersText": "1.31M","photoUrl": "https://cdn4.telesco.pe/file/FIcDDmiGCKqhOIl8CqK42p4zQ4Hw4Zd...","verified": true,"kind": "channel","counters": { "photos": 508, "videos": 220, "files": 9, "links": 9490 },"postsDelivered": 100,"pagesFetched": 5,"stopReason": "max_posts","error": null,"charged": false}
Post fields
| Field | Meaning |
|---|---|
id, url | Post number in the channel and its public link |
date | Publish time, ISO 8601 in UTC |
text | Full post text. Line breaks are kept. Emoji are kept as characters. |
hashtags, mentions, links | Pulled from the text. Hashtags work in Arabic and other scripts. |
views, viewsText | Telegram rounds large counts on the page ("5.83M"). views is that number as an integer; viewsText is the original. |
author | The signature shown on the post, when the channel signs posts |
edited | True when the page marks the post as edited |
forwardedFrom | Name, username and post link of the source. hidden: true means Telegram shows a forward with no source name. |
replyTo | The post this one replies to: id, link, author and a short text preview |
mediaType | none, photo, video, gif, round_video, voice, audio, document, sticker, location, poll, link_preview, album_photo, album_video, album or unsupported |
media | One item per photo, video or file: URL, thumbnail, duration, file name and size when the page shows them |
linkPreview | Site name, title, description and image of a shared link |
poll | Question, options and percentages, when the page shows a poll |
reactions | Each reaction with its count |
isService | Notes like "Channel photo updated". These are skipped unless you turn them on. |
Channel status values
A bad channel never stops the run. Every input gets one free channel row with a status:
ok: posts were read.not_found: Telegram has no public chat with this username.private: the input is an invite link (t.me/+...). Private chats need a login, so the actor does not read them.no_preview: the chat exists, but Telegram shows no web preview for it. Public groups are in this case.not_channel: the username belongs to a user or a bot.invalid_input: the text is not a Telegram username or link.blocked: Telegram refused every retry and every proxy tier.cut_by_cost_limitorskipped_cost_limit: your maximum cost for the run was reached.
stopReason says why a channel ended: max_posts, reached_from_date, reached_last_seen, end_of_channel, max_pages or cost_limit.
The RUN_SUMMARY record in the key-value store lists every channel with its status, pages read and timing. It also lists the requests made, status codes, retries, the proxy tier used and the bytes downloaded per 1,000 posts.
Keyword filter and Arabic text
Keywords are case-insensitive. The filter searches the post text, the link preview title and description, poll text and file names.
For Arabic, the filter ignores diacritics (harakat) and tatweel. It treats these letters as the same: أ إ آ ا, ى ي, ة ه, ؤ و, ئ ي. Arabic-Indic digits match Western digits. So افريقيا finds أفريقيا, and العربية finds الْعَرَبِيَّةُ.
The output text is never changed. Normalizing is only used to compare.
Posts that fail the filter are skipped and free. A rare keyword can mean many pages are read for few matches. maxPagesPerChannel (default 50 pages, about 1,000 posts) caps that scan.
Dates
dateFromanddateTotakeYYYY-MM-DD(UTC), a full ISO time, or a relative value such as7 daysor12 hours.- A date-only
dateToincludes that whole UTC day. - Newest posts come first. The walk stops at the first post older than
dateFrom.
Alerts on a schedule
- Turn on
onlyNewand give the list astateName. - The first run returns the newest posts as a baseline and saves the last post id per channel.
- Each later run returns only posts newer than the saved id. These rows have
isNew: true. Their channel row hasalert: "new_posts". - New posts come oldest first, so if a cost limit cuts the run, the next run picks up right after the last delivered post.
- If more than
maxPostsPerChannelposts arrived between two runs, only the newest that many are returned.
State lives in the named key-value store telegram-channels-state, under STATE-<stateName>. Use different names for different lists.
To schedule it, open the actor in Apify Console, save your input as a task, then add a schedule to the task. Every 15 minutes or every hour works for news and announcement channels. When nothing is new, the run outputs only the free channel rows and you pay nothing for posts.
Pricing
You pay per post row: $0.90 per 1,000 posts ($0.0009 per post).
- Channel info rows are free.
- Status rows for channels that return nothing are free.
- Posts removed by your keyword or date filters are free.
- Set a maximum cost per run in Apify. The actor stops cleanly at that limit and marks the channels it could not finish.
Platform usage is included in the price.
Speed and limits
Measured on 2026-10-21 from a home connection, no proxy, 256 MB memory:
| Run | Posts | Requests | Time | Downloaded |
|---|---|---|---|---|
| Empty input (default channel) | 20 | 2 | 1.2 s | 25 KB |
| English, 200 posts | 200 | 13 | 8 s | 269 KB |
| Arabic news, 200 posts | 200 | 11 | 6.5 s | 94 KB |
| Media-heavy, 200 posts | 200 | 13 | 7.7 s | 149 KB |
| News channel, 975 posts | 975 | 51 | 30.6 s | 794 KB |
| 5 channels mixed, 100 each | 400 | 30 | 17.5 s | 384 KB |
| Date range in 2020, 2 channels | 48 | 16 | 11.4 s | 321 KB |
- One preview page holds about 20 posts. The actor waits about 0.6 seconds between requests across all channels, to stay polite. Expect about 30 seconds per 1,000 posts.
- Telegram rounds view, reaction and subscriber counts on the preview page. The exact subscriber count comes from one extra small request per channel. Turn off
exactChannelInfoto skip it. - Media URLs point to Telegram's CDN. They are signed and expire after some hours. Download what you need soon after the run.
- Some media shows only "Please open Telegram to view this post" on the web. The post row is still output, with
mediaType: "unsupported"or anoteon the media item. - Some channels deleted most of their history. The preview then ends early and
stopReasonisend_of_channel. - The public preview leaves out comments under posts, member lists and private chats. The actor collects none of these.
- Proxy: requests start with no proxy. If Telegram keeps answering 429 or errors, the actor moves to Apify datacenter proxy, then residential. In our tests no proxy was needed.
Privacy
The actor reads only what the public web preview shows to any visitor. It never logs in, joins chats, reads member lists or looks up phone numbers. You are responsible for using the data in line with the laws that apply to you, such as GDPR or Saudi PDPL when posts name people.
Input reference
| Field | Default | What it does |
|---|---|---|
channels | ["telegram"] | Usernames or links |
maxPostsPerChannel | 20 | Posts kept per channel, after filters |
dateFrom, dateTo | empty | Date window |
keywords | empty | Keep only posts with these words |
keywordMode | any | any or all |
includeMedia | true | Add media URLs |
includeServiceMessages | false | Keep notes like "Channel photo updated" |
maxPagesPerChannel | 50 | Scan cap when filters are on (about 20 posts per page) |
exactChannelInfo | true | One extra request for the exact subscriber count |
onlyNew | false | Alert mode |
stateName | default | Separate memory per alert list |
proxyConfiguration | off | Your own proxy; turns the fallback off |
autoProxyFallback | true | No proxy, then datacenter, then residential |
allowResidentialFallback | true | Allow the residential step |
maxConcurrency | 2 | Channels read in parallel |