# Telegram Channel & Group Scraper (`makework36/telegram-channel-scraper`) Actor

Scrape public Telegram channels and groups. Extract messages, media links, view counts, forwarded info, and member counts. No API key or login required — works with public channels only via t.me web preview.

- **URL**: https://apify.com/makework36/telegram-channel-scraper.md
- **Developed by:** [deusex machine](https://apify.com/makework36) (community)
- **Categories:** Social media
- **Stats:** 82 total users, 10 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Telegram Channel & Group Scraper — Public Messages, Views & Media API

> ⭐ **Useful?** [Leave a review](https://apify.com/makework36/telegram-channel-scraper/reviews) — it takes 10 seconds and is the single biggest thing that helps other OSINT analysts, crypto researchers, media-monitoring teams and social-listening vendors find this Telegram scraper.

A fast, lightweight **Telegram channel scraper** that extracts messages, view counts, media URLs (photos, videos, documents, voice notes), forward attributions, reply threads, channel descriptions and member counts from any public Telegram channel or group. No Telegram account, no MTProto, no bot token, no phone-number SMS login. Just pass a list of usernames (`durov`, `telegram`, `bbcnews`) or `t.me` URLs and get back structured JSON ready to drop into Postgres, Elasticsearch, BigQuery or your SIEM.

Built for OSINT investigators, crypto-signal researchers, news and media-monitoring teams, brand-safety vendors, academic researchers studying political discourse, and growth teams tracking competitor Telegram channels. Uses raw HTTP requests against the public `t.me/s/<channel>` web preview — so it's an order of magnitude cheaper and more stable than solutions that maintain a persistent Telegram session.

### ✨ Why use this scraper

Given a list of public channel or group identifiers — full URLs like `https://t.me/durov`, the preview variant `https://t.me/s/telegram`, `@username` handles or plain usernames — this scraper fetches each channel's public web preview, paginates backward through message history using the Telegram `?before=<id>` cursor, parses each message block with [Cheerio](https://cheerio.js.org/) and emits one dataset item per message.

Because it uses pure HTTP requests (no headless browser, no Puppeteer, no Playwright, no MTProto sessions) a run of 100 messages on one channel finishes in roughly 20 seconds and costs a fraction of a cent. The scraper is also safe from the account-ban risk that comes with Telegram API clients — it never authenticates, it never touches the MTProto protocol, it only reads what the public web preview exposes.

Telegram exposes three ways to read channel data:

1. **Bot API** — requires adding your bot as an administrator of the channel. Useless for scraping public channels you don't own.
2. **MTProto** (via `tdlib`, `telethon`, `gramjs`) — needs a phone number, an app ID, an `api_hash` and a persistent session file. Telegram periodically flags or bans accounts that scrape aggressively, and rebuilding a session is painful.
3. **t.me web preview** (what this actor uses) — the public page at `https://t.me/s/<channel>` that renders recent messages without any login. No account risk. No rate-limit games with Telegram's core protocol.

For OSINT, media-monitoring and competitive-intelligence workflows, option 3 is usually the right trade-off: you give up access to private channels, but you gain stability, repeatability and zero account-ban risk. This actor is a production-ready wrapper around option 3.

### 📤 Output fields

One item per message. Flat JSON that maps 1:1 to a SQL column layout, so it drops straight into Postgres, Snowflake, BigQuery or ClickHouse.

#### Channel metadata

| Field | Type | Description |
|-------|------|-------------|
| `channel` | string | Channel username slug, e.g. `durov` |
| `channelTitle` | string | Display name as shown in the Telegram header |
| `channelMembers` | integer | Subscriber / member count (parsed from strings like `"1.2M subscribers"`) |
| `channelDescription` | string | Channel bio text from the info panel |

#### Message fields

| Field | Type | Description |
|-------|------|-------------|
| `messageId` | integer | Numeric message ID within the channel |
| `date` | string | ISO 8601 timestamp of the message |
| `text` | string | Full message body (plain text, HTML stripped) |
| `views` | integer | View count (Telegram aggregates are parsed from `"4.8M"` / `"120K"` notation) |
| `url` | string | Canonical link `https://t.me/<channel>/<messageId>` |

#### Media attachments (enabled when `includeMedia=true`)

| Field | Type | Description |
|-------|------|-------------|
| `mediaType` | string | `photo`, `video`, `document` or `voice` — null if no media |
| `mediaUrl` | string | Direct URL to the media asset when Telegram exposes it |

#### Thread and social context

| Field | Type | Description |
|-------|------|-------------|
| `forwardedFrom` | string | Name of the original channel when the message was forwarded |
| `author` | string | Author name — relevant in group chats where multiple users post |
| `replyTo` | string | First 100 characters of the message being replied to |

#### Run metadata

| Field | Type | Description |
|-------|------|-------------|
| `scrapedAt` | string | ISO 8601 timestamp of when the message was extracted |

### 🎯 Use cases

- **Crypto signal research** — pull every post from a list of trading-signal channels, parse the ticker and direction, backtest the signal accuracy over weeks and months. Quantify whether the "pro" signal channels beat buy-and-hold.
- **OSINT and threat intelligence** — monitor public channels run by threat actors, scam operations or extremist groups. Feed messages into an IOC extraction pipeline or Maltego investigation.
- **News media monitoring** — aggregate posts from regional news channels (Russia-Ukraine coverage, Brazilian politics, Iranian protest channels). Many newsrooms treat Telegram as primary-source social media.
- **Brand safety** — check whether ads or affiliate links attributed to your brand show up on disinformation or pirate-content channels.
- **Competitive intelligence** — many B2B and consumer brands post product news on Telegram before blog posts. Track what competitors announce, when, and with what engagement.
- **Influencer analytics** — measure view counts, posting cadence and forward ratios across lifestyle, fashion or tech Telegram creators.
- **Academic research** — reproducible Telegram datasets for studies on platform moderation, political discourse, misinformation networks and social movements.
- **Training data for LLMs and classifiers** — public Telegram posts in underrepresented languages are valuable for small-model fine-tuning.

### 🚀 How to use

#### Mode 1 — scrape the last N messages from a public channel

```json
{
    "channels": ["durov", "https://t.me/s/telegram"],
    "maxMessages": 50,
    "includeMedia": true
}
```

#### Mode 2 — scrape a specific date range

Pass `dateFrom` / `dateTo` in `YYYY-MM-DD` format. The scraper paginates backward until it hits `dateFrom`.

```json
{
    "channels": ["bbcnews"],
    "maxMessages": 500,
    "dateFrom": "2026-01-01",
    "dateTo": "2026-01-31"
}
```

#### Mode 3 — scrape everything the public preview exposes

Set `maxMessages` to `0` for unlimited. Telegram's web preview typically caps at around 1,000–2,000 older messages per channel, so in practice this still terminates naturally.

```json
{
    "channels": ["my_niche_channel"],
    "maxMessages": 0,
    "includeMedia": true
}
```

#### Mode 4 — monitor a portfolio of channels

Drop a list of 20–50 channels. Each channel runs sequentially with a 1.5-second pause between pagination hits to stay friendly with Telegram's infrastructure.

```json
{
    "channels": [
        "durov", "telegram", "bbcnews", "durov_russia",
        "cryptosignals_official", "tradingview_en"
    ],
    "maxMessages": 100,
    "includeMedia": true
}
```

### 📥 Input

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `channels` | array | — | Channel/group URLs or usernames. Accepts `https://t.me/x`, `https://t.me/s/x`, `@x` or `x` |
| `maxMessages` | integer | `100` | Max messages per channel (1–10000). Set `0` for unlimited |
| `includeMedia` | boolean | `true` | Extract media URLs for photos, videos, documents and voice notes |
| `dateFrom` | string | — | Only include messages after this date (`YYYY-MM-DD`) |
| `dateTo` | string | — | Only include messages before this date (`YYYY-MM-DD`) |

### 📋 Output example

```json
{
    "channel": "durov",
    "channelTitle": "Du Rove's Channel",
    "channelMembers": 1245000,
    "channelDescription": "Pavel Durov, founder of Telegram.",
    "messageId": 312,
    "date": "2026-03-15T14:22:00+00:00",
    "text": "We just crossed 950 million monthly active users. The next milestone is coming soon.",
    "views": 4820000,
    "mediaType": "photo",
    "mediaUrl": "https://cdn4.telegram-cdn.org/file/abc123.jpg",
    "forwardedFrom": null,
    "author": null,
    "replyTo": null,
    "url": "https://t.me/durov/312",
    "scrapedAt": "2026-04-22T10:15:33.421Z"
}
```

Export the full dataset as **CSV**, **Excel**, **XML**, **JSONL** or **HTML table** directly from the Apify console, or programmatically via the Apify API.

### 💻 Code examples

#### curl

```bash
curl -X POST 'https://api.apify.com/v2/acts/makework36~telegram-channel-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "channels": ["durov", "telegram"],
    "maxMessages": 100,
    "includeMedia": true
  }'
```

#### Python (apify-client)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("makework36/telegram-channel-scraper").call(run_input={
    "channels": ["bbcnews", "reuters"],
    "maxMessages": 200,
    "dateFrom": "2026-01-01",
    "dateTo": "2026-03-31",
    "includeMedia": True,
})
for msg in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(msg["channel"], msg["date"], msg["views"], msg["text"][:80])
```

#### Node.js (apify-client)

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('makework36/telegram-channel-scraper').call({
  channels: ['durov', 'telegram'],
  maxMessages: 50,
  includeMedia: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.table(items.map((m) => ({ ch: m.channel, id: m.messageId, views: m.views })));
```

