# Telegram Channel Scraper - Messages, Search & Subscribers (`clover_folktale/telegram-scraper`) Actor

Scrape public Telegram channels: channel messages with views, reactions, photos, videos and link previews, keyword search over recent posts, and channel info with subscriber counts. No login, Telegram account or API key needed. Export to JSON, CSV or Excel.

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

## Pricing

from $3.00 / 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 collects data from public Telegram channels and returns clean, flat JSON. It has three jobs:

- Read the message history of any public channel: text, views, reactions, photos, videos, link previews, forwards and replies.
- Search a channel's recent posts by keyword.
- Get channel info with the subscriber count.

It is made for analysts, researchers, marketers and data teams who need Telegram channel data without a Telegram account, API keys, proxies or cookies. Pricing is pay-per-event: **$0.003 per message**, plus a $0.005 start fee for each channel you give it.

### What is a public Telegram channel?

Telegram is a messaging app. A channel is a one-to-many feed: the owner posts messages and subscribers read them. Public channels have a username and a t.me link. This Actor works with public channels only. Private groups, private chats and invite links are not supported. Channel owners can also switch off the web preview, and those channels return nothing.

### What data can you get?

#### Modes

| Mode | Give it | You get |
|---|---|---|
| `channelPosts` (Channel messages) | channels, optional `oldestDate` | the latest messages of each channel, newest first, up to 1,000 per channel |
| `search` (Search channel messages by keyword) | channels and keywords | the messages among the ~100 most recent posts of each channel that contain the keyword |
| `channelInfo` (Channel info) | channels | one record per channel: name, verified, description, subscribers, number of photos, videos and links, picture |

Channel messages are read newest first, 100 per request, back to the channel's first post (the example channel goes back to 2015). You can stop at a number (`maxResults`, up to 1,000 per channel) or at a date (`oldestDate`).

#### Message items (modes `channelPosts` and `search`)

| Field | What it contains |
|---|---|
| `text` | Message text or media caption; null for media without a caption |
| `publishedAt` | Publish time, ISO 8601 UTC |
| `viewCount` | Views of the message |
| `reactionCount` | Sum of all reactions |
| `reactions` | List of reactions: `emoji` (null for custom emoji), `count`, `paid` (true for paid Telegram Stars reactions) |
| `url` | Link to the message |
| `messageType` | text, photo, video, album and so on |
| `photoUrls` | Photo links (temporary; download soon after the run) |
| `videoUrls` | Video links (temporary; download soon after the run) |
| `linkPreviewUrl` | Link of the preview card under the message |
| `linkPreviewTitle` | Title of the preview card |
| `isForwarded` | True for a message forwarded from elsewhere |
| `forwardedFrom` | Where a forwarded message came from |
| `replyToMessageId` | The message in the same channel this one replies to |
| `author` | Post signature or channel name shown on the message |
| `postKey` | Unique key of a message: `<channel>/<messageId>` |
| `messageId` | Message number inside the channel |
| `channel` | Channel username |
| `channelTitle` | Channel name; null in search mode |
| `channelSubscriberCount` | Subscribers of the channel; null in search mode |
| `channelVerified` | Whether the channel carries the verified badge; null in search mode |
| `channelPhotoUrl` | Link to the channel picture; null in search mode |
| `channelDescription` | Channel description as published; null in search mode |
| `keyword` | The keyword that found the message (search mode only) |
| `type` | Always `message` for these items |

#### Channel items (mode `channelInfo`)

| Field | What it contains |
|---|---|
| `subscriberCount` | Subscribers of the channel |
| `title` | Channel name |
| `username` | Channel username |
| `verified` | Whether the channel is verified |
| `description` | Channel description as published |
| `photoCount` | Photos shared in the channel |
| `videoCount` | Videos shared in the channel |
| `linkCount` | Links shared in the channel |
| `photoUrl` | Channel picture link |
| `url` | Link to the channel |
| `type` | Always `channel` for these items |

Counts that Telegram shows as text, such as "10.5M" or "3.42K", are returned as whole numbers. The dataset has two ready-made views: **Messages** (channel, text, publishedAt, viewCount, reactionCount, messageType, url) and **Channels** (username, title, subscriberCount, verified, description, url).

### How to use Telegram Channel Scraper

