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

Extracts posts from public Telegram channels with views, dates, text, hashtags, media URLs, link previews and forwards. Reaches full channel history, supports keyword search and incremental monitoring, and exports to JSON, CSV or Excel.

- **URL**: https://apify.com/innovatica-ai/telegram-channel-scraper.md
- **Developed by:** [Innovatica AI](https://apify.com/innovatica-ai) (community)
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.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.
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?

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 Scraper

Extract posts from **public Telegram channels** — views, text, dates, hashtags, media URLs, link previews and forwards. Reaches a channel's **entire history**, supports keyword search, and can return only what's new since your last run.

Built and maintained by [Innovatica.ai](https://innovatica.ai).

> Парсер Telegram-каналов: посты, просмотры, даты, медиа. Экспорт в JSON, CSV, Excel.

***

### What you get

Every post returns a flat, stable record:

| Field | Description |
|---|---|
| `postId`, `messageId`, `url` | Identifiers and a direct link to the post |
| `channelUsername`, `channelTitle`, `channelSubscribers` | Channel context on every row |
| `text`, `textLength` | Full post text, plain |
| `views`, `viewsLabel` | View count as a number (`3610000`) and as shown (`3.61M`) |
| `postedAt` | ISO 8601 timestamp, UTC |
| `hashtags`, `mentions`, `outboundLinks` | Extracted from the post body |
| `photoUrls`, `videoUrl`, `videoDuration` | Direct media URLs |
| `linkPreviewTitle`, `linkPreviewUrl` | Link preview card |
| `isForwarded`, `forwardedFrom`, `replyToPostId` | Message relationships |
| `hasSticker`, `hasVoice`, `hasDocument`, `pollQuestion`, `isEdited` | Content flags |
| `scrapedAt` | Collection timestamp |

With **Include a channel summary row** enabled you also get one row per channel carrying `channelDescription`, `subscribers`, `photosCount`, `videosCount` and `linksCount`.

***

### Tutorial

#### 1. Scrape a channel

Put handles into **Telegram channels** — `@durov`, `durov` and `https://t.me/durov` all work:

```
@durov
@telegram
```

Set **Maximum posts per channel** for depth. Telegram exposes the full archive, so raising it to 5,000 genuinely fetches 5,000 posts back through the channel's history.

#### 2. Scrape specific posts

Paste links into **Individual post URLs**:

```
https://t.me/durov/538
```

#### 3. Monitor a channel on a schedule

Enable **Only return posts not seen before**, then schedule the Actor. It remembers the newest post per channel and returns only what appeared since — so you pay for new content, not for re-downloading the archive.

```json
{
  "channels": ["@durov", "@telegram"],
  "maxPostsPerChannel": 50,
  "onlyNewResults": true
}
```

***

### Pricing

Pay per result. You are charged only for posts actually saved — posts removed by filters and failed lookups are **not** charged.

Cost control:

- **Maximum results** caps the entire run.
- **Filters** (minimum views, must-contain text, media-only, date range) drop unwanted posts before they are saved.
- **Only return posts not seen before** makes scheduled runs charge only for genuinely new posts.

This Actor needs **no proxy** — Telegram serves these pages to ordinary requests — which keeps your platform costs near zero.

***

### Input example

```json
{
  "channels": ["@durov"],
  "postUrls": [{ "url": "https://t.me/telegram/455" }],
  "maxResults": 500,
  "maxPostsPerChannel": 200,
  "minViews": 10000,
  "onlyWithMedia": false,
  "postedAfter": "2026-01-01",
  "includeChannelInfo": true,
  "onlyNewResults": false
}
```

### Output example

```json
{
  "postId": "durov/538",
  "messageId": 538,
  "channelUsername": "durov",
  "url": "https://t.me/durov/538",
  "text": "Last night, Apple briefly removed Telegram from the App Store…",
  "textLength": 2511,
  "hashtags": [],
  "mentions": [],
  "outboundLinks": ["https://telegram.org/safety"],
  "views": 3610000,
  "viewsLabel": "3.61M",
  "postedAt": "2026-08-04T15:20:04+00:00",
  "authorSignature": "Pavel Durov",
  "photoUrls": [],
  "videoUrl": null,
  "linkPreviewTitle": "Telegram Safety Overview",
  "linkPreviewUrl": "https://telegram.org/safety",
  "isForwarded": false,
  "isEdited": true,
  "channelTitle": "Pavel Durov",
  "channelSubscribers": 11000000,
  "scrapedAt": "2026-08-28T12:40:11+00:00"
}
```

***

### Use cases

- **Crypto and Web3 research** — Telegram is where that market talks. Track announcement channels and measure reach by view count.
- **Brand and threat monitoring** — watch channels for mentions with *Must contain text*, on a schedule.
- **News aggregation** — pull posts from media channels with media URLs attached.
- **Market research in CIS, MENA and South Asia** — Telegram is a primary channel in these regions, where other platforms give thin coverage.
- **Archiving** — export an entire channel's history to CSV or Excel in one run.

***

### Recommendations

- Start with `maxResults` at 20–50 to confirm the shape of the output, then scale up.
- Prefer **Must contain text** over **Search** when you need completeness: Search is fast but returns a single page, while the filter is applied across the full history you fetch.
- For monitoring, schedule daily with `onlyNewResults` enabled.
- Leave the proxy off unless your own network blocks `t.me`.

***

### FAQ

**Which channels work?**
Any **public** channel — one with a `t.me/<name>` address you can open in a browser without joining. Private channels, invite-only channels, groups and bot accounts are not accessible and return a clear error row.

**Can it get the full history?**
Yes. Paging runs back to the channel's very first message. Set *Maximum posts per channel* high enough and you get the whole archive.

**Why does Search return only ~20 posts?**
That is Telegram's limit, not ours — channel search returns a single page and cannot be paged. For exhaustive results, fetch history and use the **Must contain text** filter instead.

**Are view counts exact?**
Telegram publishes rounded labels above ~1,000 (`3.61M`). We return both the parsed number in `views` and the original label in `viewsLabel` so you can see the precision you're getting.

**Does it get comments, reactions or subscriber lists?**
No. Telegram's public channel pages don't expose reactions, discussion threads or member lists, so no tool can read them from this surface without an account.

**Does one bad channel kill the run?**
No. Failures are written as a row with an `error` field and the run continues.

***

### Legal

This Actor collects **only publicly available data** — content any visitor can see without logging in. It does not log in, does not join channels, does not access private channels or groups, and does not collect member lists.

You are responsible for how you use the collected data, including compliance with GDPR and other data-protection laws and with Telegram's terms of service. If you process personal data, make sure you have a valid legal basis.

***

### Support

Found a bug or need another field? Open an issue on the Actor's **Issues** tab. Maintained by [Innovatica.ai](https://innovatica.ai).

# Actor input Schema

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

Public channels to scrape. Accepts @handle, a bare handle, or a t.me link — e.g. @durov, durov, https://t.me/durov. Private channels, groups and bots are not accessible.

## `postUrls` (type: `array`):

Links to single posts, e.g. https://t.me/durov/538. Returns just those messages.

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

Only return posts containing this term. Note: Telegram's channel search returns a single page of about 20 results and cannot be paged, so this is best for finding recent mentions rather than exhaustive history.

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

Adds one extra row per channel with its title, description, subscriber count and media counts.

## `maxResults` (type: `integer`):

Hard cap on how many rows this run returns in total. Controls your cost.

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

How far back to go in each channel, newest first. Telegram exposes the full history, so raise this to archive an entire channel.

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

How many channels to fetch in parallel. Lower it if you see failures.

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

Skip posts below this view count.

## `mustContain` (type: `string`):

Keep only posts whose text contains this string (case-insensitive). Applied after fetching, so unlike Search it works across full history.

## `onlyWithMedia` (type: `boolean`):

Skip text-only posts.

## `postedAfter` (type: `string`):

Only keep posts published on or after this date.

## `postedBefore` (type: `string`):

Only keep posts published on or before this date.

## `onlyNewResults` (type: `boolean`):

Remembers the newest post collected per channel and returns only what has appeared since. Use this for scheduled monitoring so you only pay for genuinely new content.

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

Optional. Telegram serves these pages without blocking datacenter IPs, so no proxy is needed and leaving this off keeps your run cheaper. Enable it only if your own network blocks t.me.

## Actor input object example

```json
{
  "channels": [
    "@telegram"
  ],
  "includeChannelInfo": false,
  "maxResults": 200,
  "maxPostsPerChannel": 100,
  "maxConcurrency": 5,
  "onlyWithMedia": false,
  "onlyNewResults": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `posts` (type: `string`):

One row per Telegram post, with views, text, hashtags, media URLs, forwards and channel context.

## `overview` (type: `string`):

Browse the posts in a table and download them as JSON, CSV or 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("innovatica-ai/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("innovatica-ai/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 innovatica-ai/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

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