#### Export to CSV

From the console run page → *Export* → *CSV*. Or directly via API:

```
https://api.apify.com/v2/datasets/DATASET_ID/items?format=csv&token=YOUR_TOKEN
```

#### Pipe messages into Elasticsearch

```python
from apify_client import ApifyClient
from elasticsearch import Elasticsearch, helpers

client = ApifyClient("YOUR_APIFY_TOKEN")
es = Elasticsearch("http://localhost:9200/")

run = client.actor("makework36/telegram-channel-scraper").call(run_input={
    "channels": ["my_monitoring_list"], "maxMessages": 1000,
})
docs = list(client.dataset(run["defaultDatasetId"]).iterate_items())
helpers.bulk(es, ({"_index": "telegram", "_source": d} for d in docs))
```

### ⚡ Performance

- **~20 seconds per 100 messages** — pure HTTP, no browser overhead
- **1,000–2,000 message depth per channel** — capped by what Telegram's public web preview exposes
- **128 MB Apify memory tier is enough** — runs cheap
- **Billed per batch of 100 messages** — predictable per-item pricing
- **Built-in 1.5-second delay** between pagination hits to avoid hammering Telegram's servers

### 📊 Comparison

A few Telegram scrapers live on the Apify Store. Here's how this one compares on the criteria that matter for OSINT and monitoring pipelines.

