# Telegram Channel Scraper - Public Posts, Views & Reactions (`tidytools/telegram-channel-scraper`) Actor

Scrape public Telegram channels without login or API keys: post text, date, views, reactions, photos and videos, links, hashtags, forwards and replies, plus channel subscribers. Date and keyword filters, new-post monitoring. $1 per 1,000 posts; failed channels are free.

- **URL**: https://apify.com/tidytools/telegram-channel-scraper.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (community)
- **Categories:** Social media, Marketing, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Telegram Channel Scraper do?

**Paste public Telegram channels (t.me/name or @name), get one row per post (text, date, views, reactions, media links, forwards, polls) and one profile row per channel, at $1 per 1,000 posts and $1 per 1,000 channels, with no start fee.** Missing channels, filtered posts and duplicates are free.

It reads **public Telegram channels without login, phone number or API keys** and returns one clean row per post, plus the channel's profile.

- 📰 **Posts**: text, date, views, reactions (with counts per emoji and paid stars), photos and video links, link previews, polls, documents, links, hashtags, mentions, forwards, replies, author signature and edited flag.
- 📊 **Channel profile**: title, description and its links, subscribers, photo, video, file and link counts, verified badge and the latest post.
- ⏪ **Back to the first post**: older posts are read page by page (about 20 per page), as far back as you want.
- 🗓️ **Filters and monitoring**: only posts after a date, only posts with your keywords, or only posts newer than your last scheduled run.
- 💸 **$1 per 1,000 posts, no start fee.** Missing channels, filtered posts and duplicates are free.
- 🤝 **Honest by design**: the same public web preview any browser sees at `t.me/s/<channel>`; our own user agent, robots.txt followed, no Telegram account, no member lists.

#### Real results (3 October 2026)

10 inputs (6 channels, a post link, a bot, an invite link and a misspelled channel), newest 50 posts from the last 30 days: **115 rows (112 charged: 6 channels, 106 posts) in 5 seconds, $0.059**. The bot, the invite link and the missing channel were free rows (`no_preview`, `invalid_input`, `not_found`).

| Channel | Subscribers | Photos | Videos | Links | Verified | Latest post |
|---|---|---|---|---|---|---|
| Pavel Durov (@durov) | 10,500,000 | 102 | 46 | 200 | yes | 2026-09-11 |
| Telegram News (@telegram) | 9,410,000 | 16 | 228 | 378 | yes | 2026-08-26 |
| BBC News, Russian (@bbcrussian) | 388,000 | 21,400 | 4,200 | 61,700 | yes | 2026-10-03 |
| Whale Alert (@whale_alert_io) | 299,000 | 5 | - | 103,000 | no | 2026-10-03 |
| Telegram Info (@tginfo) | 83,000 | 1,380 | 90 | 2,950 | no | 2026-09-29 |

Other runs the same day: 200 posts of @bbcrussian since 20 September through our servers only (11 pages, 15 seconds, $0.10); monitoring mode run twice in a row: 10 posts the first time, 0 new (and $0) the second.

### Use cases

- **Brand, crypto and news monitoring**: schedule a run with monitoring on and get every new post of the channels you follow, with views and reactions.
- **Channel research and influencer vetting**: subscribers, posting frequency, views per post and engagement before buying an ad.
- **Market and sentiment data**: crypto signals, product announcements and news flows as structured rows for dashboards or an LLM.
- **Archiving and analysis**: a channel's full public history with dates, links and media links.

### How much does it cost?

| Event | Price | When |
|---|---|---|
| Post | **$1.00 / 1,000 posts** ($0.001 each) | one post returned |
| Channel info | **$1.00 / 1,000 channels** ($0.001 each) | one channel profile row |

**Free:** channels that do not exist or have no public preview (private channels, groups, bots, user accounts), posts older than your date filter, posts without your keywords, posts already returned in monitoring mode, service messages, duplicates and invalid inputs. **No start fee.** Larger Apify plans get the usual volume discount (Silver 10%, Gold and above 20%).

**Example:** 20 channels, newest 500 posts each: 20 x $0.001 + 10,000 x $0.001 = **$10.02**.

For comparison (Apify Store, checked 3 October 2026, users in the last 30 days): tri_angle/telegram-scraper (150 users) $0.005 per result; truefetch/telegram-channel-message (115 users) $0.00035 per post + $0.0008 per media + $0.01 per start; webfinity/telegram-channel-content-media-scraper-v2 (74 users) $0.01 per item; agentx/telegram-chat-scraper (25 users) $0.00054 per item + $0.01 per start; for channel profiles truefetch/telegram-profile (68 users) $0.003 and agentx/telegram-info-scraper (51 users) $0.0052. Some of them also download media files or read groups; this Actor returns media links, not files, and reads public channels only.

#### Control your cost

