# Telegram Channel Scraper (`thisisb3/telegram-channel-scraper`) Actor

Scrape posts and channel profiles from public Telegram channels: text, date, views, reactions, media and links, with date filters. No login or Telegram account needed.

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

## Pricing

from $0.48 / 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

Extract **posts and channel profiles from public Telegram channels**: text, date, views, reactions, photos, videos, links, edits and forwards. Scrape many channels in one run, filter by date, and export to JSON, CSV or Excel, or pull the data through the Apify API. No Telegram account, no login, no phone number.

### What does Telegram Channel Scraper do?

Give it a list of public channels (`durov`, `@durov`, `t.me/durov` or `https://t.me/s/durov`). It reads each channel's public web preview, the same page anyone sees at `t.me/s/<channel>` without logging in. It goes back in time page by page until it reaches your post limit or date filter. You get one row per post and, if you turn it on, one profile row per channel with its subscriber count.

### Why use it?

- **Channel monitoring.** Track what news, government, company or community channels publish, and schedule daily runs with "Only posts newer than: 1 day".
- **Threat research and OSINT.** Archive public channel posts with exact timestamps, edit flags, forward sources and outbound links for investigations.
- **Brand monitoring.** Watch channels that talk about your brand, product or industry, and follow views and reactions over time.
- **Crypto and market analysis.** Collect posts from crypto, trading and finance channels with views and reactions for sentiment and event tracking.

What sets it apart:

- **Many channels per run**, processed in parallel.
- **Date filters** that jump straight to the period you want instead of reading every newer page.
- **Clean, typed output:** numbers are numbers (`18.9M` views becomes `18900000`), dates are ISO 8601 (UTC), reactions are structured, and media and links are arrays.
- **No charge for channels it can't read.** Private channels, groups, bots and wrong usernames are skipped with a clear message.

### How to use it

1. Click **Try for free**.
2. Add channel usernames or links to **Channels**.
3. Optionally set **Maximum posts per channel**, a date filter, and **Include channel profiles**.
4. Click **Start**, then download your data from the **Output** tab when the run finishes.

### How much does it cost?

You pay only for results you receive (pay per event):

| Result                              | Price                                      |
| ----------------------------------- | ------------------------------------------ |
| Post (`type: "message"`)            | **$0.60 per 1,000 posts** ($0.0006 each)   |
| Channel profile (`type: "channel"`) | **$2.00 per 1,000 profiles** ($0.002 each) |

Skipped channels (no public preview) are free. Apify's free plan includes monthly credit, enough for thousands of posts. Set a maximum cost per run in the run options and the scraper stops exactly at that budget. The current prices are always shown on the **Pricing** tab.

### Input

| Field                     | Description                                                                                                |
| ------------------------- | ---------------------------------------------------------------------------------------------------------- |
| **Channels** (required)   | Public channels as `username`, `@username`, `t.me/username`, `https://t.me/s/username`, or any post link.  |
| Maximum posts per channel | Newest posts first. Default 100. Set to `0` to get only channel profiles.                                  |
| Only posts newer than     | A date (`2026-01-31`, read as UTC) or a period back from now (`7 days`, `12 hours`, `2 weeks`, `1 month`). |
| Only posts older than     | Same formats. Use both filters to collect a past period.                                                   |
| Include channel profiles  | Adds one profile row per channel. Default off.                                                             |
| Maximum results           | Stop the whole run after this many results (posts + profiles). `0` = no limit. Default 1,000.              |
| Max concurrency           | Channels loaded in parallel (Advanced). Default 5.                                                         |
| Max requests per minute   | Upper limit on page loads per minute (Advanced). Default 120.                                              |
| Proxy configuration       | Optional (Advanced). Not needed for normal runs.                                                           |

Example input:

```json
{
    "channels": ["durov", "https://t.me/s/WatcherGuru", "@tginfo"],
    "maxPostsPerChannel": 30,
    "onlyPostsNewerThan": "14 days",
    "includeChannelInfo": true
}
```

### Output

All results go to one dataset. Every item has a `type`: `"message"` for a post or `"channel"` for a channel profile. The **Messages** and **Channel profiles** views in the Output tab show the columns for each type.

A post (real output from a local run, CDN links shortened):