| Feature | This scraper | Alternative A | Alternative B | Alternative C |
|---------|--------------|---------------|---------------|---------------|
| Authentication required | **None** (public preview) | MTProto (phone + API ID) | Bot API (channel admin required) | None |
| Account-ban risk | **Zero** | High | None | Zero |
| Scraping engine | **Pure HTTP + Cheerio** | MTProto session | HTTPS via Bot API | Browser |
| Media URL extraction | **Yes** (photo, video, doc, voice) | Yes (all) | Only for own channels | Partial |
| Forwarded-from detection | **Yes** | Yes | Yes | Partial |
| Reply context | **Yes** (first 100 chars) | Yes | Yes | No |
| Date-range filter | **Yes** (`dateFrom` / `dateTo`) | Yes | No | Partial |
| Max historical depth | ~1–2K messages (Telegram cap) | Unlimited | Channel admin | ~1–2K |
| Multi-channel per run | **Yes (array)** | Yes | Yes | Single only |
| Export formats | **JSON / CSV / Excel / XML** | JSON | JSON | JSON / CSV |
| Code examples in README | **Python / Node / curl / Elastic** | None | None | None |

The honest take: if you need every message ever posted in a channel and you're comfortable managing MTProto sessions and ban risk, an MTProto-based scraper reaches deeper. For everything else — monitoring, research, brand safety, news aggregation — the public web preview approach is more predictable and much cheaper.

### 💵 Pricing

Pay-per-result model:

| Plan | Price per 100 messages | Example: 1K msgs | Example: 10K msgs | Example: 100K msgs |
|------|------------------------|------------------|-------------------|---------------------|
| FREE trial | $0 (up to $5 credit) | free | N/A | N/A |
| BRONZE | $0.50 | $5 | $50 | $500 |
| SILVER | $0.40 | $4 | $40 | $400 |
| GOLD | $0.30 | $3 | $30 | $300 |
| DIAMOND | $0.20 | $2 | $20 | $200 |

