# Telegram Channel Scraper (`santamaria-automations/telegram-scraper`) Actor

Scrape public Telegram channels without login or API key: message text, date, views, reactions, media, link previews, forwards, plus channel profile with subscribers. Keyword search inside a channel. Pay-per-result.

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

## Pricing

Pay per event

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?

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 - Public Posts and Channel Info

Scrape public [Telegram](https://telegram.org) channels without a login, API key or phone number. For each channel you get the messages (text, date, views, reactions, media, link previews, forwards, links, hashtags) and, if you want, the channel profile (title, description, subscribers, photo, verified flag). Search for a keyword inside a channel or go back in time up to a date.

### What data can you extract?

**Message rows** (`entry_type: "message"`)

- Message ID, direct message URL, channel name and title
- Publication date (ISO 8601, UTC), edited flag, author signature
- Plain text and HTML text, external links, hashtags, @mentions
- View count (as shown by Telegram, rounded for large numbers)
- Reactions with count (emoji or custom emoji ID, paid star reactions)
- Media: photos, videos, round videos, voice messages, documents, stickers, polls, with URLs where Telegram publishes them
- Link preview card (URL, site name, title, description, image)
- Forwarded-from source and reply-to reference

**Channel rows** (`entry_type: "channel"`, optional)

- Title, description (plain and HTML), links in the description
- Subscriber count (rounded as shown by Telegram), counts of photos, videos, files and links
- Verified flag, channel photo URL

Telegram does not show reply counts or forward counts on the public web preview, so those fields are not available.

### Use with AI Agents (MCP)

Connect this actor to any MCP-compatible AI client, including Claude Desktop, Claude.ai, Cursor, VS Code, LangChain and LlamaIndex.

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.apify.com?tools=santamaria-automations/telegram-scraper",
        "--header",
        "Authorization: Bearer <APIFY_TOKEN>"
      ]
    }
  }
}
```

Example prompt: "Use `telegram-scraper` to get the last 50 posts of the durov channel and summarise the topics with view counts."

### How to use

```json
{
  "channels": ["telegram", "@durov", "https://t.me/bbcnews"],
  "maxMessages": 100,
  "searchQuery": "",
  "includeChannelInfo": true
}
```

An empty input `{}` also works and runs the defaults (the `telegram` channel, 20 messages, plus the channel profile).

### Input parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `channels` | array | `["telegram"]` | Channel names: `name`, `@name` or a `t.me` link |
| `maxMessages` | integer | `20` | Maximum messages per channel, newest first (1-10,000) |
| `searchQuery` | string | empty | Only messages containing this word or phrase, searched inside each listed channel |
| `dateFrom` | string | empty | Stop at messages older than this date (`YYYY-MM-DD`) |
| `includeChannelInfo` | boolean | `true` | Add one profile row per channel |

**Limits**

- Only channels with a public web preview work. Private channels, invite links (`t.me/+...`) and channels that disabled the preview are reported and skipped.
- Telegram offers no keyword search across all channels without a login. `searchQuery` searches inside the channels you list.
- Large counters (views, subscribers, reactions) are rounded by Telegram, for example `1.59M`.

### What you get

Message rows and channel profile rows share one table; fields marked "(channel rows)" are only filled for profile rows, message fields are empty (`null`) on profile rows.

| Field | Type | Description |
|-------|------|-------------|
| `entry_type` | string | message or channel (channel profile row, only if includeChannelInfo) |
| `channel` | string | Channel username without @ |
| `channel_title` | string | Channel display name |
| `channel_url` | string | Channel URL |
| `id` | integer | Message ID, unique within the channel (message rows) |
| `message_url` | string | Direct link to the message |
| `date` | string | Publication time (ISO 8601, UTC) |
| `text` | string | Plain text of the message |
| `text_html` | string | Message text as HTML (formatting and links kept) |
| `views_text` | string | View count as shown by Telegram |
| `author_signature` | string | Post author signature if the channel shows one |
| `edited` | boolean | True if the message was edited |
| `forwarded_from` | string | Original source of a forwarded message |
| `forwarded_from_url` | string | Link to the original source |
| `reply_to_url` | string | Message this one replies to |
| `reply_to_message_id` | integer | ID of the replied message |
| `reply_to_text` | string | Preview of the replied message |
| `has_media` | boolean | True if the message has media |
| `media_type` | string | Type of the first media item: photo, video, round_video, voice, document, sticker, poll, location |
| `media` | array | All media items with type, url, thumbnail_url, title, duration, size_text, poll_question, poll_options |
| `link_preview` | object | Link preview card: url, site_name, title, description, image_url |
| `links` | array | External links in the message text |
| `hashtags` | array | Hashtags in the message text |
| `mentions` | array | @mentions in the message text |
| `reactions` | array | Reactions with emoji, custom_emoji_id, paid, count, count_text |
| `search_query` | string | Keyword used for the in-channel search |
| `title` | string | Channel title (channel rows) |
| `description` | string | Channel description (channel rows) |
| `description_html` | string | Channel description as HTML (channel rows) |
| `subscribers` | integer | Subscriber count, rounded as shown by Telegram (channel rows) |
| `subscribers_text` | string | Subscriber count as shown (channel rows) |
| `photos_count` | integer | Number of photos in the channel (channel rows) |
| `videos_count` | integer | Number of videos in the channel (channel rows) |
| `files_count` | integer | Number of files in the channel (channel rows) |
| `links_count` | integer | Number of links in the channel (channel rows) |
| `verified` | boolean | Verified channel (channel rows) |
| `photo_url` | string | Channel photo URL (channel rows) |
| `scraped_at` | string | When the row was scraped |

#### Output example

```json
{
  "entry_type": "message",
  "channel": "durov",
  "channel_title": "Pavel Durov",
  "id": 548,
  "message_url": "https://t.me/durov/548",
  "date": "2026-09-11T16:04:02+00:00",
  "text": "Telegram has become the sponsor of Codeforces ...",
  "views": 3550000,
  "views_text": "3.55M",
  "author_signature": "Pavel Durov",
  "has_media": false,
  "link_preview": {"url": "https://codeforces.com/blog/entry/156620", "site_name": "Codeforces", "title": "Telegram Returns as Title Sponsor of Codeforces!"},
  "links": ["https://codeforces.com/blog/entry/156620"],
  "reactions": [{"emoji": null, "custom_emoji_id": "5399847211989246390", "paid": false, "count": 36300, "count_text": "36.3K"}],
  "scraped_at": "2026-10-21T15:39:33Z"
}
```

Fields Telegram does not publish for a message are `null` (or an empty list).

### Pricing

Pay per result. You are charged one start event per run and one event for every row saved to the dataset.

| Event | Price | When |
|-------|-------|------|
| `telegram-start` | $0.005 | Once per run, right before the first row is saved |
| `telegram-message-result` | $0.0012 | Per message saved ($1.20 per 1,000 messages) |
| `telegram-channel-result` | $0.002 | Per channel profile row saved (only if `includeChannelInfo` is on) |

Runs without results cost nothing. Duplicates are never saved or charged twice, and a restarted run does not charge rows it already saved.

### Performance

- **Speed**: about 20 messages per page and one page per 1-2 seconds, so roughly 1,000 messages per minute
- **Transfer**: about 1.2 MB per 1,000 messages

### Tips

- Use `dateFrom` for daily or weekly monitoring so you only collect new posts
- Set `includeChannelInfo` to `false` if you only need the messages
- Combine several channels in one run; the start event is charged once

### Related actors

- [Instagram Scraper](https://apify.com/santamaria-automations/instagram-scraper) - posts and profiles from Instagram.
- [TikTok Search Scraper](https://apify.com/santamaria-automations/tiktok-search-scraper) - videos and creators from TikTok.
- [Reddit Scraper](https://apify.com/santamaria-automations/reddit-scraper) - posts and comments from Reddit.
- [YouTube Channel Scraper](https://apify.com/santamaria-automations/youtube-channel-scraper) - channel data from YouTube.

### Data protection

This actor reads only information that is publicly visible on the Telegram web preview of public channels. Messages, author signatures, forwarded-from names and linked profiles can contain personal data of named individuals. You are responsible for having a lawful basis (for example legitimate interest under Article 6 GDPR) for the way you store and use that data, and for complying with GDPR, national data-protection laws, unfair competition acts and Telegram's terms. Review your use case with a qualified data protection officer before large-scale processing or outreach. Do not use scraped data to contact people who have not agreed to it where the law requires consent.

# Actor input Schema

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

Public Telegram channels to scrape. Accepts @name, name or a t.me link (https://t.me/telegram). Only channels with a public web preview (t.me/s/name) work. Defaults to telegram when empty.

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

Maximum number of messages to return for each channel, newest first. About 20 messages are loaded per page.

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

Optional. Only return messages that contain this word or phrase. The search runs inside each listed channel; Telegram offers no search across all channels without a login.

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

Optional. Stop at messages older than this date (YYYY-MM-DD).

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

Add one row per channel with title, description, subscriber count, photo, verified flag and links. Billed at the channel rate.

## Actor input object example

```json
{
  "channels": [
    "telegram"
  ],
  "maxMessages": 20,
  "includeChannelInfo": true
}
```

# Actor output Schema

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

Dataset containing scraped Telegram message data

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

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

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

```

## MCP server setup

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