# Telegram Channel Messages Scraper (`automation-lab/telegram-public-channel-history-scraper`) Actor

Export public Telegram channel history with message IDs, text, timestamps, engagement, authors, forwarding context, links, and media metadata for monitoring and OSINT archives.

- **URL**: https://apify.com/automation-lab/telegram-public-channel-history-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Social media
- **Stats:** 1 total users, 1 monthly users, 91.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.17 / 1,000 item extracteds

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 Messages Scraper

Export **telegram channel messages** from anonymous public `t.me` previews into a structured Apify dataset.

The Actor accepts channel usernames, `@handles`, and public `t.me` URLs.

It follows Telegram's public archive pagination and returns message IDs, text, timestamps, engagement counters, authors, forwarding context, links, reactions when visible, and media metadata.

Use it for scheduled monitoring, OSINT archives, research snapshots, and data pipelines without a Telegram account.

### What does Telegram Channel Messages Scraper do?

The Actor reads server-rendered public channel archive pages at `t.me/s/<channel>`.

It normalizes every visible message into one stable record.

Each record has a channel handle and message ID that can be used as a durable comparison key.

Results can be ordered oldest-first for archives or newest-first for monitoring.

Optional date bounds narrow the accepted records.

The Actor does not join channels, log in, or access private Telegram data.

### Who is it for?

**OSINT researchers** can preserve timestamped public statements and source links.

**Media monitors** can collect recent public channel posts on a schedule.

**Data analysts** can export message history to CSV, Excel, JSON, or BI tools.

**Developers** can feed public messages into search, classification, or alert pipelines.

**Compliance teams** can retain reproducible public-source snapshots subject to their own legal policies.

