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

Export posts from any public Telegram channel without an account or bot token. Get message text, publish time, view counts, full reaction breakdowns, media URLs, links and hashtags. Search inside channels by keyword and filter by date range.

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

## Pricing

$1.10 / 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?

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

### What is Telegram Channel Scraper?

**Telegram Channel Scraper** lets you export the full post history of any public Telegram channel, helping you track competitors, monitor breaking news and crypto calls, measure which posts actually travel, and build datasets nobody else has.

No API key. No phone number. No Telegram account. You paste a channel name and get structured rows.

- **Monitor channels at scale:** pass a list of channels and export all of them in one run, with per-channel caps so one busy feed cannot eat the whole budget.
- **Find the posts that matter:** search inside a channel by keyword using Telegram's own index, instead of downloading a year of history and grepping it yourself.
- **Measure real reach:** every row carries the view count, the full reaction breakdown emoji by emoji, and a computed reactions-per-view rate you can sort on.
- **Pull a clean date range:** set a start date, an end date, or both, and get exactly that window.
- **Feed a pipeline:** media URLs, link previews, outbound links, hashtags and @mentions arrive as separate fields, already split out.

The official Telegram Bot API cannot read a channel your bot is not an admin of, and the client APIs need a phone number and a logged-in session. This Actor reads the public web preview Telegram publishes for every public channel, so there is nothing to authenticate and nothing to get banned.

### What data does Telegram Channel Scraper extract?

**Per message**

- 🆔 Message ID and permalink (`messageId`, `messageUrl`)
- 🕒 Exact publish timestamp in ISO 8601 (`postedAt`)
- 📝 Full message text with line breaks preserved, plus its length (`text`, `textLength`)
- 👁 View count, expanded from Telegram's "79.4K" to a real integer (`views`)
- ❤️ Every reaction, emoji by emoji, with counts (`reactions`, `totalReactions`)
- 📈 Reactions per view, computed (`engagementRate`)
- 🖼 Media type, count, and direct URLs for photos, videos, documents, voice notes and stickers (`mediaType`, `media`)
- 🔗 Link preview title, description, site name and image (`linkPreviewTitle`, `linkPreviewUrl`)
- 🌐 Every outbound link in the post (`links`)
- \#️⃣ Hashtags and @mentions, taken from Telegram's own linkification rather than a regex (`hashtags`, `mentions`)
- ↩️ Reply target, its author and the quoted text (`replyToMessageUrl`, `replyToAuthor`, `replyToText`)
- ➡️ Forward source and its link (`forwardedFrom`, `forwardedFromUrl`)
- 📊 Poll question, options and percentages (`poll`)
- ✏️ Edited flag and author signature (`isEdited`, `authorSignature`)

**Per channel, attached to every row**

- 📛 Channel title, @username and URL (`channelTitle`, `channelUsername`)
- 👥 Subscriber count as an integer (`channelSubscribers`)
- ✅ Verified badge (`channelIsVerified`)
- 📄 Channel description and avatar URL
- 📷 Lifetime photo, video and link counts (`channelPhotosCount`, `channelVideosCount`, `channelLinksCount`)

Channel fields are copied onto every message row on purpose, so a CSV export is usable in a spreadsheet without a join.

### How to use Telegram Channel Scraper

1. Enter one or more channels. A username (`durov`), an @handle (`@durov`) or any t.me link works, including a link to a single message.
2. Optionally type a keyword in **Keyword to search for inside the channels** to return only matching posts.
3. Optionally set **From date** and **To date** to limit the window.
4. Set **Max messages**, then click **Start**.
5. Export to JSON, CSV or Excel, or pull the dataset from the API.

### Input

