Telegram Channel Scraper - Posts & Views, No Login
Pricing
from $0.40 / 1,000 post rows
Telegram Channel Scraper - Posts & Views, No Login
Scrape posts from public Telegram channels without login or API keys: text, date, views, media, links, forwards, reactions and channel info. Search inside channels, filter by date and get only new posts on a schedule.
Pricing
from $0.40 / 1,000 post rows
Rating
0.0
(0)
Developer
Cliqto Media
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Scrape posts from public Telegram channels with no login, no phone number and no API keys, and get clean JSON rows with text, date, view count as a number, media links, reactions, forwards and channel info with the exact subscriber count.
Use it for news and crypto monitoring, research archives or AI pipelines: the smallest run is one channel name, such as durov, and two channels with 20 posts each finish in about 5 seconds for about $0.08 on the Free plan.

Table of contents
- What it does
- Quick start
- Input
- Output
- Pricing
- Use cases
- Scheduling and monitoring
- Switching from another Telegram scraper
- API, export and integrations
- Limits and partial results
- Troubleshooting
- FAQ
- Learn more
- Support
What it does
The Actor reads the public web preview of a Telegram channel (t.me/s/<channel>), the page that anyone can open in a browser without a Telegram account. For each channel you give, it saves the newest posts (or the posts that match your search), plus one row about the channel. It can stop at a date, search inside the channel with Telegram's own search, and on a schedule return only posts that are new since the last run. Every channel gets a clear status, so a wrong name is never mixed up with a quiet channel.
Good fit
- Follow what a list of public news, crypto or brand channels posted today.
- Build a dataset of posts with text, views, reactions, media and links.
- Find posts about a keyword or #hashtag inside many channels at once.
- Get subscriber counts and descriptions of many channels.
- Send only new posts to a spreadsheet, a chat or an AI agent every hour.
Not a fit / limits
- Only public channels that have a web preview. Private channels, invite links (
t.me/+...), groups, chats, users and bots are not supported. - No comments, no member or subscriber lists, no DMs, no global Telegram search: only posts of the channels you give.
- Telegram rounds view and reaction counts on the web (for example
830K). The Actor turns them into numbers (830000); it cannot make them more exact than Telegram shows. - Videos: the web preview usually gives only a preview image, not the video file. Photo links work, but all media links are temporary.
- One run reads about 1,800 posts in full; at about 4.5 minutes it stops and keeps what it has (see Limits).
Quick start
- Open the Input tab.
- Add one or more channels to Channels (
channels), for exampledurovandtelegram(this is the prefill). - Keep Max posts per channel (
maxPostsPerChannel) at 20, or set a higher number. - Click Start.
- Open the Output tab: the Posts view shows the post columns (channel, date, text, views, media); the Channels view shows the channel columns (status, title, subscribers). Both views list all rows; the Row type column tells posts and channels apart.
Expected result: 20 posts per channel, newest first, and one channel row each, in about 5 seconds.
Input
Input fields
| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect | Example | Recommendation |
|---|---|---|---|---|---|---|---|---|
channels | Channels | array of strings | yes | prefill ["durov", "telegram"] | 1–100 items: name, @name, https://t.me/name or https://t.me/s/name | which channels to read | ["bbcrussian", "@telegram"] | Repeats in any spelling are read once. Invalid values are skipped and counted in the run message; the rest still run. |
maxPostsPerChannel | Max posts per channel | integer | no | default 20, prefill 20 | 1–1000 | how many posts per channel, newest first | 200 | Each post row is charged; keep it small for frequent runs. |
since | Only posts since | string | no | empty (no date limit) | date 2026-10-01, date-time 2026-10-01T12:00:00Z, or 30 minutes, 24 hours, 7 days, 2 weeks, 1 month | skips older posts and stops reading the channel there | "7 days" | The best way to keep runs short and cheap. |
searchQuery | Search inside channels | string | no | empty | up to 200 characters; a word, phrase or #hashtag, any language | returns only posts that Telegram's channel search matches | "bitcoin" | Leave empty to get all posts. |
includeChannelInfo | Exact subscriber count | boolean | no | true | true / false | one extra request per channel for the exact number in subscribers | false | Turn off when the rounded subscribersText (10.4M) is enough. |
onlyNew | Only new posts since the last run | boolean | no | false | true / false | remembers the newest post per channel and next time returns only newer posts | true | For schedules; see Scheduling and monitoring. |
stateKey | Monitoring name | string | no | default | 1–40 letters, digits, - or _ | keeps separate memory for different schedules | "crypto-news" | Use one name per schedule. |
If no value in channels is a valid channel name, the run fails with Invalid input and only the start fee is charged.
Interacting settings
| When | Input A | Input B | Effective behavior | Example | Cost/result effect |
|---|---|---|---|---|---|
| Both set | maxPostsPerChannel | since | reading stops at whichever comes first | 200 and "1 day" | fewer posts and requests |
| Search and date | searchQuery | since | matches newer than since, newest first | "#news" and "7 days" | fewer posts |
| Monitoring | onlyNew | maxPostsPerChannel | at most this many new posts per run, oldest new post first; the rest come next run | true and 50 | no gaps between runs |
| Monitoring and search | onlyNew | searchQuery | memory is kept per channel and search; the newest matches come first | true and "ton" | set maxPostsPerChannel above the expected number of new matches |
| Monitoring and date | onlyNew | since | both apply; posts older than since are skipped | true and "1 day" | — |
Input examples
Latest posts of two channels
{"channels": ["durov", "telegram"],"maxPostsPerChannel": 20}
Expected behavior: about 4 requests, about 5 seconds.
Expected output: 40 post rows and 2 channel rows with subscribers.
Cost: $0.0841 on the Free plan.
Last 7 days of several channels, search for a word
{"channels": ["bbcrussian", "meduzalive", "nexta_live"],"since": "7 days","searchQuery": "Россия","maxPostsPerChannel": 100}
Expected behavior: only posts that Telegram's search matches, newer than 7 days.
Expected output: post rows with searchQuery set; channel rows with status.
Hourly monitoring
{"channels": ["durov", "telegram"],"maxPostsPerChannel": 50,"onlyNew": true,"stateKey": "telegram-news","includeChannelInfo": false}
Expected behavior: the first run returns the newest posts and remembers them; every next run returns only posts published after that.
Expected output: new post rows only; with nothing new, the run ends as NO_RESULTS with 2 channel rows.
Output
Results go to the default Dataset. Each row has recordType: post or channel. The dataset has two views, Posts and Channels. A view only picks columns, it does not hide rows: post rows and channel rows are in both views. To keep only posts, filter by recordType = post (for example in Excel, Google Sheets or your code). A run summary is saved as RUN_SUMMARY in the key-value store. The Output tab links all three.
Output example
{"recordType": "post","channel": "durov","channelTitle": "Pavel Durov","postId": 552,"url": "https://t.me/durov/552","date": "2026-10-11T14:52:40+00:00","edited": false,"text": "✨ Designers × AI\n\n🆕 The mockup era is over. …","author": "Pavel Durov","views": 830000,"viewsText": "830K","forwardedFrom": "Telegram Designers","forwardedFromUrl": "https://t.me/designers/266","replyToPostId": null,"replyToUrl": null,"mediaType": "video","mediaUrl": "https://cdn4.telesco.pe/file/…","media": [{"type": "video","url": null,"thumbnailUrl": "https://cdn4.telesco.pe/file/…","durationText": "1:15","title": null}],"linkPreview": null,"poll": null,"links": ["https://t.me/designers"],"hashtags": [],"reactions": [{ "emoji": "⭐", "paid": true, "count": 5280, "countText": "5.28K" },{ "emoji": "custom:5936157098181135162", "paid": false, "count": 10700, "countText": "10.7K" }],"searchQuery": null,"sourceUrl": "https://t.me/s/durov","fetchedAt": "2026-10-14T15:01:48.976Z"}
This post row is from the first input example; the text, media links and reactions are shortened. null means Telegram shows no value for this post (not a reply, no link preview, no poll). For this video the web preview has only a preview image, so media[0].url is null.
Post fields
| Field | Meaning | Type | Can be empty? | Example / enum | Notes |
|---|---|---|---|---|---|
recordType | row type | string | no | post | |
channel | channel username | string | no | durov | as in the channel link |
channelTitle | channel name | string | yes | Pavel Durov | |
postId | post number in the channel | integer | no | 552 | with channel, a stable key to remove duplicates |
url | link to the post | string | no | https://t.me/durov/552 | |
date | when it was posted | string | yes | 2026-10-11T14:52:40+00:00 | ISO 8601, UTC |
edited | the post was edited | boolean | no | false | |
text | post text | string | can be "" | ✨ Designers × AI… | line breaks kept; "" for media-only posts |
author | signature shown under the post | string | yes | Pavel Durov | only if the channel signs posts |
views | views as a number | integer | yes | 830000 | from viewsText; rounded by Telegram |
viewsText | views as Telegram shows them | string | yes | 830K | |
forwardedFrom, forwardedFromUrl | original channel or person of a forwarded post | string | yes | Telegram Designers | null if not forwarded |
replyToPostId, replyToUrl | the post this one replies to | integer, string | yes | null | |
mediaType | main media type | string | yes | photo, video, round_video, document, audio, voice, sticker, poll, location, album, unsupported | null for text-only posts |
mediaUrl | first media link | string | yes | https://cdn4.telesco.pe/file/… | photo image or video preview image; temporary |
media | all media: type, url, thumbnailUrl, durationText, title | array | can be [] | see example | url is null when the preview has no file link |
linkPreview | link card: url, siteName, title, description, imageUrl | object | yes | null if the post has no link card | |
poll | question, type, votersText, options[] (text, percent) | object | yes | null if no poll | |
links | links in the text | array | can be [] | ["https://t.me/designers"] | |
hashtags | hashtags | array | can be [] | ["#news"] | |
reactions | emoji, paid (Telegram Stars), count, countText | array | can be [] | see example | custom emoji appear as custom:<id> |
searchQuery | the search that found this post | string | yes | null | null when no search was used |
sourceUrl | page the post was read from | string | no | https://t.me/s/durov | |
fetchedAt | when the run read it | string | no | 2026-10-14T15:01:48.976Z | views and reactions are as of this time |
Channel fields
One channel row per valid channel, also when the channel is not found. If the run stops in the middle of a channel (time or request limit), that channel gets one more row with status: capped; this row is free.
| Field | Meaning | Type | Can be empty? | Example / enum |
|---|---|---|---|---|
recordType | row type | string | no | channel |
status | result for this channel | string | no | complete, not_found, error, capped |
statusCode | why, when not complete | string | yes | NOT_FOUND, NO_PUBLIC_PREVIEW, or an error code |
statusReason | plain explanation | string | yes | "telegram" exists on Telegram but has no public channel preview… |
channel, url | username and link | string | no | durov, https://t.me/durov |
title, description | channel name and description | string | yes | Pavel Durov, Founder of Telegram. |
verified | verified badge | boolean | yes | true |
avatarUrl | channel photo (temporary link) | string | yes | |
subscribers | exact subscriber count | integer | yes | 10386608 — null when includeChannelInfo is off |
subscribersText | subscribers as Telegram shows them | string | yes | 10.4M |
photos, videos, files, linksShared | media counters from the channel page | integer | yes | 102, 47, null, 200 |
postsReturned | posts saved for this channel | integer | yes | |
sourceUrl, fetchedAt | page and time | string | no |
Run summary (RUN_SUMMARY)
| Field | Meaning |
|---|---|
status | COMPLETE, PARTIAL (some channels not found or failed), NOT_FOUND (no channel found), NO_RESULTS (channels found, no posts matched), CAPPED (stopped at a limit, rows kept), SOURCE_ERROR, ERROR, INVALID_INPUT |
message | the same text as the run status message |
requested | the settings used |
counts | channels, postRows, channelRows, notFound, errors, chargedPostRows, chargedChannelRows, invalidChannels, unprocessedChannels |
channels[] | one entry per channel: channel, outcome (complete, no_posts, not_found, no_public_preview, error, capped, unprocessed), posts, pages, newestPostId, oldestPostId, watermarkBefore, watermarkAfter (monitoring), reason |
warnings, http, source, timing | warnings, request counters, data source note, start and finish time |
Output entities and surfaces
| Situation | Where | Result shape | User action |
|---|---|---|---|
| Normal | Dataset, Posts and Channels views | post rows + one channel row per channel | open, export or call the API |
| Channel does not exist | Channels view, RUN_SUMMARY | status: not_found, statusCode: NOT_FOUND | check the name |
| Group, user, bot or preview turned off | Channels view | not_found / NO_PUBLIC_PREVIEW | only public channels work |
| No posts in the period or no search match | RUN_SUMMARY | NO_RESULTS, channel rows complete, outcome no_posts | widen since or change the search |
| Stopped at a limit | Dataset + RUN_SUMMARY | rows kept, CAPPED, cut channel status: capped, unread channels unprocessed | split the list into smaller runs |
| Telegram not answering | RUN_SUMMARY, run FAILED | SOURCE_ERROR (rows kept) or ERROR | try again later |
Runs with COMPLETE, PARTIAL, NOT_FOUND, NO_RESULTS or CAPPED end as SUCCEEDED; SOURCE_ERROR, ERROR and INVALID_INPUT end as FAILED.
Pricing
Pricing unit
You pay per event. Prices per 1,000 events:
| Event | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|
| Post — one saved post row | $2.00 | $1.60 | $1.30 | $0.40 | $0.40 | $0.40 |
| Channel — one channel row (found or not found) | $2.00 | $1.80 | $1.50 | $1.00 | $1.00 | $1.00 |
Each run also has a start fee of $0.0001. Platform usage is included.
- A channel that Telegram answered about is charged once: found (
complete) or not found (not_found). A channel row withstatus: erroris free. - Invalid input values are not charged. A run with no valid channel pays only the start fee.
- A run with no matching posts, or a monitoring run with nothing new, pays only for the channel rows and the start fee.
- If you set a maximum cost per run, the Actor stops before it goes over it and keeps the rows already saved.
Cost examples (Free plan prices)
| Scenario | Input scope | Events | Charge | Time |
|---|---|---|---|---|
| Prefill | 2 channels × 20 posts | 2 channels + 40 posts | $0.0841 | about 5 s |
| Medium | 5 channels × 100 posts | 5 + 500 | $1.0101 | about 1 minute |
| Large | 10 channels × 180 posts | 10 + 1,800 | $3.6201 | about 3.5 minutes |
| Nothing new | 2 channels, onlyNew, no new posts | 2 channels | $0.0041 | a few seconds |
| Wrong names | 3 channels not found | 3 channels | $0.0061 | a few seconds |
On the Gold, Platinum and Diamond plans the prefill run costs $0.0181.
Use cases
| Buyer job | Input path | Output entity | Key fields | Next integration |
|---|---|---|---|---|
| Hourly news or crypto monitoring | channels + onlyNew | post | date, text, url, views | Schedule + webhook, Google Sheets, Slack |
| Post engagement research | channels + maxPostsPerChannel | post | views, reactions, forwardedFrom | CSV / Excel export |
| Keyword tracking | channels + searchQuery + since | post | text, date, searchQuery | API, spreadsheet |
| Channel directory | channels + includeChannelInfo | channel | subscribers, title, description, verified | API, spreadsheet |
| AI agent context | channels (small) | post | text, date, url | MCP |
Scheduling and monitoring
Create a schedule (for example every hour) with onlyNew: true and a stateKey such as telegram-news. The first run returns the newest posts and remembers the newest post of each channel. Every next run returns only posts published after it, oldest first, without gaps: if more posts arrived than maxPostsPerChannel, the rest come in the next run. The memory is kept in your account, in the key-value store telegram-channel-scraper-state; use a different stateKey for each schedule. A run with nothing new ends as NO_RESULTS and costs only the channel rows. Add a webhook or an integration to send new rows to Slack, a sheet or your own API when the run finishes.
Without onlyNew, use since: "1 day" and remove duplicates by channel + postId.
Switching from another Telegram scraper
Some Telegram scrapers return rows like username, fullName, followers and a message object, and give views as text. This Actor uses flat fields and numbers:
| Other scraper | This Actor |
|---|---|
username | channel |
fullName | channelTitle (post row), title (channel row) |
followers | subscribers (exact) and subscribersText |
message.fulldate | date (ISO 8601, UTC) |
message.views (1.16M as text) | views (1160000) and viewsText |
message.description | text |
message.link | url |
preview | linkPreview |
image, video | media[] with type, url, thumbnailUrl |
Channels that are not found get their own row with status: not_found and a reason, not an empty row.
API, export and integrations
- API: start a run with
POST https://api.apify.com/v2/acts/cliqtomedia~telegram-channel-scraper/runsand the input JSON; read rows from the run's Dataset and keep the rows with therecordTypeyou need (?view=postsor?view=channelsonly picks the columns), and read the summary from theRUN_SUMMARYrecord. - Export: JSON, CSV, Excel, XML or RSS from the Dataset.
- Webhooks / no-code: Apify integrations (Make, Zapier, Google Sheets, Slack) can send new rows when a run finishes.
Use with AI agents (MCP)
- MCP server with only this Actor:
https://mcp.apify.com?tools=cliqtomedia/telegram-channel-scraper. - Smallest useful input:
{"channels": ["durov", "telegram"], "maxPostsPerChannel": 20}. - Key fields:
text(what was posted),date(when),url(link to cite),channel(where),views(reach as a number). - Two channels with 20 posts take about 5 seconds and cost about $0.08 on the Free plan. One run reads up to about 1,800 posts; use
sinceorsearchQueryto keep answers short.
Limits and partial results
- Up to 100 channels per run and 1,000 posts per channel. Telegram's web preview gives about 20 posts per page, and the Actor reads about one page per second.
- Each run stops reading Telegram after about 4.5 minutes, whatever the run timeout is. That is enough for about 1,800 posts. If the time runs out, saved rows stay, the run is
CAPPED, the channel being read iscappedand the rest areunprocessed. Split large lists into several runs. - If you set a maximum cost per run, the Actor stops before going over it: rows already saved stay, the run is
SUCCEEDEDwithCAPPEDinRUN_SUMMARY. - View, reaction and subscriber texts are rounded by Telegram; only
subscriberswithincludeChannelInfois exact. - Media links (
telesco.pe) expire after some time. Download the files soon if you need them.
Troubleshooting
A channel returns no posts
Check its channel row (recordType = channel) in the Channels view: not_found / NOT_FOUND (wrong name), not_found / NO_PUBLIC_PREVIEW (a group, user, bot or a channel with the web preview turned off), or complete with outcome no_posts in RUN_SUMMARY (nothing matched since or searchQuery).
The run ends as CAPPED
Read RUN_SUMMARY.message: either the maximum cost you set was reached, or the run reached its 4.5-minute reading limit. Saved rows stay in the Dataset. Lower maxPostsPerChannel, use since, or split the channel list.
Some of my values were skipped as invalid
Only channel names (3–32 letters, digits or _, starting with a letter), @name and t.me links work. Invite links (t.me/+...) and other websites are skipped; the run message says how many.
The run fails with SOURCE_ERROR or ERROR
Telegram did not answer or asked the Actor to slow down several times. Rows saved before the error stay in the Dataset. Try again later.
FAQ
Do I need a Telegram account, phone number or API key?
No. The Actor reads only the public web preview that Telegram shows to anyone, without login.
Why are views like 830000 and not exact?
Telegram shows rounded counts on the web (830K). The Actor gives you the number (views) and the original text (viewsText). Exact view counts are not public.
Can I get comments, members or posts from private channels?
No. These are not in the public web preview.
Are the counts live?
Views, reactions and subscribers are as they were at fetchedAt. Run again to refresh them.
Is scraping Telegram allowed?
The Actor reads only public pages that Telegram shows without login, at a slow rate, and does not bypass any login or CAPTCHA. You are responsible for how you collect, store and reuse the content, including personal data and authors' rights, under Telegram's terms and the laws that apply to you.
Learn more
- Telegram channel web preview example: the page the Actor reads.
- Telegram Terms of Service.
- Apify pay-per-event pricing.
- Apify schedules.
Support
Use the Issues tab of this Actor for bugs, questions and feature requests. Include the run ID, the input and the error text. Never post tokens, cookies or private data.