Telegram Channel Scraper - Posts, Views, Media avatar

Telegram Channel Scraper - Posts, Views, Media

Pricing

from $1.00 / 1,000 run start fees

Go to Apify Store
Telegram Channel Scraper - Posts, Views, Media

Telegram Channel Scraper - Posts, Views, Media

Scrape posts from any public Telegram channel: full text, publish time, view counts, media flags, forwards and outbound links, plus channel title and subscriber count. No login, no bot token. For channel monitoring, news tracking and content research.

Pricing

from $1.00 / 1,000 run start fees

Rating

0.0

(0)

Developer

SR

SR

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

20 days ago

Last modified

Share

This Telegram scraper reads the posts of any public Telegram channel for monitoring, news tracking and content research: the full text, when it was published, how many people saw it, whether it carries a photo or a video, what it forwards, and every external link in it. Every row also carries the channel's display name and subscriber count, so a post's reach can be read against the size of its audience.

No login, no bot token, no API registration. Give it channel names, get back one row per post.

What this Telegram scraper returns

  • One row per post, newest first within each channel, with a stable post identifier (durov/548) you can join runs on.
  • Text with its shape intact. Line breaks and emoji survive, so a fifteen-paragraph post does not arrive as one line.
  • View counts twice: views_text exactly as Telegram shows it (1.97M) and views as an integer (1970000) that sorts and sums correctly.
  • Attribution: the channel owner name, per-post signatures where a channel uses them, and forwarded_from when a post came from elsewhere.
  • Media and link flags: has_photo, has_video, has_link_preview, is_reply, and outbound_links with Telegram's own domains removed.
  • Channel context on every row: channel_title and subscribers at the time of the run.

What a row looks like

An actual row collected from the public channel durov on 4 October 2026:

{
"channel": "durov",
"message_id": 548,
"post": "durov/548",
"url": "https://t.me/durov/548",
"text": "🤝 Telegram has become the sponsor of Codeforces — the largest competitive programming platform in the world. \n\n ⚡CodeForces organizes over 100 coding contests every year and has 4 million contestants signed up.\n\n🌱 Since 2010, I've supported Codeforces through the companies I started.\n\n🆙 But this year will be special. Its founder Mike wants to bring Codeforces to the next level — and we'll be happy to help him do that 🤝",
"text_length": 422,
"posted_at": "2026-09-11 16:04:02",
"views_text": "1.97M",
"views": 1970000,
"author": "Pavel Durov",
"signature": "Pavel Durov",
"forwarded_from": null,
"is_forwarded": false,
"has_photo": false,
"has_video": false,
"has_link_preview": true,
"is_reply": false,
"outbound_links": [
"https://codeforces.com/blog/entry/156620",
"https://codeforces.com/blog/entry/156620"
],
"channel_title": "Pavel Durov",
"subscribers": 10500000,
"position": 1,
"source": "telegram-channel-scraper",
"platform": "telegram",
"fetched_at": "2026-10-04T04:13:26.285Z",
"post_url": "https://t.me/durov/548",
"post_id": "durov/548",
"author_handle": "durov",
"posted_at_iso": "2026-09-11T16:04:02.000Z"
}

The same collection returned 100 posts from two channels: 99 with text, 100 with a view count, and 57 with a photo or video. View and subscriber counts change over time.

Input

FieldTypeDefaultExampleWhat it controls
channelsarray of stringsNo default; the editor pre-fills ["durov", "telegram"]["durov", "https://t.me/telegram"]The public channels to read. An https://t.me/name link or an @name is reduced to the bare name. Names must be 4 to 32 characters of letters, digits and underscores; anything else is reported as bad_input and skipped.
limitinteger, 1 to 5000100500The total number of posts across all channels. Each channel gets a share, but complete source pages can overshoot it. The combined results are trimmed to the total before sorting; the example with 100 posts returns 59 from durov and 41 from telegram. A higher limit means more pages and a longer run.
retriesinteger, 1 to 846Maximum attempts per page, including the first attempt, before it is reported as fetch_failed. Attempts remain bounded even when a large collection is resumed.

Paging walks backwards through message ids: each request asks for the posts older than the lowest id seen so far. There is no page count and no total, so a channel is finished when a page returns nothing new, when its share of limit is reached, or after 40 pages.

Output

One row per post in the default dataset.

FieldMeaning
channelChannel username
message_idMessage number within the channel
postchannel/message_id. Stable, join runs on it
urlDirect link to the post
textPost text with line breaks preserved, capped at 8,000 characters
text_lengthCharacters of text
posted_atWhen it was published, UTC
views_textView count as Telegram shows it, for example 1.2K
viewsView count as an integer, which is what sorts correctly
authorChannel owner name shown on the post
signaturePer-post author signature, when the channel uses them
forwarded_fromOriginal channel, when the post is a forward
is_forwardedWhether the post is forwarded from elsewhere
has_photoWhether the post carries a photo
has_videoWhether the post carries a video
has_link_previewWhether a link preview card is attached
is_replyWhether the post replies to another
outbound_linksExternal links in the post, Telegram's own excluded, at most 20
channel_titleDisplay name of the channel
subscribersSubscriber count at the time of the run
positionRow position in this run
source, platformProduct identifier and social platform
fetched_atFetch time in ISO 8601 UTC
post_url, post_id, author_handleStandard aliases of url, post, and channel
posted_at_isoPublish time in ISO 8601 UTC; the original posted_at format remains available

