# Telegram Channel Scraper: Messages, Views, Reactions & Media (`sauliusautomatesit/telegram-channel-scraper`) Actor

Scrape any public Telegram channel without login or API key: message text, date, views, reactions, photo and video links, polls, replies and forwards, plus channel stats with the exact subscriber count. Date range, keyword search and only-new-messages mode for schedules. $0.50 per 1,000 messages.

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

## Pricing

from $0.43 / 1,000 messages

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

## Telegram Channel Scraper: Messages, Views, Reactions & Media

Scrape any public Telegram channel without a Telegram account, API key or phone number. This Actor reads Telegram's own public web preview (`t.me/s/<channel>`) and returns every message as clean JSON, CSV or Excel.

Give it channel usernames (`durov`), `@handles` or `t.me` links. For each message you get:

- **Text** with line breaks kept, plus the links, hashtags and @mentions in it.
- **Date** (ISO, UTC), the post link, the author signature and whether it was edited.
- **Views** and **reactions** (each emoji with its count, plus a total).
- **Media**: photo links, video links with thumbnail and duration, albums, files, polls with percentages, voice notes, link previews.
- **Context**: what it replies to and where it was forwarded from.

Add the channel record and you also get the channel title, description, verified badge, **exact subscriber count** (not the rounded "10.5M") and its photo, video, file and link counts.

### Why use it

- **$0.50 per 1,000 messages.** The best-known Telegram scrapers on the Store charge $1 to $5 per 1,000 messages. Paid Apify plans get the usual discounts on top.
- **No login, no ban risk.** Nothing runs on your Telegram account, so there is no session to lose and no phone number to verify.
- **Only new messages.** Turn on `onlyNewMessages`, schedule the run hourly or daily, and each run returns just what was posted since the last one. You pay only for new messages.
- **Date windows and keyword search.** `dateFrom: "7 days"` stops paging as soon as it reaches older posts, so short windows are fast and cheap. `searchQuery` uses Telegram's own in-channel search.
- **Channels that cannot be read are free.** Private channels, users, bots and unknown names are listed in the run summary and never charged.

### What it's used for

- **News and market monitoring**: crypto, trading signals, breaking news and OSINT channels into Google Sheets, Slack or your database on a schedule.
- **Brand and competitor tracking**: search channels for your brand or product name and catch mentions as they happen.
- **Research and datasets**: full channel histories with views and reactions for analysis, sentiment or AI training data.
- **Influencer and channel vetting**: exact subscribers, views per post and engagement before you buy a placement.
- **AI agents and RAG**: feed an agent the latest posts from a set of channels and let it summarise them.

### Input

| Field | What it does | Default |
|---|---|---|
| `channels` | Usernames, `@handles`, `t.me` links or single post links, one per line | |
| `maxMessagesPerChannel` | Newest first; `0` = whole history (up to 100,000) | `100` |
| `dateFrom`, `dateTo` | `2026-09-01` or relative (`7 days`, `24 hours`) | all time |
| `searchQuery` | Only messages matching a word, phrase or `#hashtag` | |
| `onlyNewMessages` | Return only messages newer than the last run's newest | `false` |
| `includeChannelInfo` | One channel record per channel | `true` |
| `channelInfoOnly` | Only the channel records, no messages | `false` |
| `concurrency` | Channels read in parallel | `3` |

Example:

```json
{
  "channels": ["durov", "https://t.me/telegram", "@tginfo"],
  "dateFrom": "30 days",
  "maxMessagesPerChannel": 1000
}
```

### Output

One item per message:

```json
{
  "type": "message",
  "channel": "durov",
  "channelTitle": "Pavel Durov",
  "messageId": 548,
  "url": "https://t.me/durov/548",
  "date": "2026-09-11T16:04:02+00:00",
  "edited": false,
  "author": "Pavel Durov",
  "text": "🤝 Telegram has become the sponsor of Codeforces, the largest competitive programming platform in the world...",
  "views": 1950000,
  "viewsText": "1.95M",
  "reactions": [{ "emoji": "telegram-stars", "count": 21100 }, { "emoji": "custom:5399847211989246390", "count": 31000 }],
  "reactionsTotal": 48510,
  "mediaType": "text",
  "photos": [],
  "videos": [],
  "linkPreview": { "url": "https://codeforces.com/blog/entry/156620", "siteName": "Codeforces", "title": "Telegram Returns as Title Sponsor of Codeforces!" },
  "links": ["https://codeforces.com/blog/entry/156620"],
  "hashtags": [],
  "mentions": [],
  "forwardedFrom": null,
  "replyTo": null,
  "scrapedAt": "2026-10-03T13:32:48.223Z"
}
```

And one channel record per channel (`type: "channel"`):

```json
{
  "type": "channel",
  "title": "Pavel Durov",
  "username": "durov",
  "verified": true,
  "description": "Founder of Telegram.",
  "subscribers": 10530052,
  "subscribersText": "10.5M",
  "photoCount": 102,
  "videoCount": 46,
  "linkCount": 200,
  "kind": "channel",
  "url": "https://t.me/durov"
}
```

`mediaType` is one of `text`, `photo`, `video`, `album`, `document`, `poll`, `voice`, `sticker`, `round_video` or `unsupported`. Views above 1,000 are rounded by Telegram (`viewsText` has the original, for example `1.95M`); reactions shown as `custom:<id>` are custom emoji. The run summary (messages per channel, channels without a public preview, failures) is saved as `OUTPUT` in the run's key-value store.

### Pricing

Pay per result, no subscription:

| Event | Price |
|---|---|
| Message | $0.0005 per message ($0.50 per 1,000) |
| Channel record (only with `includeChannelInfo`) | $0.001 per channel |
| Actor start | $0.00005 per run |

