# Telegram Channel Scraper – Messages, Views & Subscribers (`phantom-x64/telegram-channel-scraper`) Actor

Extract messages, views, reactions, links, media, forwards and exact subscriber counts from public Telegram channels. No login, bot token or API key. Filter by keyword and date, or monitor channels and get only new posts. $0.50 per 1,000 messages, no start fee.

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

## Pricing

from $0.50 / 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.

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 & subscribers (no API key)

**Telegram scraper for public channels.** Export messages, views, reactions, links, photos/videos, forwards, replies, author signatures and **exact subscriber counts** from any public Telegram channel — **no login, no phone number, no bot token, no Telegram API key**. Turn on **monitor mode** and every scheduled run returns **only the new posts**, so you never pay twice for the same message.

- ✅ Works with any public channel: `durov`, `@durov`, `https://t.me/durov`, `https://t.me/s/durov`
- ✅ Monitor mode (only new messages since the last run) for alerts and dashboards
- ✅ Keyword filter: Telegram's own in-channel search (fast) or an exact text filter (thorough)
- ✅ Date range: absolute dates or relative ("7 days", "24 hours")
- ✅ Exact subscriber count (10157932, not "10.2M")
- ✅ Clean, flat JSON — export to CSV, Excel, Google Sheets, or call it as a **Telegram API** alternative from your code
- ✅ **$0.50 per 1,000 messages, no start fee** — empty monitor runs cost $0

### What can this Telegram channel scraper do?

| Use case | How |
|---|---|
| **Crypto & token monitoring** | Watch project, exchange and "alpha" channels hourly; filter by ticker (`$BTC`, `binance`, contract address) and push new posts to Slack, Discord or a webhook with an Apify integration. |
| **Brand monitoring & PR** | Track mentions of your brand or competitors in news and community channels; measure views and reactions per post. |
| **OSINT & research** | Archive what public channels publish (text, links, media URLs, forwards) with timestamps, for investigations, journalism and academic research. |
| **Market research & content analysis** | Compare channel growth (subscribers), engagement (views, reactions) and posting frequency; find the posts that perform best. |
| **Archiving & backups** | Keep a JSON/CSV archive of your own or any public channel; schedule it daily and keep only new posts. |
| **AI / LLM pipelines** | Feed clean channel text into RAG, summarisation or sentiment pipelines. |

### Quick start (under 1 minute)

1. Click **Try for free** and enter one or more public channels (`durov`, `@durov` or `https://t.me/durov`).
2. Set how many messages you want per channel, and optionally a date range or keywords. The pre-filled input (2 channels, 50 messages each) is a safe first run.
3. Click **Start**. The default run finishes in a few seconds. Download JSON, CSV or Excel, or schedule it with monitor mode to get only new posts.

### Input