The key-value store holds two more records. summary carries postsReturned, channelsRead, withText, withViews, withMedia, forwarded, fetchedInSeconds and errorCount. errors lists each failure with a code and a message, and is only written when something went wrong.

Pricing

Pay per event, no subscription.

  • $0.001 per post returned (post).
  • $0.001 per run start fee (run_start).

A run that returns 100 posts costs $0.101. A run that returns 1,000 posts costs $1.001. Posts are billed only when they land in the dataset, so a channel that turns out to be private costs nothing beyond the start fee.

Free Apify plans receive at most 10 rows per run and are billed for those 10, not for the posts fetched and discarded. A paid Apify plan removes the cap.

Limits and failure modes

  • Public channels only. A private channel, a group or a user account has no public preview. Telegram answers with a valid page that contains no posts, which is easy to mistake for a channel that never posted. The Actor recognises that page and reports not_a_public_channel naming the channel, while every other channel in the run continues.
  • A run with no rows still succeeds. The dataset is empty, summary.errorCount is set, and errors holds the reason: bad_input for names that fail validation, not_a_public_channel, fetch_failed when a page failed after all retries, or no_results when a channel exists and has nothing to show.
  • Per-channel ceiling. The walk stops after 40 pages per channel. For a deep archive, run the channel on its own with a high limit rather than sharing the limit across many channels.
  • Timeouts. The default run timeout is 300 seconds; a limit of several thousand across many channels can need more, so raise the run timeout in the run options for large jobs.
  • views can be null. Some channels hide view counts. That is the channel's setting, and it comes back as null rather than zero.
  • Rounded subscriber counts. Telegram rounds them: a channel of 10.7M is reported as 10,700,000, not to the person.
  • No comments, reactions or media files. These live in the app rather than the web preview. has_photo and has_video tell you a post carries media; the files are not downloaded.
  • No historical view counts. views is the count at the moment of the run. Schedule the Actor and join on post to watch a number grow.

Use cases

Competitor and market monitoring. "I want to know what the channels in my sector publish and which posts actually travel." Read them weekly, divide views by subscribers for an engagement rate, and rank posts across channels on one scale.

News and signal collection. "Announcements in my niche land on Telegram before anywhere else." Schedule the Actor, join each run on post, and pipe only the new rows into a feed or an alert.

Content research. "Which topics does this audience respond to?" text next to views shows which subjects a channel's readers open. Filter on has_video to compare formats.

Link and campaign tracking. "Where does this channel send its readers, and who amplifies it?" outbound_links shows the destinations; is_forwarded with forwarded_from maps how a message moves between channels.

How it compares to other ways to scrape Telegram

ApproachWhat it takesWhat you get
This ActorA list of channel namesOne row per post with text, views, media flags, forwards, links and subscriber count, as JSON, CSV or Excel
Official Telegram API (Telethon, Pyrogram)A phone number, an api_id, a logged-in session and your own codeFull access to what that account can see, but you maintain the script, the session and the parsing
Bot APIA bot token and admin rights in the channelOnly new messages in channels where the bot was added; no history of someone else's channel
Browser extensionsA logged-in Telegram Web tab and manual clicks per channelA one-off export; no schedule, no API and no join key across runs

The trade-off is scope: this Telegram scraper reads what a public channel shows to anyone without an account. That covers posts, views and attribution for public channels, and nothing behind a login (private channels, groups, member lists, comments).

FAQ

Do I need a Telegram account, a bot token or an API key?

No. The Actor reads the public web preview of a channel, the same page anyone sees at https://t.me/s/<channel>. You only need an Apify account to run it.

Can this Telegram scraper read private channels, groups or member lists?

No. Only public channels have a web preview. A private channel or group is reported as not_a_public_channel in the errors record, the rest of the run continues, and you are not billed for posts that were never returned.

How many posts can I get from one channel?

Up to 40 pages per channel, and at most 5,000 posts per run across all channels. For a deep archive, run a single channel with a high limit.

How do I track Telegram post views over time?

views is the count at the moment of the run. Schedule the Actor (daily or hourly), keep each dataset, and join the runs on post; the difference between runs is the growth of a post's reach.

Can I export the results to CSV, Excel or Google Sheets?

Yes. Every Apify dataset downloads as JSON, CSV, Excel, XML or RSS, and the Apify integrations push it to Google Sheets, a webhook, Make, Zapier or n8n.

The Actor only collects content that the channel owner published openly on the web, without logging in. How you store and use that data, especially text that names people, is your responsibility under the laws that apply to you, such as the GDPR.

Run it from the API

Start a run with the default input and read the dataset when it finishes:

response="$(curl --fail --silent --show-error --request POST \
'https://api.apify.com/v2/acts/s-r~telegram-channel-scraper/runs?waitForFinish=120' \
--header "Authorization: Bearer $APIFY_TOKEN" \
--header 'Content-Type: application/json' \
--data '{"channels":["durov","telegram"],"limit":100,"retries":4}')"
dataset="$(python3 -c 'import json,sys;print(json.load(sys.stdin)["data"]["defaultDatasetId"])' <<< "$response")"
curl --fail --silent --show-error \
"https://api.apify.com/v2/datasets/$dataset/items?format=json" \
--header "Authorization: Bearer $APIFY_TOKEN"

Set APIFY_TOKEN in your environment before running these commands. For longer runs, poll the run until it finishes before reading the dataset. The response provides the run ID.

Use format=csv or format=xlsx for a spreadsheet.