1. Open the Actor in Apify Console.
2. Choose a mode: Channel messages, Search channel messages by keyword, or Channel info.
3. Add one or more public channels. You can paste usernames or links.
4. For search, add keywords. For channel messages, optionally set an oldest date. Set the maximum results per channel.
5. Click Start and wait for the run to finish.
6. Open the dataset and export it as JSON, CSV or Excel, or read it through the Apify API.

An empty input `{}` also works: it reads the 20 latest messages of the example channel and runs in seconds.

### Input

| Field | Type | Required | What it does | Default |
|---|---|---|---|---|
| `mode` | string | Required | `channelPosts`, `search` or `channelInfo` | `channelPosts` |
| `channels` | array of strings | Optional | Public channel usernames (`durov`), `@durov`, or links (`https://t.me/durov`, `https://t.me/s/durov`, a link to one post) | Falls back to `durov` when empty |
| `keywords` | array of strings | Optional (mode `search`) | Search keywords; each is searched in each channel. Only the ~100 most recent posts of a channel are checked | Falls back to `telegram` when empty |
| `oldestDate` | string (YYYY-MM-DD) | Optional (mode `channelPosts`) | Stop reading a channel at messages older than this date. Leave empty to read back until `maxResults` | empty |
| `maxResults` | integer, 1 to 1000 | Optional | Maximum messages per channel (channel messages: up to 1,000) or per channel and keyword (search: up to 100). Ignored in channel info mode | 20 |

`maxResults` is the limit for each channel (in search: for each channel and keyword). With `oldestDate`, reading of a channel stops at the first message older than that date, even if `maxResults` is not reached; the run then continues with the next channel.

#### Example: channel messages since a date

```json
{
  "mode": "channelPosts",
  "channels": ["durov", "https://t.me/s/telegram"],
  "maxResults": 100,
  "oldestDate": "2026-01-01"
}
```

#### Example: keyword search in recent posts

```json
{
  "mode": "search",
  "channels": ["durov"],
  "keywords": ["privacy", "wallet"],
  "maxResults": 100
}
```

#### Example: channel info and subscriber counts

```json
{
  "mode": "channelInfo",
  "channels": ["durov", "telegram"]
}
```

### Output example

#### Message item (mode `channelPosts`)

The values below are illustrative.

```json
{
  "type": "message",
  "postKey": "examplenews/1234",
  "messageId": 1234,
  "channel": "examplenews",
  "channelTitle": "Example News",
  "channelSubscriberCount": 48200,
  "channelVerified": false,
  "channelPhotoUrl": "https://cdn4.telesco.pe/file/example.jpg",
  "channelDescription": "Daily news digest.",
  "url": "https://t.me/examplenews/1234",
  "messageType": "text",
  "publishedAt": "2026-09-11T16:04:02Z",
  "text": "New report on mobile payments is out ...",
  "viewCount": 15300,
  "author": "Example News",
  "isForwarded": false,
  "forwardedFrom": null,
  "replyToMessageId": null,
  "linkPreviewUrl": "https://example.com/report",
  "linkPreviewTitle": "Mobile Payments Report 2026",
  "photoUrls": [],
  "videoUrls": [],
  "reactionCount": 420,
  "reactions": [{"emoji": "⭐", "count": 120, "paid": true}, {"emoji": null, "count": 300, "paid": false}]
}
```

Search items have the same fields plus `keyword`, and `channelTitle`, `channelSubscriberCount`, `channelVerified`, `channelPhotoUrl` and `channelDescription` are null.

#### Channel item (mode `channelInfo`)

```json
{
  "type": "channel",
  "username": "examplenews",
  "url": "https://t.me/examplenews",
  "title": "Example News",
  "verified": false,
  "description": "Daily news digest.",
  "subscriberCount": 48200,
  "photoCount": 1320,
  "videoCount": 215,
  "linkCount": 890,
  "photoUrl": "https://cdn4.telesco.pe/file/example.jpg"
}
```

### How much does it cost?

The Actor uses pay-per-event pricing. These are the current prices:

| Charge event | Price (USD) | When it is charged |
|---|---|---|
| `run-start` | $0.005 | Channel messages: once for each channel. Search: once for each channel and keyword pair. Channel info: once per run |
| `result` | $0.003 | Each message returned (channel messages and search) |
| `profile` | $0.005 | Each channel found (channel info) |