- `maxPostsPerChannel` and `maxResults` (per run) cap the number of posts.
- Before reading, the run logs its plan (worst case) and compares it with your **maximum charge per run**. When the next post could pass it, the run stops: `SUMMARY.status` is `LIMIT_REACHED` and `SUMMARY.notProcessed` lists the channels not read.
- Every row has `charged: true` or `false`. If Apify restarts the run, posts already delivered are skipped and not charged again.

### How to use it

1. **Channels or post links**: one per line, e.g. `https://t.me/durov`, `t.me/s/durov`, `@durov`, `durov`, or a post `https://t.me/durov/300`. Line breaks, spaces, commas and semicolons all separate inputs, and pasted quotes or brackets are ignored. The same channel written in different ways (`@durov`, `t.me/s/durov`) is read and charged once.
2. **Posts per channel**: the newest N (0 = channel profile only).
3. Optional: **Only posts published after** (`2026-09-01`, `7 days`, `12 hours`), **Only posts containing** (keywords), **monitoring** for scheduled runs.
4. Click **Start**. The **Posts** view shows the key columns, **Channels** the profiles, **All fields** everything (CSV, Excel, JSON).

#### Input example

```json
{
    "channels": ["https://t.me/whale_alert_io", "@durov", "bbcrussian"],
    "maxPostsPerChannel": 200,
    "postedAfter": "7 days",
    "keywords": ["ETH", "USDT"]
}
```

#### Output example

A real post (3 October 2026), shortened:

```json
{
    "type": "post",
    "channel": "whale_alert_io",
    "channelTitle": "Whale Alert",
    "postId": 103692,
    "url": "https://t.me/whale_alert_io/103692",
    "date": "2026-10-03T14:07:56+00:00",
    "text": "🚨 🚨 🚨  20,000 $ETH (53,573,068 USD) transferred from #Bitfinex to #Aave\nDetails",
    "views": 1120,
    "viewsText": "1.12K",
    "reactionCount": null,
    "mediaTypes": [],
    "links": ["https://whale-alert.io/transaction/ethereum/0x6193864a7b6eb0070fb3929666545feb1b842049d10ff6b6668a1566c36e27fb"],
    "hashtags": ["#Bitfinex", "#Aave"],
    "forwardedFrom": null,
    "replyTo": null,
    "isEdited": false,
    "charged": true
}
```

#### What the fields mean

- **views / viewsText**: Telegram shows rounded numbers above 1,000 (`1.95M`), so `views` is approximate for big posts.
- **reactions**: `emoji` is the emoji itself, `custom:<id>` for a custom emoji or `paid_star` for paid Telegram Stars reactions; `reactionCount` is their sum.
- **media**: `photo` (image URL), `video` / `round_video` (video URL when Telegram shows it, thumbnail, duration; `onlyInApp: true` when Telegram plays it only in its apps), `document` (title), `voice`, `sticker`, `location`. Media URLs are Telegram's CDN links and expire after a while: download what you need soon.
- **forwardedFrom / replyTo**: the original channel and post of a forward; the post a reply answers.
- **SUMMARY** (key-value store): counts, `perChannel` (posts, pages, why it stopped), skipped counts per filter and the cost plan.

### Use it from code

```bash
curl -X POST "https://api.apify.com/v2/acts/tidytools~telegram-channel-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"channels":["@durov","https://t.me/telegram"],"maxPostsPerChannel":50}'
```

**With AI agents (MCP):** connect Apify's MCP server (`https://mcp.apify.com?tools=tidytools/telegram-channel-scraper`) to Claude, Cursor or any MCP client and ask, for example, "Summarize the last 7 days of posts on t.me/whale_alert_io". Minimal input: `{"channels": ["@durov"]}`. Failed items are not charged and carry an `errorType`.

### Limitations

- **Public channels only.** Private channels, invite links, groups, bots and user accounts have no public web preview (`no_preview`, free). No group members, no phone numbers, no message search across Telegram.
- **What the web preview shows**: polls, some videos ("Media is too big") and some media types are only playable in Telegram's apps; the row then has the post with `onlyInApp` media. Comments under posts are not read.
- **Rounded numbers**: views and reaction counts above 1,000 are rounded by Telegram (1.95M).
- Telegram slows down bursts of requests (HTTP 429): the Actor waits and retries, then reads the page from our servers. Very large channels take about one second per 20 posts.

### Responsible use

The Actor reads only the public web preview that Telegram serves to every browser, with an honest user agent (`TidyToolsTelegram`) and robots.txt followed. It does not log in, use a Telegram account, solve CAPTCHAs or collect member lists or phone numbers. Posts belong to their authors: use the data for analysis and monitoring, credit the source when you quote, and follow Telegram's Terms of Service and the laws that apply to you (for example the GDPR when posts name private persons).

### FAQ

**Does the Actor collect usage data?** The Actor sends one anonymous usage counter per run (input type, item count range, outcome) to improve the tool; no URLs, content or account data.

**Why does a channel return `no_preview`?** It is private, a group, a bot or a user account, or the owner turned the preview off. Only public channels can be read without logging in.

