# Telegram Channel Scraper: Posts, Views, Reactions, Alerts (`nat-labs/telegram-channel-scraper`) Actor

Scrape public Telegram channels without login or API keys: post text, exact views, reactions incl. paid stars, media, polls, forwards, replies, links, plus exact subscriber counts. Many channels per run, date window, keyword filters, only-new-posts monitoring. $0.30 / 1,000 posts, no start fee.

- **URL**: https://apify.com/nat-labs/telegram-channel-scraper.md
- **Developed by:** [Nat | Data Tools](https://apify.com/nat-labs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.22 / 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.
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: Posts, Views, Reactions, Alerts

Scrape **public Telegram channels** into clean JSON, CSV or Excel: post text, **exact view counts, every reaction (including paid Telegram Stars)**, photos, videos, files, polls, link previews, forwards, replies, hashtags, mentions and links, plus a channel row with the **exact subscriber count**. Many channels per run, date windows, keyword filters, and **only-new-posts monitoring** for alerts. No login, no phone number, no API keys, no proxy to set up.

**$0.30 per 1,000 posts, no start fee. You never pay for channels that can't be read or for posts your filters drop.**

### Why this Telegram scraper

| | This Actor | Typical Telegram scrapers |
|---|---|---|
| Login | **None.** Reads the public channel preview at t.me/s/… that anyone can open in a browser. | Many need your phone number, an api_id/api_hash or a session string, and can get your account limited. |
| Price | **$0.30 / 1,000 posts**, down to $0.22 on higher plans. **No start fee.** | $0.35–5 / 1,000, often plus a $0.01 start fee on every run, which makes hourly monitoring expensive. |
| Monitoring | **Only new posts since the last run**, per channel. A channel with nothing new costs $0.0005. | Re-download the same posts every run and pay again. |
| Numbers | Views as integers (`1060000`, not `"1.06M"`), every reaction with its count, paid Stars separated, **exact subscribers** (`10337782`). | Strings like "1.2K", reactions missing, rounded subscriber counts. |
| Many channels | Paste hundreds of channels; usernames, @handles and t.me links all work. | One channel per run, or strict URL formats. |
| Filters before billing | Date window, keywords, minimum views, media type, forwarded or not, in-channel search. **Filtered-out posts are free.** | Download everything, filter later, pay for all of it. |
| Errors | Channels that don't exist, bots, users and groups without a preview are listed with a reason, **free**. | Silent empty runs, or charged anyway. |
| Spending | Stops cleanly at your **maximum cost per run**. | Overruns or opaque fees. |

### What you can do

- **News and media monitoring**: follow news, politics and industry channels and get new posts within the hour.
- **Crypto and trading signals**: watch project, exchange and signal channels; filter by keywords like a ticker.
- **Brand and competitor tracking**: see what competitors announce and how their audience reacts (views, reactions, paid Stars).
- **Research and OSINT**: archive public channels for a date range, with forwards and replies to map who reposts whom.
- **Content analysis and AI**: feed clean post text, links and engagement into dashboards, sentiment analysis or LLM pipelines.
- **Influencer and ad vetting**: check real subscriber counts and average views before you buy an ad post.
- **AI agents**: call it from Claude, ChatGPT or Cursor through Apify's AI integrations. Input is lenient: `"durov"`, `"@telegram"`, `"https://t.me/s/tginfo"` all work.
- **Your own stack**: run it from the Apify API, the Python or JavaScript client, Make, Zapier, n8n, Google Sheets, Slack, webhooks or a schedule; export JSON, CSV or Excel.

### Quick start

#### 1. Latest posts from a few channels

```json
{ "channels": ["telegram", "durov", "https://t.me/tginfo"], "maxPostsPerChannel": 50 }
```

#### 2. Everything from the last 30 days

```json
{ "channels": ["durov"], "lastDays": 30, "maxPostsPerChannel": 0 }
```

#### 3. Alerts: only new posts, every hour

```json
{
    "channels": ["channel_one", "channel_two", "channel_three"],
    "onlyNew": true,
    "monitorName": "my-alerts",
    "maxPostsPerChannel": 100
}
```

Schedule it hourly and connect a webhook, Slack or email integration. The first run delivers the newest posts as a baseline; every later run delivers only posts published since.

#### 4. Keyword and engagement filters

```json
{
    "channels": ["durov", "tginfo"],
    "keywordsAny": ["ton", "wallet"],
    "minViews": 100000,
    "excludeForwarded": true,
    "dateFrom": "2026-01-01"
}
```

#### 5. Search inside a channel

```json
{ "channels": ["telegram"], "searchQuery": "stories", "maxPostsPerChannel": 100 }
```

### Output

#### Post

```json
{
    "type": "post",
    "channel": "durov",
    "postId": 552,
    "url": "https://t.me/durov/552",
    "date": "2026-10-11T14:52:40.000Z",
    "isEdited": false,
    "author": "Pavel Durov",
    "text": "✨ Designers × AI\n\n🆕 The mockup era is over…",
    "views": 1060000,
    "reactions": [
        { "emoji": "⭐", "emojiId": null, "isPaid": true, "count": 5730 },
        { "emoji": "❤", "emojiId": null, "isPaid": false, "count": 12500 }
    ],
    "reactionsTotal": 18230,
    "paidStars": 5730,
    "forwardedFrom": null,
    "replyTo": null,
    "media": [{ "type": "video", "url": null, "thumbnailUrl": "https://cdn4.telesco.pe/file/…jpg", "duration": 75 }],
    "poll": null,
    "linkPreview": null,
    "buttons": [],
    "hashtags": [],
    "mentions": ["designers"],
    "links": ["https://t.me/designers"],
    "channelTitle": "Pavel Durov",
    "scrapedAt": "2026-10-18T12:00:00.000Z"
}
```

- `media[]`: `photo`, `video`, `gif`, `round_video`, `document` (file name and size), `voice`, `sticker`, `location`. Direct media URLs are included when Telegram shows the file in the preview; very large videos show a thumbnail and duration only.
- `poll`: question, type, options with percentages, voters.
- `linkPreview`: URL, site name, title, description, image.
- `forwardedFrom`: name, author, channel and post when public. `replyTo`: the replied post's ID, author and text.
- `reactions`: standard emoji come as characters; Telegram custom emoji come as `emojiId`; paid Stars are flagged `isPaid`.
- With **Include post HTML** each post also has `html` (bold, links, quotes kept). In monitoring mode each post has `isNew`.

#### Channel

```json
{
    "type": "channel",
    "channel": "durov",
    "status": "ok",
    "title": "Pavel Durov",
    "username": "durov",
    "description": "Founder of Telegram.",
    "verified": true,
    "subscribers": 10337739,
    "subscribersRounded": 10300000,
    "photos": 102,
    "videos": 47,
    "files": null,
    "links": 200,
    "avatarUrl": "https://cdn4.telesco.pe/file/…jpg",
    "previewAvailable": true,
    "latestPostId": 552
}
```

Channels that can't be read are listed with `status: "not-found"` or `"preview-unavailable"` (a user, bot or group) and a plain-language `statusMessage`. They are free.

### Pricing

Pay per event. **Platform usage is included**: no start fee, no compute or proxy bill on top.

| Event | What it is | Price |
|---|---|---|
| Post | One post delivered (after your filters) | **$0.30 / 1,000** ($0.0003) |
| Channel info | One channel row: title, description, exact subscribers, counters | $1.00 / 1,000 |
| Monitor check | Monitoring mode: a channel checked, nothing new | $0.50 / 1,000 checks ($0.0005) |

Lower rates apply automatically on higher Apify plans (posts down to $0.22 / 1,000, checks down to $0.0003). **Never charged:** channels without a public preview, failed channels, invalid links, filtered-out posts and posts outside your date window.

Examples:

- 20 latest posts from 10 channels = 200 posts + 10 channel rows = **$0.07**.
- A 30-day archive of a busy channel (≈ 3,000 posts) = **$0.90**.
- Hourly monitoring of 10 channels for a month, ~5 new posts per channel per day: 1,500 posts ($0.45) + ~5,700 checks with nothing new ($2.85) ≈ **$3.30 / month**, with no start fees.

The Actor checks your run's **maximum cost** before pushing posts and stops cleanly when it is reached, so you never pay more than you set.

**Free Apify plan:** up to 200 posts from 10 channels per run (a limit set by the developer, not by Apify). Any paid plan removes it.

### Good to know

- **Public channels only.** The Actor reads the same public web preview anyone can open at `https://t.me/s/<channel>`. Private channels, invite links, groups, chats and member lists are not accessible and not supported.
- **Some channels turn the preview off.** They show up as `preview-unavailable` and are free.
- **Views and reactions are rounded by Telegram** on the preview (e.g. `1.06M`). They are converted to numbers; the exact subscriber count comes from the channel page.
- **History depth.** The preview pages back through the whole public history, 20 posts per page. A scan stops after 10,000 posts per channel unless a date window or your post limit stops it earlier.
- **Custom emoji reactions** have no standard character; they come as `emojiId`.
- **Polite by design.** Requests are paced and retried with backoff, so runs don't hammer Telegram.

### Is it legal to scrape Telegram channels?

This Actor reads only **publicly available** channel previews that Telegram serves to anyone without signing in. It does not log in, does not use a Telegram account or API session, does not access private chats, groups or member lists, and does not bypass any protection. t.me publishes no robots.txt restrictions for these pages. Posts can still contain personal data; you are responsible for using the results lawfully (for example under GDPR) and in line with Telegram's terms. For commercial redistribution of content, consult a lawyer.

### FAQ

**Do I need a Telegram account, phone number or API key?** No. Nothing to set up.

**Can it read private channels or groups?** No. Only public channels with a web preview.

**How fresh is the data?** Live: every run reads the channel at that moment. With monitoring on a schedule, new posts arrive at the next run.

**Can I get comments?** Comments live in linked discussion groups, which have no public preview, so they are not included. `replyTo` covers replies inside the channel.

**What happens when Telegram changes the page?** Each run reports per-channel status and fails loudly instead of returning silent empties, so changes are caught and fixed fast. Open an issue in the Issues tab.

# Actor input Schema

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

Public channels, one per line: <code>durov</code>, <code>@telegram</code>, <code>https://t.me/tginfo</code> or <code>https://t.me/s/bloomberg</code>. Private invite links (<code>t.me/+…</code>) can't be read without joining and are skipped.

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

Newest posts first. <code>0</code> = all posts in the date window (whole history if no dates are set; up to 10,000 scanned posts per channel).

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

Shortcut for <i>Posts from</i>: e.g. <code>7</code> = posts from the last 7 days. 0 = no limit.

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

Oldest post date to include (YYYY-MM-DD or ISO time). Scanning stops when older posts are reached.

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

Newest post date to include. Newer posts are skipped (not charged).

## `onlyNew` (type: `boolean`):

Remember the last post delivered per channel and, on the next run, deliver only newer posts. Schedule the Actor hourly or daily and connect a webhook, Slack or email integration for alerts. The first run delivers the newest posts as the baseline. A channel with nothing new costs one cheap <i>monitor check</i>.

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

Keeps separate memories for different schedules (e.g. <code>crypto-news</code>, <code>competitors</code>).

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

Telegram's own in-channel search, e.g. <code>bitcoin</code> or a hashtag <code>#news</code>. Only matching posts are scanned.

## `keywordsAny` (type: `array`):

Keep posts whose text, link preview, poll or file name contains any of these words (case-insensitive).

## `keywordsNone` (type: `array`):

Drop posts containing any of these words.

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

Drop posts with fewer views.

## `mediaTypes` (type: `array`):

Keep only posts containing any of these. Empty = all posts.

## `excludeForwarded` (type: `boolean`):

Drop posts forwarded from other channels or users.

## `onlyForwarded` (type: `boolean`):

Keep only forwarded posts (useful to map which channels a channel reposts).

## `includeServiceMessages` (type: `boolean`):

Also deliver service messages such as <i>Channel photo updated</i> or <i>Channel name was changed</i>.

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

One row per channel: title, description, exact subscriber count, verified flag, number of photos/videos/files/links, avatar.

## `exactSubscribers` (type: `boolean`):

Reads the channel's landing page for the exact number (e.g. 10,337,782 instead of 10.3M). One extra light request per channel.

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

Adds the original formatted HTML of each post (bold, links, quotes) next to the plain text.

## `beforePostId` (type: `integer`):

Backfill: start from posts older than this ID (e.g. continue a previous export).

## `maxItems` (type: `integer`):

Stop the whole run after this many posts. 0 = no limit. The run also always stops cleanly at your <i>maximum cost per run</i>.

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

Requests are paced politely either way; more parallel channels mainly help when some channels need many pages.

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

Not needed: t.me serves public channel previews to everyone. Only set one for very large jobs.

## Actor input object example

```json
{
  "channels": [
    "telegram",
    "durov"
  ],
  "maxPostsPerChannel": 20,
  "onlyNew": false,
  "monitorName": "default",
  "excludeForwarded": false,
  "onlyForwarded": false,
  "includeServiceMessages": false,
  "includeChannelInfo": true,
  "exactSubscribers": true,
  "includeHtml": false,
  "maxItems": 0,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Post rows (type=post) and channel rows (type=channel).

## `statistics` (type: `string`):

Per-run counters, channels without a preview or failed with reasons, requests and bytes.

# 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"
    ],
    "maxPostsPerChannel": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("nat-labs/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",
    ],
    "maxPostsPerChannel": 20,
}

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

```

## MCP server setup

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