Channels without a public preview and filters that match nothing are free. Set **Maximum cost per run** in the run options and the run stops cleanly when it is reached.

### Tutorial: Python, schedules and AI agents

You need an Apify API token (Apify Console > Settings > API & Integrations). Apify's free plan includes $5 of monthly credit, about 10,000 messages.

#### 1. Python: the last 30 days of a channel

```bash
pip install apify-client
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("sauliusautomatesit/telegram-channel-scraper").call(run_input={
    "channels": ["durov", "telegram"],
    "dateFrom": "30 days",
    "maxMessagesPerChannel": 0,
    "includeChannelInfo": False,
})

posts = list(client.dataset(run.default_dataset_id).iterate_items())
top = sorted(posts, key=lambda p: p["views"] or 0, reverse=True)[:10]
for p in top:
    print(p["views"], p["url"], (p["text"] or "")[:80])
```

`run.default_dataset_id` is for `apify-client` 3.x; on 2.x write `run["defaultDatasetId"]`. Pass `max_total_charge_usd=1` to `.call()` to cap a run at $1.

#### 2. Hourly monitoring with only new messages

Save an input like this as a Task and give it an hourly or daily schedule. Each run returns only posts published since the previous run; connect the Task to Slack, Google Sheets or a webhook in the Integrations tab.

```json
{
  "channels": ["channel_one", "channel_two", "channel_three"],
  "onlyNewMessages": true,
  "maxMessagesPerChannel": 500,
  "includeChannelInfo": false
}
```

The last message seen per channel is kept in a key-value store named `telegram-channel-scraper-state` in your account. Delete a key there to start a channel over.

#### 3. AI agents: one MCP URL, one tool

This URL gives any MCP client exactly one tool, this Actor:

```
https://mcp.apify.com/?tools=sauliusautomatesit/telegram-channel-scraper
```

Claude Code:

```bash
claude mcp add --transport http telegram-channels "https://mcp.apify.com/?tools=sauliusautomatesit/telegram-channel-scraper"
```

Cursor, Claude Desktop, VS Code and other clients that take JSON:

```json
{
    "mcpServers": {
        "telegram-channels": {
            "url": "https://mcp.apify.com/?tools=sauliusautomatesit/telegram-channel-scraper",
            "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
        }
    }
}
```

Then ask: *"What did @durov and @telegram post this week, and which post got the most views?"*

### Related Actors

- [Reddit Search Scraper](https://apify.com/sauliusautomatesit/reddit-customer-questions): the same keyword monitoring across Reddit posts, no login.
- [YouTube Transcript Scraper](https://apify.com/sauliusautomatesit/youtube-transcript-scraper): YouTube videos and whole channels as text, for cross-platform monitoring and RAG.
- [Google Trends API](https://apify.com/sauliusautomatesit/google-trends-api): Google search interest for the topics your channels track.

### Limits and notes

- Only public channels with Telegram's web preview switched on can be read. Private channels and groups, invite links (`t.me/+...`), users and bots are skipped and listed in the summary.
- Comments under posts (the discussion group) are not included.
- Photo and video links point to Telegram's CDN and expire after some hours; download the files soon after the run if you need them.
- Video links are present for videos Telegram plays in the web preview; very large videos show only the thumbnail.
- Not affiliated with Telegram.

# Actor input Schema

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

Public Telegram channels, one per line: a username (`durov`), `@durov`, a link (`https://t.me/durov`) or a single post link (`https://t.me/durov/548`). Private invite links (`t.me/+...`) cannot be read without joining.

## `maxMessagesPerChannel` (type: `integer`):

Newest messages first. 0 = the whole history (up to 100,000 per channel).

## `dateFrom` (type: `string`):

Only messages posted on or after this date: `2026-09-01`, or relative like `7 days` or `24 hours`. Paging stops at the first older message, so short windows are fast and cheap.

## `dateTo` (type: `string`):

Only messages posted on or before this date (`2026-09-30`). Leave empty for now.

## `searchQuery` (type: `string`):

Only messages that match this word or phrase, using Telegram's own channel search (also matches hashtags such as `#news`).

## `onlyNewMessages` (type: `boolean`):

For scheduled runs: remembers the newest message seen in each channel and returns only messages posted after it. The first run returns up to the max above.

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

One extra item per channel: title, description, verified badge, exact subscriber count, and photo, video, file and link counts.

## `channelInfoOnly` (type: `boolean`):

Return just the channel record for each channel (exact subscribers, description, counts), for example to vet or rank many channels cheaply.

## `concurrency` (type: `integer`):

How many channels to read at the same time (1 to 5).

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

Apify Proxy (datacenter) is used by default and is enough for Telegram.

## Actor input object example

```json
{
  "channels": [
    "telegram",
    "durov"
  ],
  "maxMessagesPerChannel": 50,
  "onlyNewMessages": false,
  "includeChannelInfo": true,
  "channelInfoOnly": false,
  "concurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One item per message (and per channel). Download as JSON, CSV or Excel, or read it from this API endpoint.

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

Messages per channel, channels without a public preview, and failures.

# 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": [
        "telegram",
        "durov"
    ],
    "maxMessagesPerChannel": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("sauliusautomatesit/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": [
        "telegram",
        "durov",
    ],
    "maxMessagesPerChannel": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("sauliusautomatesit/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": [
    "telegram",
    "durov"
  ],
  "maxMessagesPerChannel": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call sauliusautomatesit/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sauliusautomatesit/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/fevSd8EsdbZFMVbJ7/builds/7dA4SQXgm0gbOqpxi/openapi.json