**Can I get only new posts every hour?** Yes: turn on **Only posts newer than the last run**, give the monitor a name and schedule the Actor. The first run returns the newest posts and saves a baseline.

**How far back can it go?** To the channel's first post. Use `maxPostsPerChannel` or `postedAfter` to limit it. With a keyword filter the Actor looks at no more than the newest 10,000 posts of a channel, even if fewer than `maxPostsPerChannel` posts matched (`SUMMARY.perChannel` says so).

**What happens with a mistake in the input?** A line that is not a public Telegram channel or post link (an invite link, `t.me/c/...`, a username that is too short or too long), an unknown channel, or a date filter such as "someday" or `7` (write `7 days`) gives a free row with an `errorType` (`invalid_input`, `not_found` or `no_preview`) and the run ends normally, never as a failed run. List items that are not text (a number, an object without `url`) are free `invalid_input` rows too. Also fine: several inputs in one line (separated by spaces, commas, semicolons or tabs), quotes or brackets around an input, and the same channel written twice (read once). Text such as `"false"` for a yes/no option and numbers written as text (`"50"`) are understood; a value that is not a number falls back to the default and is listed in `SUMMARY.inputNotes`. Posts per channel `0` together with the channel info row off returns one free `invalid_input` row, because there would be nothing to return.

**What if I give both a channel and one of its posts?** The post is returned and charged once, whichever way it was requested. An explicitly requested post link is returned even if it is older than your date filter or does not contain your keywords; the filters apply to the posts read from channels.

**Are media files downloaded?** No: rows contain the photo and video URLs Telegram serves, which expire after a while.

### Support

Open an issue in the **Issues** tab with your input and the run link. Issues are checked regularly.

# Actor input Schema

## `channels` (type: `array`):

One per line: a public channel (https://t.me/durov, t.me/s/durov, @durov or durov) or a single post (https://t.me/durov/300). Several per line are fine (spaces, commas or semicolons); pasted quotes are ignored; the same channel written twice is read once. Up to 1,000 per run. Invite links, private channels and lines that are not Telegram links become a free row with errorType "invalid_input".

## `maxPostsPerChannel` (type: `integer`):

The newest N posts of each channel (0 = channel info only). Older posts are read page by page (about 20 posts per page) back to the channel's first post.

## `postedAfter` (type: `string`):

A date (2026-09-01) or a period (12 hours, 7 days, 3 months). Reading a channel stops at the first older post; older posts are free.

## `keywords` (type: `array`):

Keep only posts whose text or link preview contains any of these words or phrases (case-insensitive). Posts that do not match are free.

## `includeChannelInfo` (type: `boolean`):

One row per channel: title, description, subscribers, photo, video, file and link counts, verified badge, photo and the latest post ($1 per 1,000 channels).

## `includeHtml` (type: `boolean`):

Add the post's original HTML (bold, links, custom emoji) next to the plain text.

## `onlyNewPosts` (type: `boolean`):

For scheduled runs: return (and charge) only posts newer than the newest post seen by earlier runs with the same monitor name, and each channel info row only once. The first run returns the newest posts and saves the baseline.

## `monitorName` (type: `string`):

Separate monitors keep separate memories (letters, digits and dashes). Only used with monitoring on.

## `maxResults` (type: `integer`):

The run stops after this many post rows (channel rows are not counted). A cost cap on top of Apify's maximum charge per run.

## `httpVia` (type: `string`):

Every request uses our own user agent and follows robots.txt, with no cookies, login or Telegram account. If Telegram rate-limits one network (HTTP 429), Auto waits briefly, then retries the page from our servers.

## `proxyConfiguration` (type: `object`):

Only used for requests sent from Apify's network. Apify proxy usage is billed to your Apify account.

## `maxConcurrency` (type: `integer`):

How many channels are read at the same time (each reads its pages one after another).

## `pageDelayMs` (type: `integer`):

Politeness delay between two older-post pages of the same channel.

## Actor input object example

```json
{
  "channels": [
    "https://t.me/telegram"
  ],
  "maxPostsPerChannel": 20,
  "includeChannelInfo": true,
  "includeHtml": false,
  "onlyNewPosts": false,
  "monitorName": "default",
  "maxResults": 10000,
  "httpVia": "auto",
  "maxConcurrency": 3,
  "pageDelayMs": 400
}
```

# Actor output Schema

## `posts` (type: `string`):

No description

## `channels` (type: `string`):

No description

## `full` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "channels": [
        "https://t.me/telegram"
    ],
    "maxPostsPerChannel": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidytools/telegram-channel-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "channels": ["https://t.me/telegram"],
    "maxPostsPerChannel": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("tidytools/telegram-channel-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "channels": [
    "https://t.me/telegram"
  ],
  "maxPostsPerChannel": 20
}' |
apify call tidytools/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidytools/telegram-channel-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/VaQRGy2pRk3siLAbs/builds/YajHM1TBQJ29JN87q/openapi.json