```json
{
  "channels": ["durov", "@telegram", "https://t.me/bloomberg"],
  "searchQuery": "inflation",
  "fromDate": "2026-01-01",
  "toDate": "2026-06-30",
  "maxItems": 500,
  "maxMessagesPerChannel": 200,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

| Field | Type | Notes |
|---|---|---|
| `channels` | array | Required. At least one public channel. |
| `searchQuery` | string | Optional. Uses Telegram's in-channel search. |
| `fromDate` / `toDate` | string | Optional, `YYYY-MM-DD`. `toDate` includes the whole day. |
| `maxItems` | integer | Total cap across every channel. `0` means no limit. |
| `maxMessagesPerChannel` | integer | Optional per-channel cap. |
| `proxyConfiguration` | object | Apify datacenter proxy is enough. Leave the group unset. |

### Output

One row per message, newest first.

```json
{
  "channelUsername": "durov",
  "channelTitle": "Pavel Durov",
  "channelSubscribers": 10900000,
  "channelIsVerified": true,
  "messageId": 546,
  "messageUrl": "https://t.me/durov/546",
  "postedAt": "2026-08-31T16:30:15+00:00",
  "text": "Gram Wallet in Telegram is ready and is now accessible to a select group of users.\n\nWe'll be gradually rolling it out to our billion+ users over the next couple of weeks.",
  "textLength": 406,
  "views": 1300000,
  "reactions": [
    { "emoji": "👍", "count": 56000 },
    { "emoji": "❤", "count": 41000 }
  ],
  "totalReactions": 97000,
  "engagementRate": 0.074615,
  "mediaType": "photo",
  "media": [{ "type": "photo", "url": "https://cdn5.telesco.pe/file/..." }],
  "links": ["https://telegram.org/blog/wallet"],
  "hashtags": [],
  "isEdited": false,
  "scrapedAt": "2026-09-02T13:58:04.221Z"
}
```

The **Overview** table view shows channel, publish time, message, views, reactions, media type and link. A second **Engagement** view swaps in subscriber count and the reactions-per-view rate for reach analysis.

### How much does it cost to scrape Telegram?

A flat **$1.10 per 1,000 messages**. One price, every message, no tiers and no separate charge for starting a run.

| Messages | Cost |
|---|---|
| 1,000 | $1.10 |
| 10,000 | $11.00 |
| 100,000 | $110.00 |

The cheapest comparable Telegram scraper on the Store charges $2.00 per 1,000 messages plus a per-run start fee. This one is about 45% cheaper and has no start fee.

### Measured coverage

Measured across 386 messages from 5 channels (`durov`, `durov_russia`, `telegram`, `bloomberg`, `tginfo`), run through the Apify datacenter proxy.

| Field | Coverage | Notes |
|---|---|---|
| `messageId`, `messageUrl`, `postedAt` | 100% | Guaranteed on every row |
| `channelTitle`, `channelSubscribers` | 100% | Guaranteed on every row |
| `views` | 99.5% | Telegram omits the counter on a small number of posts |
| `text` | 98.4% | Media-only posts have no caption |
| `totalReactions` | 100% | `0` when a channel has reactions disabled |
| `media` | 48.7% | Present only on posts that carry media |
| `links` | 57.5% | Present only on posts that link out |
| `linkPreviewUrl` | 16.8% | Present only when Telegram rendered a preview card |
| `hashtags` | 10.1% | Varies enormously by channel |
| `replyToText` | 3.1% | Present only on replies |
| `forwardedFrom` | 0.3% | Present only on forwards |

Conditional fields are low by nature, not by failure. A post with no link cannot have a link.

### Limits

- **Public broadcast channels only.** Telegram publishes a web preview for public channels. Private channels, groups, and channels whose owner disabled the preview cannot be read without a logged-in account, and this Actor will not ask you for one. Those inputs come back as an explanatory row rather than a failed run.
- **Reactions are the aggregate only.** Telegram's public preview shows how many people reacted with each emoji, not who they are.
- **No comment threads.** Discussion-group comments under a post live in a separate linked group and are not part of the channel preview.
- **Views are rounded above 1,000.** Telegram itself publishes "79.4K", so the integer is 79,400. The original string is kept in `viewsLabel` so you can see the precision you actually have.
- **In-channel search is Telegram's own.** It matches whole words and behaves like the search box in the app.
- **No message deletion history.** Deleted posts are gone from the preview and cannot be recovered.

### FAQ

#### Is it legal to scrape Telegram?

This Actor reads only the public web preview that Telegram itself publishes at `t.me/s/<channel>` for public broadcast channels. That page is served without a login, is indexed by search engines, and is intended to be publicly readable. No account, cookie or credential is used, and no private chat, group or direct message is accessible. Channel posts are public broadcasts rather than personal correspondence. You remain responsible for how you use the data, including under GDPR and any local rules that apply to you.

#### Do I need a Telegram account or a bot token?

No. That is the point. The Bot API cannot read a channel your bot does not administer, and the client APIs require a phone number. This Actor needs neither.

#### Can I use Telegram Channel Scraper as an API?

Yes. Every Actor on Apify is callable over REST. See the [Apify API docs](https://docs.apify.com/api/v2) and the **API** tab on this Actor's page for ready-made snippets.

#### Can I use this in Python?

Yes, with `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("cirkit/telegram-channel-scraper").call(
    run_input={"channels": ["durov"], "maxItems": 100}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["postedAt"], item["views"], item["text"])
