Telegram Channel Scraper - Posts, Views, Reactions & Media avatar

Telegram Channel Scraper - Posts, Views, Reactions & Media

Pricing

from $1.50 / 1,000 results

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

Telegram Channel Scraper - Posts, Views, Reactions & Media

Scrape any public Telegram channel without logging in: post text, date, views, reactions, photos, videos, files, polls, forwards, replies and channel info (subscribers, description). Pages back through full history. Fast HTTP-only, no browser, no Telegram account.

Pricing

from $1.50 / 1,000 results

Rating

0.0

(0)

Developer

Giovanni Rich

Giovanni Rich

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Telegram Channel Scraper - Scrape Telegram Messages & Posts API

This Telegram scraper lets you scrape Telegram messages and posts from any public channel, with no login, phone number or API key. Use this Telegram channel scraper as a simple Telegram posts API: send channel names, get back each post's text, date, views, reactions, photos, videos, files, polls, forwards and replies, plus channel info (subscribers, description), as JSON, CSV or Excel.

The scraper reads Telegram's own public web preview (t.me/s/<channel>) over plain HTTP, with no headless browser. It returns about 100 posts per second, and runs cost very little. It also saves one channel-info row per channel with subscribers, description, media counters and the verified badge.

How to use

  1. Enter one or more public channels in Channels (durov, @durov or https://t.me/durov). Optionally set Max posts per channel, a date limit or a search keyword.
  2. Click Start. Small runs finish in seconds.
  3. Download the results as JSON, CSV or Excel from the Output tab, or call the actor from your own code through the Apify API.

What you get

  • Post content: the full text with line breaks and emoji, plus the links, hashtags and @mentions found in it.
  • Engagement: view count (e.g. 18.8M becomes 18800000), every reaction with its count (including paid ⭐ Star reactions), and the total reaction count.
  • Media: photo URLs, video URLs with thumbnails and duration, albums, voice notes and audio, round videos, stickers, file names and sizes, polls with option percentages and voter counts, locations, and link previews (title, site, description, image).
  • Context: forwarded-from (channel name, username, original post), reply-to (post ID and quoted text), author signature, and whether the post was edited.
  • IDs and links: post ID, post URL and the numeric Telegram channel ID.
  • Channel info: title, description, subscriber count, photo/video/file/link counters, the verified badge and the avatar URL.
  • Full history: the scraper pages back from the newest post to the oldest. You can stop it at a post count, at a date, or let it read the whole channel.
  • In-channel search: pass a keyword to get only the matching posts, using Telegram's own search.

Use cases

  • Media monitoring and news tracking across dozens of channels
  • Crypto, trading and signals research: what got posted when, and how much attention it got
  • Brand and competitor monitoring, influencer and channel vetting (subscribers, views per post, engagement)
  • Datasets for NLP, sentiment analysis and LLM training
  • Archiving a channel's history, with links to every media file

Input

FieldDefaultDescription
channelsdurovUsernames or links, any form: durov, @durov, https://t.me/durov, https://t.me/s/durov
maxPostsPerChannel100Newest posts first. 0 = the whole history
onlyPostsNewerThan-A date (2026-09-01) or a relative period (7 days, 2 weeks, 3 months). The scraper stops when posts get older than this.
searchQuery-Returns only the posts that match this keyword
includeChannelInfotrueAdds one channel row per channel
maxConcurrency5Channels scraped in parallel (1-20)
maxRequestRetries6Retries per failed page (0-20)
proxyConfigurationApify ProxyDatacenter proxy is enough

Input example

{
"channels": ["durov", "bloomberg", "https://t.me/telegram"],
"maxPostsPerChannel": 500,
"onlyPostsNewerThan": "30 days"
}

Output example

Each post is one dataset item with type: "post". The example below is a real item from a run on 24 Sep 2026. The long CDN URL is shortened, and 5 of its 8 reactions are left out.

{
"type": "post",
"channel": "bloomberg",
"channelId": "1488156064",
"channelTitle": "Bloomberg",
"postId": 3225,
"url": "https://t.me/bloomberg/3225",
"date": "2026-09-22T03:26:17.000Z",
"timestamp": 1790047577,
"edited": false,
"text": "Alibaba unveils new AI chip it says is China's most powerful and targets 20 gigawatts of data center capacity by 2032. https://bloom.bg/4ydroys\n\nπŸ“·: Qilai Shen/Bloomberg",
"textLength": 169,
"views": 13400,
"viewsText": "13.4K",
"reactionsTotal": 173,
"reactions": [
{ "emoji": "πŸ”₯", "customEmojiId": null, "isPaid": false, "count": 62, "countText": "62" },
{ "emoji": "❀", "customEmojiId": null, "isPaid": false, "count": 33, "countText": "33" },
{ "emoji": "πŸ‘", "customEmojiId": null, "isPaid": false, "count": 27, "countText": "27" }
],
"mediaType": "photo",
"mediaUrls": ["https://cdn5.telesco.pe/file/B-Vjm-tufLB1ZSrW02mb...R7w.jpg"],
"isAlbum": false,
"photos": [{ "url": "https://cdn5.telesco.pe/file/B-Vjm-tufLB1ZSrW02mb...R7w.jpg", "postUrl": "https://t.me/bloomberg/3225" }],
"videos": [],
"roundVideos": [],
"audio": [],
"documents": [],
"sticker": null,
"poll": null,
"location": null,
"linkPreview": null,
"links": ["https://bloom.bg/4ydroys"],
"hashtags": [],
"mentions": [],
"forwardedFrom": null,
"replyTo": null,
"author": null,
"unsupportedInPreview": false,
"input": "bloomberg",
"searchQuery": null,
"scrapedAt": "2026-09-24T05:15:24.280Z"
}

When a post has them, the fields look like this:

"forwardedFrom": { "name": "Mash Money", "url": "https://t.me/mashmoneymash/2732", "username": "mashmoneymash", "postId": 2732 },
"replyTo": { "postId": 78053, "url": "https://t.me/mash/78053", "author": "Mash", "text": "..." },
"videos": [{ "url": "https://cdn4.telesco.pe/file/7451201fa5.mp4?token=...", "thumbnail": "https://cdn4.telesco.pe/file/...", "duration": "0:25", "isGif": false, "tooLargeForPreview": false }],
"documents": [{ "title": "Features 1.png", "size": "3.9 MB", "postUrl": "https://t.me/designers/243?single" }],
"poll": { "question": "...", "type": "Anonymous Poll", "voters": 1720, "options": [{ "text": "Yes", "percent": 42 }, { "text": "No", "percent": 34 }] }

Each channel also gets one item with type: "channel":

{
"type": "channel",
"username": "bloomberg",
"title": "Bloomberg",
"description": "The first word in business news.",
"subscribers": 175000,
"photos": 800,
"videos": 49,
"files": null,
"links": 3180,
"verified": true,
"photoUrl": "https://cdn5.telesco.pe/file/TczlHBZov8...jpg",
"url": "https://t.me/bloomberg",
"previewUrl": "https://t.me/s/bloomberg",
"previewAvailable": true
}

The dataset has three ready-made views: Overview (date, text, views, reactions, media), Engagement (views and reactions for each post) and Channels (the channel-info rows).

Field fill rates (real test run: 900 posts across 6 channels)

FieldFill rate
postId, url, date, channelId, channelTitle100%
views100%
text99% (the rest are media-only posts)
links in text77%
media (photo / video / file / poll)66%
reactions62% (100% on channels that have reactions turned on; some channels switch them off)
linkPreview17%
author signature17% (only channels that sign their posts)
hashtags15%
edited14%
forwardedFrom / replyTo3% / 2%

Pricing

Pay per result: $1.50 per 1,000 results. A result is a post or a channel-info row saved to the dataset.

  • 1,000 results = $1.50
  • 10,000 results = $15

You're never charged for failed requests or for duplicates. If you set a maximum cost per run, the scraper stops cleanly when it reaches that amount. The Apify free plan includes $5 of monthly credit, enough to try the scraper on a few thousand posts.

Integrations

  • Make, Zapier and n8n: start runs and pass new posts into your workflows.
  • Google Sheets: export the dataset straight to a spreadsheet.
  • Apify API: run the actor and fetch results over REST, or with the JavaScript and Python API clients.
  • Webhooks: get a call when a run finishes.
  • MCP for AI agents: through the Apify MCP server (https://mcp.apify.com), Claude, ChatGPT and Cursor agents can call this actor as a tool.
  • Schedules: run it daily or hourly to track channels over time.

Limits (read before large runs)

  • Only public channels that have a web preview. Private channels, invite links (t.me/+...), groups and bots have no public post history. For groups and channels with the preview turned off, the scraper returns only the channel-info row (previewAvailable: false), and the log says why.
  • Comments are not included. Telegram doesn't show discussion-group replies in the web preview.
  • Custom (premium) emoji reactions come back as a customEmojiId with no emoji character, because Telegram shows them as images. Their counts are still correct.
  • Counts are rounded the way Telegram shows them, e.g. 13.4K is saved as 13400. The raw text is kept in viewsText and countText.
  • Very large videos are listed without a direct file URL, because Telegram doesn't stream them in the preview. You still get the thumbnail, the duration and a link to the post (tooLargeForPreview: true).
  • Media URLs are signed CDN links that expire after a while. Download the files soon after the run if you need to keep them.
  • A few service messages and deleted posts leave gaps in the post IDs. That's normal.

FAQ

Do I need a Telegram account or API ID? No. The scraper reads the same public page you see when you open https://t.me/s/durov in a browser.

How far back can it go? All the way to the first post. Set maxPostsPerChannel: 0 to read the whole history. In our tests, 250 posts took 7 seconds and 480 posts took 12 seconds.

How do I get only new posts every day? Schedule the actor with onlyPostsNewerThan: "1 day". To skip anything you've already stored, deduplicate on url (or on channel + postId).

Why did a channel return 0 posts? Check the RUN_SUMMARY record in the key-value store. It gives a stop reason for each channel, such as "channel not found" or "web preview not available (group)".

Which proxy should I use? The default Apify Proxy works. Telegram rarely blocks the preview pages. When it rate-limits a request, the scraper retries it on a new IP.

How does it avoid blocks? It sends realistic desktop Chrome headers through Apify Proxy and keeps a pool of sessions. A 401, 403 or 429 response, or a page it doesn't recognise, retires that session, so the next attempt goes out on a new IP. Failed pages are retried up to 6 times by default (maxRequestRetries), with a growing back-off between attempts.

What are the limits? Only public channels with a web preview (no private channels, groups or comments), up to 20 posts per preview page, and counts rounded the way Telegram shows them. See Limits above for the full list. There's no fixed cap on posts: maxPostsPerChannel: 0 reads the whole history.

Is it legal to scrape Telegram? The scraper collects only content that channel owners have published publicly on the open web. You're responsible for how you use the data, including complying with Telegram's terms of service and with privacy laws such as GDPR for any personal data that appears in posts. This is not legal advice; if you're unsure, check with a lawyer.

How it works (for developers)

t.me/s/<channel> returns server-rendered HTML with up to 20 posts per page, plus a <link rel="prev" href="?before=N"> cursor. The scraper parses each tgme_widget_message block with cheerio. It reads every field defensively, so one malformed post can never break a page. Channels run in parallel, and the pages of one channel run in order. Everything the scraper has already seen is kept in the actor state, so a migrated run resumes without creating duplicates.

Run it locally:

npm install
npm test # parser tests on saved fixtures
APIFY_LOCAL_STORAGE_DIR=./storage node src/main.js # input in storage/key_value_stores/default/INPUT.json