# Telegram Channel Scraper & Monitor: Posts, Views, Search (`hydrocore/telegram-channel-scraper`) Actor

Scrape public Telegram channels without login: every post with text, date, views as numbers, reactions, links, photos and videos, plus channel profile and exact subscriber count. Keyword search inside channels and 'only new posts' monitoring.

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

## Pricing

from $0.85 / 1,000 posts

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 & Monitor: Posts, Views, Search

Download the posts of any **public Telegram channel** into a spreadsheet: text, date, view count as a real number, reactions, links, photos, videos, files and polls, plus the channel's profile and subscriber count. You can search inside channels by keyword and, on a schedule, get **only the posts that are new since the last run**.

You don't need a Telegram account, phone number, API key or login. The scraper reads the same public web preview anyone can open at `https://t.me/s/<channel>` in a browser.

### What does it do?

You give it one or more channel names, such as `durov`, `@whale_alert_io` or `https://t.me/telegram`. For each channel it saves:

- **Posts** (one row each): post link and number, date, text, **views** (for example "4.24M" becomes `4240000`), each reaction with its count (including paid ⭐ star reactions), links, hashtags, @mentions, $cashtags, photos, videos, files, polls, link previews, forwarded-from and reply-to info, and whether the post was edited.
- **Channel profile** (one row per channel): title, description, verified badge, **exact subscriber count**, photo, and the counts of photos, videos, files and links.

Optional extras:

- **Search inside channels**: uses Telegram's own in-channel search to find only posts that contain your words.
- **Date filters**: posts from a date range or the last N days.
- **Monitoring**: "Only new posts since the last run" remembers what you already received, so scheduled runs return and charge only new posts.
- **Crypto addresses**: finds Ethereum/EVM, Tron and Solana wallet or contract addresses in the post text.

### What can I use it for?

- **Crypto and trading signals**: collect posts from signal, whale-alert or project channels every hour and feed them into a bot or spreadsheet.
- **News and OSINT monitoring**: follow news, government or conflict channels and keep an archive of what was posted and when.
- **Brand and competitor tracking**: see what competitors post, which posts get the most views and reactions, and how fast their audience grows.
- **Marketing research**: compare channels by subscribers, views per post and engagement before buying an ad or partnership.
- **Keyword alerts**: search channels for your brand, a token name or a topic and get only new matches each day.
- **Data for AI and research**: build clean text datasets from public channels.

### How do I use it?

1. Click **Try for free** (or **Start**).
2. Under **Telegram channels**, enter one channel per line: the name (`durov`), `@durov`, or a link (`https://t.me/durov`).
3. Optionally add **Search inside the channels** words, a date range, or **Max posts per channel** (default 100).
4. Click **Start**. Small runs usually finish in under 10 seconds.
5. Open the **Posts** or **Channels** table, or download everything as Excel, CSV, JSON or HTML.

**Monitoring tip:** turn on **Only new posts since the last run**, then create a **Schedule** (for example every hour) in Apify. The first run saves the current posts; every later run gives you only new posts. Use **Monitoring list name** if you run several schedules so their memories don't mix.

### Input

| Field | What it does | Example |
|---|---|---|
| Telegram channels | Public channels, one per line (name, @name or link) | `durov`, `https://t.me/telegram` |
| Search inside the channels | Optional words; only posts found by Telegram's channel search are saved | `bitcoin`, `ETF` |
| Max posts per channel | Default 100; 0 = profile only | `200` |
| Sort messages by | Newest first (default), oldest, most views or most reactions; applied before the limit | `mostViews` |
| Minimum views | Skip posts with fewer views (not charged) | `100000` |
| Max posts in total | Stop the whole run after this many posts | `1000` |
| Posted on or after / before | Date range | `2026-10-01` |
| Or: posted in the last N days | Simple alternative to a date range | `7` |
| Only new posts since the last run | Monitoring mode (see above) | `true` |
| Monitoring list name | Optional name for the monitoring memory | `crypto-signals` |
| Save posts | Turn off to get only channel profiles | `true` |
| Find crypto wallet / contract addresses | Adds `cryptoAddresses` to each post | `false` |
| Max spend for this run (USD) | The run stops before it would cost more | `0.50` |
| Proxy | Optional, normally not needed | off |

Example input:

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

### Output

Every row has `recordType`: `message` (one post) or `channel` (the channel profile). Rows of the same type always have the same columns in the same order, with `null` for anything a post doesn't have, and dates in ISO 8601 UTC. Below is a real post row from a test run on 2026-11-05 (input above; long text, lists and the photo link are shortened):