```

#### Can I integrate Telegram Channel Scraper with other apps?

Yes. Apify connects to Zapier, Make, n8n, Slack, Google Sheets, Airtable and plain webhooks, so a run can push new posts straight into a sheet or a channel alert.

#### How do I schedule Telegram Channel Scraper to run automatically?

Use **Schedules** in Apify Console to run it hourly or daily. Pair it with `fromDate` set to the last run, or a small `maxItems`, to keep incremental runs cheap.

#### Why did I get fewer results than expected?

Most often the channel has less visible history than you assumed, or your date range does not overlap it. Other causes: `maxMessagesPerChannel` capped the channel before `maxItems` did, a keyword matched fewer posts than you expected, or the channel has no public preview at all. The run log names which one happened, and a run that finds nothing writes a single row explaining why rather than failing.

#### What happens if a channel is private or does not exist?

The run still succeeds and writes one row with `isNoResults: true` and a `noResultsReason` explaining that Telegram publishes no preview for it. A scheduled job then sees a clear reason instead of a failed run.

#### Can I get a field that is not listed?

If it appears on the public channel page, probably. Open an issue on the Actor's **Issues** tab and describe the field.

#### How far back can it go?

To the start of the channel's visible history. Long-running channels regularly return thousands of posts; set `maxItems` to `0` and a `fromDate` to control how deep it goes.

### You might also like

- [Google News Scraper](https://apify.com/cirkit/google-news-scraper) - news coverage for the same stories these channels break
- [Reddit Posts Scraper](https://apify.com/cirkit/reddit-posts-scraper) - the other place a narrative spreads first
- [YouTube Comments Scraper](https://apify.com/cirkit/youtube-comments-scraper) - audience reaction at scale
- [TradingView Screener Scraper](https://apify.com/cirkit/tradingview-screener-scraper) - pair crypto channel chatter with price data

### Changelog

**0.1** - 2026-09-02 - First release. Multi-channel export, in-channel keyword search, date range filtering, full reaction breakdown, media and link extraction.

# Actor input Schema

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

Public channels to export. Accepts a username (durov), an @handle (@durov), or a link (https://t.me/durov). Private invite links are not readable without an account and are skipped.

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

Optional. Returns only messages containing this word or phrase, using Telegram's own in-channel search. Leave empty to export the whole history newest first.

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

Hard cap on the total number of messages returned across every channel. Set to 0 for no limit.

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

Optional per-channel cap, so one busy channel cannot use up the whole budget. Leave empty to let the total cap decide.

## `fromDate` (type: `string`):

Optional. Skip messages published before this date. Format YYYY-MM-DD.

## `toDate` (type: `string`):

Optional. Skip messages published after this date. The whole day is included. Format YYYY-MM-DD.

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

Proxy settings. The Apify datacenter proxy is enough for Telegram and is the cheapest option. Leave the group unset.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "@telegram",
    "https://t.me/bloomberg"
  ],
  "searchQuery": "inflation",
  "maxItems": 100,
  "fromDate": "2026-01-01",
  "toDate": "2026-06-30",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Every message from this run as the Overview table: channel, publish time, text, views, reactions and the permalink.

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

The same messages ranked for reach analysis: views, total reactions and the reaction-to-view rate.

## `messagesCsv` (type: `string`):

The full result set as CSV, ready for a spreadsheet or a BI tool.

## `consoleRun` (type: `string`):

The Apify Console page for this run, including the live log.

# 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"
    ],
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cirkit/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"],
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

```

## MCP server setup

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