Worked examples:

- 100 messages from one channel cost $0.005 + 100 × $0.003 = **$0.305**.
- 1,000 messages from one channel cost $0.005 + 1,000 × $0.003 = **$3.005**.
- Channel info for 10 channels costs $0.005 + 10 × $0.005 = **$0.055**.

On the free Apify plan, a run returns at most 20 results in total; any paid Apify plan removes the limit. On the free Apify plan each account can run this Actor up to 3 times per 24 hours and 10 times per 7 days, within a small lifetime free allowance shared across all our Actors, and all free accounts share a daily free budget. When any of these limits is reached, the run stops before fetching anything and nothing is charged.

How charging works:

- The first start fee is charged when the first request to the data source succeeds, even if that request returns nothing. It is not charged when the data source fails from the start or rejects the inputs, or when your spending limit cannot cover it.
- In channel messages and search, each further start fee (for the next channel, or channel and keyword pair) is charged before that input's first request, and it is kept even if that input then fails or returns nothing.
- In channel messages and search, a channel that does not exist, a channel whose owner switched off the web preview, or a keyword with no matches returns nothing but still costs its start fee, because the request was made.
- In channel info, an unknown channel returns nothing and costs no channel fee (`profile`). The run's single start fee is still charged once the first request succeeds, so a channel-info run with only unknown channels costs $0.005.
- If the data source is down from the start, nothing is charged. A start fee already paid for a channel (or channel and keyword pair) is not refunded if the source fails during it.
- You are never charged for items the Actor did not deliver, and you never pay for more than `maxResults` messages per channel.
- The run stops by itself if your spending limit is reached.

### Use cases

- **Market and crypto monitoring**: what announcement channels post, how fast they post, and what gets views and reactions.
- **News and OSINT**: collect a set of public channels into one dataset, with timestamps and links back to the source.
- **Channel research**: compare subscriber counts, posting frequency and engagement across channels in a niche.
- **Brand and topic tracking**: search the recent posts of a list of channels for your brand, product or topic keywords.
- **Source and media analysis**: see which websites a channel links to through `linkPreviewUrl`, and which posts are forwarded through `isForwarded` and `forwardedFrom`.
- **AI and data projects**: Telegram text with clean metadata for classification, summaries and trend analysis.

### Integrations and API

You can start runs from Apify Console or through the Apify API, schedule them (for example, every day to collect new channel messages), and connect the results to Apify integrations and webhooks. Every run stores its results in a dataset you can download as JSON, CSV or Excel. This means you can get Telegram channel data into your own tools without setting up Telegram API keys.

Python example with `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("clover_folktale/telegram-scraper").call(run_input={
    "mode": "channelPosts",
    "channels": ["durov", "https://t.me/s/telegram"],
    "maxResults": 100,
    "oldestDate": "2026-01-01",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

### Tips and limits

- **Any channel format works.** `durov`, `@durov`, `https://t.me/durov`, `https://t.me/s/durov`, or a link to one post.
- **Search is shallow.** There is no full-text search over a whole channel: search looks only at the ~100 most recent posts of each channel and returns the ones that match. For older posts, read the channel with `channelPosts` and `oldestDate` and filter the result.
- **No comments.** Replies in a channel's discussion group are not available. `replyToMessageId` only shows which channel message a post answers.
- **Channels with the web preview switched off return nothing**, and some channels are not public at all. They are skipped (their start fee still applies). In channel messages and search, empty channels or keywords do not stop the run. Private groups and invite links are not supported.
- **Empty fields in search mode:** `channelTitle`, `channelSubscriberCount`, `channelVerified`, `channelPhotoUrl` and `channelDescription` are null. Use `channelInfo` for them.
- **Empty fields in channel messages:** `forwardedFrom`, `replyToMessageId`, `videoUrls`, `linkPreviewUrl` and `linkPreviewTitle` are filled only when the message has them. `text` is null for media without a caption.
- **Numbers are a snapshot.** Views, reactions and subscriber counts are what Telegram shows at the time of the run.
- **Media links expire.** Photo and video links are temporary; download what you need soon after the run.
- **No author IDs.** Telegram does not expose sender or author IDs on channel posts, so there are none to return. `author` is the signature or channel name shown on the post.
- **Free plan:** a run returns at most 20 results in total. Any paid Apify plan removes the limit.
- **How runs stop.** The run stops by itself when your spending limit is reached. In channel info mode only, the run ends as failed after 5 unknown channels, to protect your budget.