```json
{
  "recordType": "message",
  "channel": "durov",
  "channelTitle": "Pavel Durov",
  "messageId": 548,
  "publishedAt": "2026-09-11T16:04:02.000Z",
  "text": "🤝 Telegram has become the sponsor of Codeforces — the largest competitive programming platform in the world.\n\n ⚡CodeForces organizes over...",
  "views": 4060000,
  "reactionsTotal": 79800,
  "firstPhotoUrl": null,
  "photoCount": 0,
  "videoCount": 0,
  "documentCount": 0,
  "linkCount": 1,
  "isForwarded": false,
  "forwardedFromName": null,
  "forwardedFromUrl": null,
  "isReply": false,
  "replyToUrl": null,
  "replyToAuthor": null,
  "replyToText": null,
  "linkPreviewUrl": "https://codeforces.com/blog/entry/156620",
  "linkPreviewSiteName": "Codeforces",
  "linkPreviewTitle": "Telegram Returns as Title Sponsor of Codeforces!",
  "linkPreviewDescription": "Hi, Codeforces!",
  "linkPreviewImageUrl": "https://cdn4.telesco.pe/file/J3y4bSZ0MHHlOkGwzSlBok60Tw37kivBKJ7T8Anxhp0FMCBDS0J9Oi1qLkbKZ5ZQftp0vkQk6oWOPOwAHj7Uhz-_8DHPVW3bLnnystop-iVE...",
  "pollQuestion": null,
  "pollType": null,
  "pollOptions": null,
  "edited": false,
  "hasUnsupportedMedia": true,
  "mediaGroup": false,
  "url": "https://t.me/durov/548",
  "viewsText": "4.06M",
  "reactions": [
    {
      "emoji": "⭐",
      "customEmojiId": null,
      "paidStars": true,
      "count": 21900,
      "countText": "21.9K"
    },
    {
      "emoji": null,
      "customEmojiId": "5399847211989246390",
      "paidStars": false,
      "count": 36900,
      "countText": "36.9K"
    },
    "..."
  ],
  "photos": null,
  "videos": null,
  "documents": null,
  "links": [
    "https://codeforces.com/blog/entry/156620"
  ],
  "hashtags": null,
  "mentions": null,
  "cashtags": null,
  "cryptoAddresses": null,
  "isNew": null,
  "searchKeyword": null,
  "scrapedAt": "2026-11-05T04:31:08.417Z"
}
```

And the channel profile row from the same run:

```json
{
  "recordType": "channel",
  "channel": "durov",
  "channelTitle": "Pavel Durov",
  "subscribers": 10125968,
  "subscribersExact": true,
  "subscribersText": "10.1M",
  "verified": true,
  "description": "Founder of Telegram.",
  "photoUrl": "https://cdn4.telesco.pe/file/AM3EaKY4OWoCIpqIJGL2G4hcjBupbm4OuzSyAnEGF2gBOPJJIYOcnXbjDp...",
  "photosCount": 102,
  "videosCount": 47,
  "filesCount": null,
  "linksCount": 200,
  "url": "https://t.me/durov",
  "scrapedAt": "2026-11-05T04:31:08.379Z"
}
```

**Views.** The Output tab opens in **Overview** (photo, channel, date, text, views, reactions, link; profile rows show the title and subscribers). There are also **Engagement**, **Media and links**, **Forwards and replies**, **Channels** and **Detailed** (all fields) views. In views that mix both row types, the columns of the other type are just empty.

**Sorting and filters.** *Sort messages by* (newest, oldest, most views, most reactions) is applied before *Max posts per channel*, so "top 10 most viewed in the last 30 days" really is that. *Minimum views* skips smaller posts without charging for them.

> Changed in v1.1: `type` → `recordType`, `date` → `publishedAt`. The nested `forwardedFrom`, `replyTo`, `linkPreview` and `poll` objects are now flat columns (`forwardedFromName`, `forwardedFromUrl`, `replyToUrl`, `replyToAuthor`, `replyToText`, `linkPreviewUrl`/`SiteName`/`Title`/`Description`/`ImageUrl`, `pollQuestion`, `pollType`, `pollOptions`), plus new `isForwarded`, `isReply`, `firstPhotoUrl`, `photoCount`, `videoCount`, `documentCount`, `linkCount`. Empty lists are now `null`. New inputs *Sort messages by* and *Minimum views*.

The run also saves a `RUN_SUMMARY` record (key-value store) with, per channel, how many posts were saved, how many pages were read, and any problems in plain English.