Apify gives every new user a $5 trial credit — enough to scrape ~1,000 Telegram messages to evaluate. No subscription, no minimums, no egress fees.

### ❓ FAQ

**Do I need a Telegram account or bot?**
No. This scraper reads the public web preview at [t.me/s/channel](https://t.me/) — the same page any browser visitor sees. No phone number, no bot, no admin permissions.

**Does it work on private or restricted channels?**
No. Only channels with the public preview enabled are scrapable. If the channel owner disabled the web preview, the page redirects and the scraper returns nothing.

**Can I scrape group chats?**
Yes, provided the group has a public preview enabled. The `author` field populates with each poster's display name.

**Can I scrape messages older than a year?**
Usually yes up to the Telegram web-preview cap (~1K–2K messages). Beyond that, schedule the actor repeatedly — new scrapes will surface messages the previous ones didn't reach.

**Is scraping Telegram legal?**
This actor only reads data Telegram itself renders to any anonymous browser. Review [Telegram's Terms of Service](https://telegram.org/tos) and your local data-protection laws (GDPR, CCPA, LGPD). We do not bypass access controls.

**How is this different from using `telethon` or `tdlib` directly?**
Those are MTProto clients that log in as a user and can read anything the user sees — including private chats. They also carry ban risk and session-management overhead. This actor trades reach for stability by reading only public previews.

**Can I get message reactions or poll results?**
Not in v1. The public preview shows views and text but hides reactions and poll votes. Drop a request on the [issues tab](https://apify.com/makework36/telegram-channel-scraper/issues) and we'll prioritize.

**Can I download media files?**
The actor returns `mediaUrl` links when Telegram exposes them. Downloading the actual bytes is outside the actor's scope — pipe the URLs through your own HTTP downloader or a second Apify actor.

**What language are messages returned in?**
The message text itself is in whatever language the channel posts. There's no translation — pipe the output through an LLM or DeepL for normalization.

### 📝 Changelog

- **v1.0** (2026-04-22) — Full SEO rewrite, pricing tiers, comparison table, Python/Node/curl/Elastic examples, tutorial, advanced patterns.
- **v0.3** (2026-02-15) — Reply-thread extraction and forward attribution.
- **v0.2** (2026-01-10) — Media URL support (photos, videos, documents, voice notes).
- **v0.1** (2025-12-05) — Initial release — public channel and group scraping with Cheerio.

### 🔗 Related actors

- [Google Play Reviews Scraper](https://apify.com/makework36/play-store-reviews-scraper) — Android app reviews, ratings and locales
- [YouTube Shorts Scraper](https://apify.com/makework36/youtube-shorts-scraper) — trending Shorts with engagement metrics
- [Google Maps Lead Scraper](https://apify.com/makework36/google-maps-scraper-full) — business phone, email and website data
- [Reddit MCP Server](https://apify.com/makework36/reddit-mcp-server) — Reddit via MCP tools for AI agents
- [Fast Airbnb Price Scraper](https://apify.com/makework36/fast-airbnb-price-scraper) — Airbnb listings, prices and coordinates
- [VRBO Scraper](https://apify.com/makework36) — vacation-rental data via the mobile GraphQL endpoint

# Actor input Schema

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

List of public Telegram channel or group URLs (e.g., https://t.me/channel\_name) or just the username (e.g., channel\_name)

## `maxMessages` (type: `integer`):

Maximum number of messages to scrape per channel. Set 0 for all available.

## `includeMedia` (type: `boolean`):

Extract URLs for images, videos, and documents attached to messages

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

Only scrape messages after this date (YYYY-MM-DD format)

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

Only scrape messages before this date (YYYY-MM-DD format)

## Actor input object example

```json
{
  "channels": [
    "telegram"
  ],
  "maxMessages": 100,
  "includeMedia": true
}
```

# Actor output Schema

## `dataset` (type: `string`):

All scraped items in the default dataset (JSON).

## `csv` (type: `string`):

Default dataset formatted as CSV for spreadsheets (Google Sheets, Excel).

# 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"
    ]
};

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

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

```

## MCP server setup

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