| Field | What it does |
|---|---|
| `channels` | Usernames or links of public channels. |
| `maxMessagesPerChannel` | Newest messages per channel (default 50). `0` = only the channel info row. |
| `dateFrom` / `dateTo` | `2026-10-01`, an ISO date-time, or relative values like `7 days`, `24 hours`. |
| `onlyNewMessages` | Monitor mode: remembers the last message per channel and returns only newer ones. |
| `monitorStateKey` | Name of the monitor (use one per schedule/client). |
| `keywords` | Keep only messages containing any of these words. |
| `keywordMode` | `telegramSearch` (Telegram's in-channel search, fast, whole words) or `localFilter` (exact substring, reads page by page). |
| `includeChannelInfo` | One row per channel: title, description, subscribers, verified, photo, media counters. |
| `exactSubscriberCount` | Exact subscribers from the channel page (one extra request). |
| `proxyConfiguration` | Apify Proxy (datacenter is enough). |

#### Example: last 100 posts from three channels

```json
{
  "channels": ["durov", "https://t.me/telegram", "@AJENews_Official"],
  "maxMessagesPerChannel": 100
}
```

#### Example: hourly crypto monitor (only new posts that mention Binance or ETF)

```json
{
  "channels": ["cointelegraph", "whale_alert_io"],
  "onlyNewMessages": true,
  "monitorStateKey": "crypto-hourly",
  "keywords": ["binance", "etf"],
  "keywordMode": "localFilter",
  "includeChannelInfo": false,
  "maxMessagesPerChannel": 200
}
```

Schedule it in **Apify Console → Schedules** and connect a Slack/Discord/webhook integration: each run only delivers (and only charges) the posts you have not seen yet.

#### Example: archive a date range

```json
{
  "channels": ["telegram"],
  "dateFrom": "2026-01-01",
  "dateTo": "2026-06-30",
  "maxMessagesPerChannel": 100000,
  "maxPagesPerChannel": 5000
}
```

### Output

Every row has a `type`: `message`, `channel` or `error` (errors are free). Real sample (shortened):

```json
{
  "type": "message",
  "channel": "durov",
  "message_id": 552,
  "url": "https://t.me/durov/552",
  "date": "2026-10-11T14:52:40Z",
  "text": "✨ Designers × AI\n\n🆕 The mockup era is over...",
  "views": 1850000,
  "reactions": [
    {"emoji": "⭐", "custom_emoji_id": null, "paid_stars": true, "count": 7740},
    {"emoji": null, "custom_emoji_id": "5936157098181135162", "paid_stars": false, "count": 17500}
  ],
  "forwarded_from": {"name": null, "url": null, "channel": null, "message_id": null, "hidden": true},
  "reply_to": null,
  "links": ["https://t.me/designers"],
  "link_preview": null,
  "media": [{"type": "video", "url": null, "thumbnail_url": "https://cdn4.telesco.pe/file/...jpg", "duration": "1:15", "too_big_for_web": true}],
  "poll": null,
  "album_message_ids": [],
  "author_signature": "Pavel Durov",
  "edited": false,
  "hashtags": [],
  "mentions": [],
  "matched_keyword": null,
  "scraped_at": "2026-11-02T13:17:31Z"
}
```

```json
{
  "type": "channel",
  "channel": "durov",
  "url": "https://t.me/durov",
  "title": "Pavel Durov",
  "description": "Founder of Telegram.",
  "subscribers": 10157892,
  "subscribers_exact": true,
  "verified": true,
  "photo_url": "https://cdn4.telesco.pe/file/...jpg",
  "counters": {"subscribers": 10200000, "photos": 102, "videos": 47, "links": 200},
  "has_public_preview": true
}
```

Message fields: `channel`, `message_id`, `url`, `date` (UTC), `text`, `views`, `reactions` (emoji or custom-emoji id, count, paid Stars), `forwarded_from`, `reply_to` (id, author, text preview), `links`, `link_preview`, `media` (photo, video, gif, round_video, voice, audio, document, sticker, location, poll — with URL/thumbnail when Telegram exposes it), `poll` (question, options, %), `album_message_ids`, `author_signature`, `edited`, `hashtags`, `mentions`, `matched_keyword`, `scraped_at`.

### Pricing

Pay per event — you only pay for data you get:

| Event | Price |
|---|---|
| Message | **$0.0005** ($0.50 per 1,000 messages) |
| Channel info row | **$0.001** |
| Run start | **free** |
| Error rows | free |

Examples: 1,000 messages ≈ **$0.50**. An hourly monitor of 5 channels that finds ~15 new posts per hour costs ≈ **$5.40/month**; runs with nothing new cost **$0**. Set "Max cost per run" in the run options to cap any run.

### FAQ

**Do I need a Telegram account, API ID/hash or bot token?** No. The scraper reads the public web preview that Telegram itself publishes at `https://t.me/s/<channel>` and the public channel page `https://t.me/<channel>`.

**Can it scrape private channels, groups or users?** No. Only public channels with a username and the web preview enabled. Private invite links (`t.me/+...`), private groups and user chats need an account and are out of scope; you get a free `error` row explaining why.

**Why is a video `url` sometimes null?** Telegram's web preview does not serve large videos ("Media is too big"). You still get the thumbnail, duration and the message link.

**Why do some reactions have `custom_emoji_id` instead of an emoji?** Premium custom emoji are images on Telegram's side; we return their id so you can still count and group them.

**How far back can it go?** As far as the channel's public history goes. Use `dateFrom` or raise `maxPagesPerChannel` for full archives (about 20 messages per page).

**How does monitor mode work?** After each run the newest message id of every channel is saved in a named key-value store (`telegram-channel-scraper-monitor`, key = your monitor name). The next run stops as soon as it reaches that id. If a run is capped by `maxMessagesPerChannel`, older unseen posts of that run are skipped — you never pay twice for the same message.

**Telegram search vs exact filter?** `telegramSearch` uses the search box of the public channel page: very fast on huge channels, but it matches words the way the Telegram app does. `localFilter` reads every page and matches substrings anywhere in the text or link preview (best for tickers, URLs, partial words).

**Is it legal?** It only reads information that channel owners chose to publish openly on the web, without logging in or bypassing any protection. You are responsible for how you use the data (e.g. GDPR when processing personal data such as author signatures).

**Is this an official Telegram API?** No, it is an independent tool that reads public pages; it is a simple no-code alternative to the Telegram API for public channel data.

### Limits

- Only **public** channels with the web preview enabled. Private channels, groups, user chats and invite links (`t.me/+...`) are out of scope.
- Very large videos are not served by Telegram's web preview, so some video `url` values are `null` (you still get the thumbnail and message link).
- About 20 messages are read per page; very long archives need a higher *Max pages per channel*.
- Reactions with Telegram Premium custom emoji are returned as `custom_emoji_id`.

### Use it from code (API)

Every Apify Actor is also an API. Replace `<YOUR_API_TOKEN>` with the token from your Apify account (Settings, API & Integrations).

**JavaScript**

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('phantom-x64/telegram-channel-scraper').call({
    "channels": [
      "durov"
    ],
    "maxMessagesPerChannel": 20
  });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient('<YOUR_API_TOKEN>')
run = client.actor('phantom-x64/telegram-channel-scraper').call(run_input={
      "channels": [
        "durov"
      ],
      "maxMessagesPerChannel": 20
    })
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

You can also start it from Make, Zapier, n8n or Google Sheets with the Apify integrations, schedule it, or call it as a tool from an AI agent through the Apify MCP server.

### Support and feedback

Found a bug, need a new field or another source? Open an issue in the **Issues** tab of this Actor page. Reports with a run link are fixed fastest. If it saves you time, a short review on the Store page helps other people find it.

# Actor input Schema

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

Public Telegram channels to scrape. Use the username (durov, @durov) or any link (https://t.me/durov, https://t.me/s/durov, https://t.me/durov/123). Private invite links (t.me/+...) cannot be scraped.

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

Newest messages to save per channel (after date and keyword filters). Set 0 to get only the channel info row.

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

Only messages posted on or after this date. Accepts YYYY-MM-DD, an ISO date-time, or a relative value such as "7 days", "24 hours" or "2 weeks". Leave empty for no limit.

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

Only messages posted on or before this date (a plain YYYY-MM-DD includes the whole day). Accepts the same formats as the oldest date. Leave empty for no limit.

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

Remembers the newest message id of every channel in a named key-value store and, on the next run, returns only messages that are newer. Ideal for scheduled runs (hourly/daily alerts) because you never pay twice for the same message.

## `monitorStateKey` (type: `string`):

Name of the saved monitor state. Use a different name for each independent monitor (for example one per client or schedule). Only used when monitor mode is on.

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

Only return messages that contain at least one of these words or phrases (case-insensitive). Leave empty to get every message.

## `keywordMode` (type: `string`):

"Telegram search" uses the channel search built into the public t.me page: very fast on big channels, but it matches whole words like the Telegram app does. "Exact text filter" reads the channel page by page and keeps messages whose text contains the keyword anywhere (slower, but catches partial words, tickers and URLs).

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

Add one row per channel with title, description, subscriber count, verified badge, photo and media counters.

## `exactSubscriberCount` (type: `boolean`):

Read the exact number of subscribers (e.g. 10157932 instead of 10.2M) from the public channel page. Costs one extra request per channel.

## `maxPagesPerChannel` (type: `integer`):

Safety limit for the number of pages (about 20 messages each) read per channel and keyword. Raise it for full-history archives.

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

How many channels are read at the same time. Each channel is always read page by page at a polite pace.

## `requestDelayMs` (type: `integer`):

Minimum pause between two requests for the same channel, in milliseconds (a small random jitter is added).

## `maxRetries` (type: `integer`):

How many times a failed request (network error, HTTP 429 or 5xx) is retried with a new proxy session and exponential backoff.

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

Apify Proxy is recommended for large or scheduled runs. Datacenter proxies are enough for t.me.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "maxMessagesPerChannel": 50,
  "onlyNewMessages": false,
  "monitorStateKey": "default",
  "keywordMode": "telegramSearch",
  "includeChannelInfo": true,
  "exactSubscriberCount": true,
  "maxPagesPerChannel": 200,
  "maxConcurrency": 3,
  "requestDelayMs": 800,
  "maxRetries": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

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

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

```

## MCP server setup

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