### How much does it cost?

You pay per result (pay-per-event). Apify platform usage is included.

| Event | Price | When |
|---|---|---|
| Run start | $0.0005 | Once per run |
| Post | $0.00085 | Each post saved ($0.85 per 1,000) |
| Channel profile | $0.0015 | Once per valid channel per run |

Worked examples (based on our test runs on 4 November 2026, at current prices):

| Run | Calculation | Total |
|---|---|---|
| 2 channels, 20 posts each | $0.0005 + 40 × $0.00085 + 2 × $0.0015 | **$0.0375** |
| 1 channel, 120 posts | $0.0005 + 120 × $0.00085 + $0.0015 | **$0.104** |
| 1,000 posts from 1 channel | $0.0005 + 1,000 × $0.00085 + $0.0015 | **$0.852** |
| Hourly monitoring run with no new posts | $0.0005 + 1 channel profile | **$0.002** |
| 3 channel profiles only | $0.0005 + 3 × $0.0015 | **$0.005** |
| Channel that doesn't exist or is private | run start only | **$0.0005** |

Posts outside your date range, posts you already received in monitoring mode and duplicate posts are never charged. Set **Max spend** (or Apify's "Maximum cost per run") and the run stops cleanly before going over. The run start fee counts toward that limit.

### How does it compare?

Other Telegram scrapers on the Apify Store, from their public Store listings checked on **4 November 2026**. Prices and ratings change, so please check the listings for current figures.

| Actor | Price | Rating |
|---|---|---|
| **This actor** | **$0.00085 per post, $0.0015 per channel, $0.0005 per run** | new |
| automation-lab/telegram-scraper | $0.005 start + $0.001 per message + $0.002 per channel | 5.0 (2 reviews) |
| lofomachines/telegram-keyword-search-scraper | $0.0025 per result | 5.0 (1 review) |
| tri_angle/telegram-scraper | $0.005 per result | 2.81 (17 reviews) |
| khadinakbar (Telegram scraper) | $0.002 per result | — |
| fetch_cat (Telegram scraper) | $0.005 start + $0.00045 per result | — |
| truefetch/telegram-channel-message | $0.00035 per result | 3.11 (4 reviews) |

Why we think this one is a good choice:

- **Competitive pricing** with pay-only-for-useful-results — out-of-range, duplicate and already-seen monitoring posts are free.
- **Views and reactions as real numbers** (`4240000`, not "4.24M"), ready for sorting and charts. The original text is kept too.
- **Exact subscriber counts** from the channel page, not rounded ones.
- **Monitoring mode** that charges only for new posts, built for hourly schedules.
- **In-channel keyword search** using Telegram's own search.
- **Fast and light**: plain web requests, no browser. In our tests, 40 posts from 2 channels took under 5 seconds.
- **Fails softly**: a wrong channel name is skipped (not charged) with a clear message; the other channels still run.

### FAQ

**What is a "scraper"?**
A program that opens public web pages and copies the information into a table for you. This one opens Telegram's public channel previews and turns each post into a row.

**Is it legal to scrape Telegram channels?**
The scraper only reads public channels that Telegram itself publishes on the web at t.me/s/, which anyone can open without an account. It does not log in, join channels, read private chats or groups, or get around any access control. Posts can contain personal data (for example names in the text), so you are responsible for using the data lawfully, for example under the GDPR, and for respecting copyright and Telegram's terms. If in doubt, ask a lawyer.

**Do I need a Telegram account, API key or proxy?**
No. A proxy is optional; turn it on only if you see repeated "too many requests" messages in the log.

**Why doesn't my channel work?**
Only public channels with a web preview work. Private channels (invite links like `t.me/+abc`), groups, personal accounts and bots have no public preview, and some channels turn the preview off. These are skipped with a note in the log and are not charged; only the $0.0005 run fee applies.

**Why are view counts slightly rounded?**
Telegram's web preview shows views rounded (for example 4.24M). We convert that to a number (4240000); the exact figure isn't public. Subscriber counts, by contrast, are exact.

**How does monitoring work?**
With **Only new posts since the last run** on, the scraper remembers the newest post number it gave you for each channel (and keyword). The next run returns only newer posts, and you pay only for those plus the channel profile ($0.0015) and the $0.0005 run fee. The very first run works like a normal run and saves the starting point.

**Can I get very old posts?**
Yes. The scraper pages back through the channel's history until it reaches your **Max posts** or date limit.

**How fast is it?**
In our tests, 40 posts from 2 channels took under 5 seconds and 120 posts from one channel about 7 seconds. It waits a little between pages on purpose so Telegram stays happy.

### Limits

- Public channels with a web preview only: no private channels, groups, comments, user accounts or bots.
- Views are rounded the way Telegram shows them; reactions with custom emoji give the emoji id, not a picture.
- Some media (for example some stickers, round videos or very large files) are not shown in the web preview. Those posts have `hasUnsupportedMedia: true` and a link to the post.
- Telegram's in-channel search decides what matches a keyword; it may miss word forms or match parts of words.
- Each request has a short time limit and a run stops cleanly after about 60 seconds of failed requests in total, or after 45 minutes, saving what it has.
- Telegram can change its web preview without notice. If something breaks, please open an issue and we'll fix it quickly.

### Support

Found a bug or need a field we don't have yet? Open an issue on the **Issues** tab and include the run link.

# Actor input Schema

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

Public channels, one per line. Use the name (durov), @durov or the link (https://t.me/durov or https://t.me/s/durov). Only public channels with a web preview work. Private invite links (t.me/+...), groups, users and bots are skipped for free.

## `searchKeywords` (type: `array`):

Leave empty to get the latest posts. Add words to use Telegram's own search inside each channel, for example bitcoin or "price alert". Each post is saved once, with the keyword that found it.

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

Stop after this many posts for each channel, newest first. 0 = channel profiles only (no posts).

## `maxMessagesTotal` (type: `integer`):

Optional overall limit for the whole run. 0 or empty = no overall limit.

## `sortBy` (type: `string`):

Order of the saved messages for each channel. Newest first is fastest. Other orders are applied before 'Max messages per channel': the Actor reads up to max(300, 5 × that limit) messages (at most 3,000, inside the date filter) for free, sorts them and saves the top ones.

## `minViews` (type: `integer`):

Only save messages with at least this many views. Skipped messages are not charged. 0 = no filter.

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

Only posts from this date on (YYYY-MM-DD, UTC). The scraper stops paging back once it reaches older posts. Leave empty for no limit.

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

Only posts up to this date (YYYY-MM-DD, UTC). Leave empty for today.

## `lastDays` (type: `integer`):

Shortcut for a moving window, handy for scheduled runs: 7 = posts from the last 7 days. Overrides the two dates above. 0 or empty = off.

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

Remember the newest post you received from each channel (and each keyword) and next time save only newer posts, so you never pay twice for the same post. The first run saves up to 'Max posts per channel' (your starting point). Schedule the Actor hourly or daily to get a feed of new posts. Memory is kept in your account's key-value store telegram-channel-monitor.

## `monitorKey` (type: `string`):

Optional. By default the memory is tied to the exact channels and keywords. Set a name (for example crypto-news) to keep the same memory even if you edit the list.

## `includeMessages` (type: `boolean`):

Switch off to save only one channel profile row per channel (title, description, exact subscriber count). Handy for daily subscriber tracking.

## `extractCryptoAddresses` (type: `boolean`):

Adds a cryptoAddresses list to each post with EVM (0x...), Solana and TRON-looking addresses found in the text. Cashtags such as $BTC are always extracted. Free.

## `maxSpendUsd` (type: `string`):

Optional, for example 1.50. The run stops before going over it. Apify's own "Maximum cost per run" option is respected too; the lower of the two applies. Leave empty for no extra limit.

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

Not needed for normal use. Only if Telegram starts answering 'too many requests' to the server, enable Apify Proxy and the Actor switches to a fresh IP. Proxy traffic is billed by Apify to your account.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "https://t.me/telegram"
  ],
  "maxMessagesPerChannel": 20,
  "sortBy": "newest",
  "minViews": 0,
  "onlyNewMessages": false,
  "includeMessages": true,
  "extractCryptoAddresses": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `messages` (type: `string`):

Channel, date, text, views, reactions, links and media.

## `engagement` (type: `string`):

Views and reactions per message.

## `media` (type: `string`):

Photos, videos, files, link previews and polls.

## `forwards` (type: `string`):

Forward sources and replies.

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

Channel title, description and subscriber count.

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

Every post and channel profile, all fields.

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

Per channel: posts saved, pages read, keyword matches, problems and total charged.

# 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",
        "https://t.me/telegram"
    ],
    "maxMessagesPerChannel": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("hydrocore/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",
        "https://t.me/telegram",
    ],
    "maxMessagesPerChannel": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("hydrocore/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",
    "https://t.me/telegram"
  ],
  "maxMessagesPerChannel": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call hydrocore/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

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