```json
{
    "type": "message",
    "channelUsername": "WatcherGuru",
    "id": 15387,
    "url": "https://t.me/WatcherGuru/15387",
    "date": "2026-10-07T14:17:53.000Z",
    "text": "JUST IN: 🇺🇸🇨🇳 Webull $BULL crashes 20% after US House panel flags its China ties as a national security risk.\n\n@WatcherGuru",
    "views": 40600,
    "reactions": [
        { "type": "paid", "emoji": "⭐", "customEmojiId": null, "count": 3 },
        { "type": "emoji", "emoji": "🤣", "customEmojiId": null, "count": 308 },
        { "type": "emoji", "emoji": "🤡", "customEmojiId": null, "count": 79 },
        { "type": "emoji", "emoji": "❤", "customEmojiId": null, "count": 56 },
        { "type": "emoji", "emoji": "🔥", "customEmojiId": null, "count": 21 },
        { "type": "emoji", "emoji": "🤯", "customEmojiId": null, "count": 14 }
    ],
    "isEdited": false,
    "isForwarded": false,
    "forwardedFrom": null,
    "replyToUrl": null,
    "media": [
        {
            "type": "photo",
            "url": "https://cdn4.telesco.pe/file/suMArP7_9fo5ESG7XPylbRdR7tQoHbAX98qDfp45...FdA.jpg",
            "thumbnailUrl": null,
            "durationSeconds": null,
            "title": null,
            "details": null
        },
        {
            "type": "photo",
            "url": "https://cdn4.telesco.pe/file/b30fra0-bSAVc0Ragphoqq7qlqUHmU0zgmW90oVIV...xtw.jpg",
            "thumbnailUrl": null,
            "durationSeconds": null,
            "title": null,
            "details": null
        }
    ],
    "links": ["https://t.me/WatcherGuru"],
    "scrapedAt": "2026-10-07T16:24:45.601Z"
}
```

A channel profile:

```json
{
    "type": "channel",
    "channelUsername": "WatcherGuru",
    "url": "https://t.me/WatcherGuru",
    "title": "Watcher Guru",
    "description": "Watcher Guru gives you unparalleled, unbiased coverage of crypto & finance in real-time.\n\n💬 @WatcherGuruChat",
    "subscribers": 629000,
    "photoCount": 5700,
    "videoCount": 718,
    "fileCount": null,
    "linkCount": 2350,
    "isVerified": true,
    "scrapedAt": "2026-10-07T16:24:45.601Z"
}
```

#### Post fields (`type: "message"`)