Choose [Telegram Info Scraper](https://apify.com/automation-lab/telegram-info-scraper) when you only need profile or channel metadata.

Choose [Telegram Media Downloader](https://apify.com/automation-lab/telegram-media-downloader) when downloading public media files is the primary job.

### Why use this Actor?

- No Telegram login or API credentials are required.
- Public channel usernames and URLs share one input contract.
- Archive pagination is bounded and deduplicated by message ID.
- Output includes both plain text and optional sanitized message HTML.
- Media URLs and structured media metadata are included when Telegram exposes them.
- Date filters and sort order support repeat monitoring workflows.
- Invalid private or invite links fail clearly instead of producing misleading empty data.
- A 256 MB HTTP implementation avoids browser overhead.

### What data can you extract?

| Field | Meaning |
| --- | --- |
| `channelHandle` | Public channel username without `@` |
| `channelTitle` | Visible channel title |
| `channelUrl` | Canonical channel URL |
| `channelPhotoUrl` | Public channel image URL when exposed |
| `messageId` | Stable message ID within the channel |
| `messageUrl` | Canonical public message URL |
| `publishedAt` | Telegram publication timestamp |
| `text` | Plain-text message body |
| `html` | Optional sanitized body HTML |
| `views` | Visible view count |
| `forwards` | Visible forward count |
| `replies` | Visible reply/comment count |
| `commentsUrl` | Public comments link when exposed |
| `authorName`, `authorUrl` | Visible message author context |
| `isForwarded` | Whether Telegram marks the message as forwarded |
| `forwardedFromName`, `forwardedFromUrl` | Visible original-source context |
| `reactions` | Visible emoji reaction counts |
| `media` | Typed photo, video, audio, document, or voice metadata |
| `mediaUrls` | Direct media URLs for convenient export |
| `links` | Unique non-media links in the widget |
| `scrapedAt` | Extraction timestamp |

Optional Telegram fields are returned as `null` or empty arrays when they are not visible in the public web preview.

### How to scrape public Telegram channel history

1. Open the Actor in Apify Console.
2. Add one or more public channels such as `telegram`, `@durov`, or `https://t.me/s/telegram`.
3. Set `maxMessages` to the total number of records you need.
4. Increase `maxPagesPerChannel` only when you need deeper history.
5. Optionally add `minDate` or `maxDate` in ISO 8601 format.
6. Choose oldest-first for an archive or newest-first for monitoring.
7. Click **Start**.
8. Open the Dataset tab to inspect, download, or integrate the records.

A small first run is recommended before collecting deep history from many channels.

### Input parameters

#### `channels`

An array of public usernames, `@handles`, or `t.me` channel/post URLs.

Example: `["telegram", "@durov"]`.

Up to 100 channel inputs are accepted in one run.

#### `startUrls`

Apify request-list style public `t.me` URLs.

Use this field when another Actor or integration already emits URL objects.

#### `maxMessages`

Maximum records saved across all channels.

The default is 100 and the hard maximum is 10,000.

#### `maxPagesPerChannel`

Maximum archive pages requested for each channel.

One page normally exposes around 20 messages.

The default is 10 and the hard maximum is 250.

#### `minDate` and `maxDate`

Optional inclusive ISO 8601 date bounds.

Example: `2025-01-01T00:00:00Z`.

#### `sortOrder`

Use `oldestFirst` for chronological archives.

Use `newestFirst` for recent-message review and monitoring.

#### `includeHtml`

Set to `true` to include Telegram's message-body HTML in addition to plain text.

### Example input

```json
{
  "channels": ["telegram", "durov"],
  "maxMessages": 40,
  "maxPagesPerChannel": 3,
  "sortOrder": "oldestFirst",
  "includeHtml": false
}
```

For a recurring monitor, schedule the Actor and compare `channelHandle/messageId` keys with the previous dataset.

### Example output

```json
{
  "channelHandle": "samplechannel",
  "channelTitle": "Sample Channel",
  "channelUrl": "https://t.me/samplechannel",
  "messageId": 1234,
  "messageUrl": "https://t.me/samplechannel/1234",
  "publishedAt": "2025-01-15T12:00:00+00:00",
  "text": "This is an example public update.",
  "views": 12500,
  "forwards": 45,
  "replies": 12,
  "authorName": "Sample Channel",
  "isForwarded": false,
  "reactions": [],
  "media": [],
  "mediaUrls": [],
  "links": ["https://example.org/report"],
  "scrapedAt": "2025-01-15T12:05:00.000Z"
}
```

The exact optional fields depend on what Telegram exposes for that message.

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

The Actor uses pay-per-event pricing.

Each run has a **$0.005 start fee**.

Each saved message is charged under the `item` event.

The current per-message tiers are:

| Plan | Price per message |
| --- | ---: |
| Free | $0.00032612 |
| Bronze | $0.00028358 |
| Silver | $0.00022120 |
| Gold | $0.00017015 |
| Platinum | $0.00011343 |
| Diamond | $0.000079404 |

At the Bronze tier, 100 messages cost about **$0.03336** including the start fee.

At the Bronze tier, 1,000 messages cost about **$0.28858** including the start fee.

Only accepted dataset messages incur the item event; rejected, duplicate, or filtered records do not.

Apify platform pricing and plan rules can affect the final billed total.

### Scheduled monitoring workflow

Create an Apify Schedule with a small newest-first limit.

Keep the resulting dataset or export it to your database.

Use `channelHandle` plus `messageId` as the unique key.

Compare the current run with the previous run to identify newly visible messages.

Use a webhook to trigger Slack, email, a queue, or your own endpoint after successful runs.

The Actor exports snapshots; it does not itself send alerts or maintain a cross-run change ledger.

### Export and integration options

Download the dataset as JSON, JSONL, CSV, Excel, XML, or RSS from Apify Console.

Connect Apify to Google Sheets for analyst review.

Send records to Make, Zapier, n8n, or a webhook.

Use the Dataset API for incremental ingestion into a data warehouse.

Store canonical `messageUrl` values with your analysis so reviewers can return to the public source.

Media CDN links may expire, so downstream systems should not treat them as permanent file storage.

### Run with the Apify API

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~telegram-public-channel-history-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"channels":["telegram"],"maxMessages":20,"sortOrder":"newestFirst"}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/telegram-public-channel-history-scraper').call({
  channels: ['telegram'],
  maxMessages: 20,
  sortOrder: 'newestFirst',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/telegram-public-channel-history-scraper').call(
    run_input={'channels': ['telegram'], 'maxMessages': 20, 'sortOrder': 'newestFirst'}
)
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

Never put an Apify token in source control.

### Use with MCP and AI agents

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/telegram-public-channel-history-scraper"
```

#### Claude Desktop

Add this JSON to Claude Desktop's MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/telegram-public-channel-history-scraper"
    }
  }
}
```

#### Cursor

Add the same `apify` server URL in **Cursor Settings → Tools & MCP → New MCP Server**.

#### VS Code

Add the same HTTP MCP server URL to your VS Code MCP configuration, then enable the `automation-lab/telegram-public-channel-history-scraper` tool.

Example prompt: “Collect the 20 newest public messages from the Telegram channel and summarize recurring product themes with source URLs.”

Example prompt: “Archive up to 100 public messages from these two channel URLs in chronological order and return the dataset link.”

AI-generated interpretations should preserve message URLs and publication timestamps for source review.

### Limits and expected behavior

Only anonymous public Telegram web previews are supported.

Private channels, invite links, `/c/` links, login-only messages, and deleted posts are unavailable.

Telegram may expose only part of a channel's history through its public pagination.

Reactions, forwards, replies, authors, or media fields may be absent on individual messages.

The default implementation uses direct HTTP and does not expose an automatic residential proxy fallback.