### FAQ

#### Do I need a Telegram account or login?

No. You do not need a Telegram account, API keys, proxies or cookies. You only give the Actor public channel names or links.

#### Is it legal to scrape Telegram channels?

The Actor returns only public data: the message, its numbers and links, the author signature shown on the post, plus the channel's public profile (name, verified badge, picture, description, subscriber count). It does not access private chats, private groups or direct messages. The channel description is returned as published, including any business contact info the owner put there. You are responsible for how you use the data and for following the laws that apply to you.

#### How fresh is the data?

The data is read live during each run. Views, reactions and subscriber counts are what Telegram shows at that moment.

#### Why are some fields empty?

In search mode the channel fields are null; run `channelInfo` to get them. Fields such as `forwardedFrom`, `replyToMessageId`, `videoUrls` and the link preview fields are filled only when the message has them.

#### How many messages can I get?

Up to 1,000 messages per channel in channel messages mode, and up to 100 per channel and keyword in search mode. You can give many channels in one run. On the free Apify plan a run returns at most 20 results in total.

#### Can I search a whole channel's history?

No. Search checks only the ~100 most recent posts of each channel. To find older posts, read the channel with `channelPosts` (optionally with `oldestDate`) and filter the dataset yourself.

#### Can I schedule runs and export the data?

Yes. You can schedule runs in Apify, start them through the API, and export each dataset as JSON, CSV or Excel.

#### A field or mode is missing. What can I do?

Open an issue from the Actor page and it will be looked at.

### More tools from the same developer

- [Reddit Scraper - Posts, Comments & User IDs, No Login](https://apify.com/clover_folktale/reddit-scraper)
- [Threads Scraper - Posts, Replies & User Profiles, No Login](https://apify.com/clover_folktale/threads-scraper)
- [YouTube Scraper - Shorts, Channel Videos & Video Search](https://apify.com/clover_folktale/youtube-scraper)
- [TikTok Scraper - Video Search, Hashtags & Author Profiles](https://apify.com/clover_folktale/tiktok-scraper)
- [WeChat Official Account 公众号 Scraper - Articles & Read Counts](https://apify.com/clover_folktale/wechat-scraper)
- [Weibo 微博 Scraper - Posts, Comments, User IDs & IP Location](https://apify.com/clover_folktale/weibo-scraper)

# Actor input Schema

## `mode` (type: `string`):

What to scrape: the messages of channels, a keyword search in channels, or channel info.

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

Public channel usernames (durov), @durov, or links (https://t.me/durov, https://t.me/s/durov, a link to one post). Falls back to "durov" when empty.

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

Search keywords; each is searched in each channel. Telegram has no real search here: only the ~100 most recent posts of a channel are checked. Falls back to "telegram" when empty.

## `oldestDate` (type: `string`):

Stop reading a channel at messages older than this date (YYYY-MM-DD). Leave empty to read back until maxResults.

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

Maximum messages per channel (channel messages: up to 1000) or per channel and keyword (search: up to 100). Ignored in channel info mode. You are charged $0.003 per message returned, plus a $0.005 start fee for each channel (or channel and keyword pair in search). On the free Apify plan a run returns at most 20 results in total; any paid Apify plan removes the limit.

## Actor input object example

```json
{
  "mode": "channelPosts",
  "channels": [
    "durov"
  ],
  "keywords": [
    "privacy"
  ],
  "maxResults": 20
}
```

# Actor output Schema

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

All scraped items (JSON, CSV, Excel via the dataset API).

# 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"
    ],
    "keywords": [
        "privacy"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("clover_folktale/telegram-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"],
    "keywords": ["privacy"],
}

# Run the Actor and wait for it to finish
run = client.actor("clover_folktale/telegram-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"
  ],
  "keywords": [
    "privacy"
  ]
}' |
apify call clover_folktale/telegram-scraper --silent --output-dataset

```

## MCP server setup

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