| Field             | Description                                                                                                                                                                                                          |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channelUsername` | Channel username without `@`.                                                                                                                                                                                        |
| `id`              | Post id within the channel.                                                                                                                                                                                          |
| `url`             | Link to the post.                                                                                                                                                                                                    |
| `date`            | Publish time, ISO 8601 in UTC.                                                                                                                                                                                       |
| `text`            | Post text or caption, with line breaks. `null` for posts without text.                                                                                                                                               |
| `views`           | View count. Telegram rounds large values (for example `18.9M`), so they are approximate.                                                                                                                             |
| `reactions`       | Each reaction as `{ type, emoji, customEmojiId, count }`. `type` is `emoji` (a standard emoji), `custom_emoji` (Telegram shows only the custom emoji's id, not a character) or `paid` (Telegram Stars, shown as ⭐). |
| `isEdited`        | `true` if the post was edited after publishing.                                                                                                                                                                      |
| `isForwarded`     | `true` if the post was forwarded from elsewhere.                                                                                                                                                                     |
| `forwardedFrom`   | `{ name, url }` of the original post when it comes from a public channel, otherwise `null`.                                                                                                                          |
| `replyToUrl`      | Link to the post this one replies to, or `null`.                                                                                                                                                                     |
| `media`           | Photos, videos and other media as `{ type, url, thumbnailUrl, durationSeconds, title, details }`. An album is one post with several items.                                                                           |
| `links`           | Web links in the post text (hashtag and cashtag searches are left out).                                                                                                                                              |
| `scrapedAt`       | When the post was scraped.                                                                                                                                                                                           |

#### Channel profile fields (`type: "channel"`)

| Field                                                | Description                                                                                                            |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `channelUsername`, `url`                             | Username and link of the channel.                                                                                      |
| `title`, `description`                               | Channel name and description.                                                                                          |
| `subscribers`                                        | Subscriber count (rounded by Telegram, for example `10.5M`).                                                           |
| `photoCount`, `videoCount`, `fileCount`, `linkCount` | The media counters shown on the channel page. `null` when Telegram doesn't show the counter, which usually means zero. |
| `isVerified`                                         | `true` if the channel has Telegram's verified badge.                                                                   |
| `scrapedAt`                                          | When the profile was scraped.                                                                                          |

### Tips

- **Monitoring:** schedule the scraper daily with "Only posts newer than: 1 day" (or `25 hours` for some overlap) to get only new posts.
- **Subscriber tracking:** set "Maximum posts per channel" to `0` and turn on profiles. You get one cheap row per channel.
- **Past periods:** set both date filters. The scraper jumps to the older date in a few page loads instead of reading every page.
- **Media links expire.** Photo and video URLs are signed CDN links that stop working after a while, so download what you need soon after the run.
- Start with a small "Maximum posts per channel" to preview the data before a big run.

### What the web preview does not show

This scraper reads only what Telegram shows logged-out visitors, so some data is not available:

- **Forward counts.** Telegram doesn't show how often a post was forwarded. You get `isForwarded` and the source channel instead.
- **Comments and member lists.** They are not on the public preview and are never collected.
- **Exact numbers.** Views, reactions and subscribers are rounded by Telegram (`1.2K`, `3.4M`).
- **Document downloads.** Files show a name and size but no download link.
- Some post types appear on the preview only as "Please open Telegram to view this post". They are returned with their date and views, and empty `text` and `media`.
- **Private channels, groups, bots and user accounts** have no public preview and are skipped.

### Legal and responsible use

**Can I use the data to train AI or machine-learning models?**
No. You may not use this Actor's output to train, fine-tune or evaluate AI or machine-learning models, or to build AI datasets. Telegram's terms (updated 2026-02-03) prohibit any AI or machine-learning use of Telegram content.

**Is it legal to scrape Telegram channels?**
This Actor reads only public pages that Telegram serves to anyone without logging in (`t.me/s/<channel>`). It does not use a Telegram account, the Telegram API, or any login. Telegram's terms of service (updated 2026-02-03) restrict third-party scraping of Telegram data. **You are responsible for making sure your use complies with Telegram's terms and the laws that apply to you**, such as GDPR and copyright law. Post content belongs to the channel owners. If you are unsure, ask a lawyer before you run it.

**What does the scraper do to protect privacy?**

- Only public broadcast channels are read. Usernames of private channels, groups, bots and user accounts are skipped without loading their pages.
- It never collects member or subscriber lists, phone numbers, or user profiles.
- Post author signatures are not collected, and when a post was forwarded from a person (not a public channel), the person's name is left out.
- Requests are paced politely, and the scraper slows down automatically when Telegram asks it to.

**What is it meant for?**
Monitoring public channels, threat research and OSINT, brand monitoring, and crypto or market analysis.

### FAQ

**Why was a channel skipped?**
The log says `Skipping @name: it has no public web preview`. Open `https://t.me/s/name` in a private browser window. If you don't see posts, the channel is private, is a group, bot or user, or the username is wrong. You are not charged for skipped channels.

**I got fewer posts than expected.**
Check the date filters and "Maximum results". Very old posts may have been deleted by the channel. If a public channel shows more posts in your browser than in the output, open an issue on the **Issues** tab.

**Can I get other fields?**
Open an issue on the **Issues** tab with the field you need.

# Actor input Schema

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

Public Telegram channels to scrape. Enter a username (durov), @username, or a link (t.me/durov, https://t.me/s/durov, or a post link). Private channels, groups, bots, user accounts and invite links have no public preview and are skipped without charge.

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

Stop after this many posts from each channel, newest first. Set to 0 to get only channel profiles (turn on "Include channel profiles").

## `onlyPostsNewerThan` (type: `string`):

Keep only posts published on or after this date. Use a date (2026-01-31, read as UTC) or a period counted back from the start of the run (7 days, 12 hours, 2 weeks, 1 month). Leave empty for no limit.

## `onlyPostsOlderThan` (type: `string`):

Keep only posts published before this date, same formats as above. Useful to collect a past period. The scraper jumps to the date instead of reading every newer page.

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

Also save one profile per channel: title, description, subscribers, verified badge and the photo/video/file/link counters. Profiles are charged separately from posts.

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

Stop the whole run after this many results (posts plus profiles). Set to 0 for no limit. You are only charged for results you receive.

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

How many channels to load in parallel. Pages of one channel are always loaded one after another.

## `maxRequestsPerMinute` (type: `integer`):

Upper limit on page loads per minute across the whole run. Each page holds about 20 posts. The scraper also slows down automatically when Telegram answers "Too Many Requests".

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

Optional. Not needed for normal runs. Use proxies only if you scrape very large volumes and get rate-limited.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "maxPostsPerChannel": 20,
  "includeChannelInfo": true,
  "maxItems": 1000,
  "maxConcurrency": 5,
  "maxRequestsPerMinute": 120,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Posts and channel profiles (JSON).

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

The "Messages" table view of the dataset.

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

The "Channel profiles" table view of the dataset.

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

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

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

```

## MCP server setup

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