A source layout change can require a parser update.

Very broad date ranges still require pagination; date filters do not create a Telegram server-side search.

Messages are deduplicated within a run, not across separate runs.

### Reliability and troubleshooting

**The run says the channel is invalid.**

Use a public username or `https://t.me/s/<username>` URL, not an invite or private link.

**The channel has no output.**

Confirm that its public web preview is visible in a logged-out browser and check the date range.

**I received fewer records than requested.**

Increase `maxPagesPerChannel`, remove restrictive date bounds, and verify that Telegram exposes older pages publicly.

**A media URL no longer works.**

Telegram CDN links are source URLs, not durable storage. Process permitted media soon after extraction.

**A run fails with an upstream HTTP error.**

Retry later after checking Telegram's public page. The Actor automatically retries transient network failures twice.

Inspect the run log for the channel and page that failed before changing limits.

### Responsible use and legality

Use the Actor only for public data you are authorized to collect and process.

Follow Telegram's terms, Apify's terms, copyright rules, privacy law, and applicable data-retention requirements.

Do not use the output for harassment, unlawful surveillance, discrimination, doxxing, or attempts to identify protected people.

Minimize collection, retain source context, secure exported datasets, and delete data when it is no longer needed.

The Actor does not bypass access controls or grant rights to reuse message text or media.

You are responsible for the purpose, legal basis, and downstream use of each run.

### Related Automation Lab Actors

- [Telegram Info Scraper](https://apify.com/automation-lab/telegram-info-scraper) — public profile and channel metadata.
- [Telegram Media Downloader](https://apify.com/automation-lab/telegram-media-downloader) — download publicly exposed Telegram media files.
- [TGKit Telegram Channel Finder Scraper](https://apify.com/automation-lab/tgkit-telegram-channel-finder-scraper) — discover channels before extracting history.

Combine discovery, metadata, and message-history Actors only when each additional dataset is necessary for your workflow.

### FAQ

#### Does this Actor require a Telegram account?

No. It reads anonymous public web previews.

#### Can it scrape private channels or groups?

No. Private, invite-only, joined-account, and login-gated content is intentionally unsupported.

#### Does it download media files?

No. It exports media metadata and public URLs when exposed. Use Telegram Media Downloader for a file-download workflow.

#### Can I scrape one direct post URL?

Yes. A public post URL is normalized to its channel archive; the Actor returns channel history subject to your limits and filters.

#### Are results real-time?

Results reflect what Telegram exposes when each page is fetched. Scheduled runs provide periodic snapshots, not a live stream.

#### How do I avoid duplicate records in my database?

Use the combination of `channelHandle` and `messageId` as the unique key.

#### Why are some counters null?

Telegram does not expose every engagement field on every public message widget.

#### What output order should I use?

Use oldest-first for archival imports and newest-first for review or monitoring.

# Actor input Schema

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

Public channel usernames, @handles, or t.me channel/post URLs. Each URL is normalized to its public channel archive.

## `startUrls` (type: `array`):

Alternative request-list input for public t.me channel or post URLs. Useful with Apify integrations.

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

Maximum number of message records saved across all channels.

## `maxPagesPerChannel` (type: `integer`):

Safety limit for Telegram archive pagination. Each page normally contains about 20 messages.

## `minDate` (type: `string`):

Optional inclusive ISO 8601 lower bound, for example 2025-01-01T00:00:00Z.

## `maxDate` (type: `string`):

Optional inclusive ISO 8601 upper bound.

## `sortOrder` (type: `string`):

Return each channel's collected messages from oldest to newest or newest to oldest.

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

Include Telegram's sanitized message-body HTML alongside plain text.

## Actor input object example

```json
{
  "channels": [
    "telegram",
    "durov"
  ],
  "startUrls": [
    {
      "url": "https://t.me/s/telegram"
    }
  ],
  "maxMessages": 20,
  "maxPagesPerChannel": 10,
  "sortOrder": "oldestFirst",
  "includeHtml": false
}
```

# Actor output Schema

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

Default dataset containing all accepted public channel message records.

# 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"
    ],
    "startUrls": [
        {
            "url": "https://t.me/s/telegram"
        }
    ],
    "maxMessages": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/telegram-public-channel-history-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",
    ],
    "startUrls": [{ "url": "https://t.me/s/telegram" }],
    "maxMessages": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/telegram-public-channel-history-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"
  ],
  "startUrls": [
    {
      "url": "https://t.me/s/telegram"
    }
  ],
  "maxMessages": 20
}' |
apify call automation-lab/telegram-public-channel-history-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/telegram-public-channel-history-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/EKCivrpwMioXcTXWK/builds/X8N75D9BJLfpG3QsC/openapi.json
