# Telegram Channel Posts: Messages, Views, Media (`ailu_studio/telegram-channel-posts`) Actor

Public Telegram channel posts from the t.me web preview: text, links, views, reactions, media flags, photo URLs, forwards, replies and channel subscribers. No login, no bot token, no phone number.

- **URL**: https://apify.com/ailu_studio/telegram-channel-posts.md
- **Developed by:** [Ailu Studio](https://apify.com/ailu_studio) (community)
- **Categories:** Social media, News, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 post rows

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Posts: Public Channel Messages, Views, Reactions, Media

Returns posts from public Telegram channels as flat JSON rows, read from Telegram's public web preview (`https://t.me/s/<channel>`): message text with line breaks, links, hashtags, view count, reaction totals, author signature, forwarded-from, reply-to, photo URLs, video/document/poll flags, poll options and link preview cards. Give it any number of channel usernames or links; it pages backwards through each channel's history, newest first, and can stop at a date or keep only posts with your keywords. An optional free row per channel adds title, description and subscriber count. No Telegram account, bot token, phone number, browser or proxy is needed. Compared with the most used Telegram channel Actors there is no start fee, channel rows and error rows are free, and every post row carries reactions, polls and link previews.

### Use cases

- Track what a set of news, crypto or brand channels publish each day, with views and reactions, for dashboards or LLM summaries.
- Monitor channels for keywords (product names, tickers, competitors) with `keywords` and `sinceDays` on a schedule.
- Collect subscriber counts and post engagement for many channels to compare their reach.
- Archive public announcements (text, links, photos, polls) from official channels.

### Input

| Field | Default | Meaning |
|---|---|---|
| `channels` | `["durov", "telegram"]` | Public channel usernames or links: `durov`, `@durov`, `https://t.me/durov`, `https://t.me/s/durov`. |
| `maxPostsPerChannel` | `50` | Posts returned per channel, newest first. About 20 posts per request. |
| `sinceDays` | `0` | Stop at posts older than this many days. 0 = no date limit. |
| `keywords` | `[]` | Keep only posts whose text, link preview or poll question contains one of these words (case-insensitive). |
| `includeChannelInfo` | `true` | Add one free `rowType: "channel"` row per channel (title, description, subscribers, photo, counters). |
| `maxItems` | `200` | Maximum billable post rows for the whole run. |

Running with the defaults returns the latest 50 posts of @durov and @telegram plus two free channel rows in about 6 seconds.

### Output

| Field | Type | Meaning |
|---|---|---|
| `id` | string | `<channel>/<messageId>` |
| `rowType` | string | `post` (billable), `channel` (free), `error` (free) |
| `channel`, `messageId`, `url` | string, integer, string | Channel username, message number, `https://t.me/<channel>/<messageId>` |
| `date` | string | Publication time, ISO 8601 UTC |
| `text` | string or null | Plain text, line breaks kept, emoji kept, formatting removed |
| `links`, `hashtags` | string\[] | Links in the text (deduplicated) and hashtags without `#` |
| `views`, `reactionsTotal`, `paidStars` | integer or null | Counters as shown by Telegram ("5.87M" becomes 5870000) |
| `author` | string or null | Author signature, when the channel signs posts |
| `isEdited`, `isForwarded` | boolean | Edited mark, forwarded mark |
| `forwardedFrom`, `forwardedFromUrl` | string or null | Original chat name and link, when Telegram shows them |
| `hasPhoto`, `hasVideo`, `hasDocument`, `hasPoll` | boolean | Media flags |
| `photoUrls` | string\[] | Photo URLs on Telegram's CDN |
| `linkPreview` | object or null | `{url, siteName, title, description}` |
| `poll` | object or null | `{question, type, options: [{text, percent}]}` |
| `replyToMessageId` | integer or null | Message this post replies to |
| `fetchedAt` | string | ISO 8601 UTC fetch time |

Channel rows add `title`, `description`, `subscribers`, `photoUrl`, `verified`, `photosCount`, `videosCount`, `filesCount`, `linksCount`. Error rows add `error`.

Sample post row (trimmed):

```json
{
  "id": "durov/538",
  "rowType": "post",
  "channel": "durov",
  "messageId": 538,
  "url": "https://t.me/durov/538",
  "date": "2026-08-04T15:20:04.000Z",
  "text": "Last night, Apple briefly removed Telegram from the App Store ...",
  "links": ["https://telegram.org/safety"],
  "hashtags": [],
  "views": 4550000,
  "reactionsTotal": 76200,
  "paidStars": 17900,
  "author": "Pavel Durov",
  "isEdited": true,
  "isForwarded": false,
  "hasPhoto": false,
  "hasVideo": false,
  "linkPreview": { "url": "https://telegram.org/safety", "siteName": "Telegram", "title": "Telegram Safety Overview" },
  "replyToMessageId": null,
  "fetchedAt": "2026-11-04T11:30:00.000Z"
}
```

### How it works and limits

- Reads only the public web preview at `t.me/s/<channel>` (plain HTML, no browser) and pages back with `?before=<messageId>`, about 20 posts per page.
- Politeness: one request per second to t.me, with retries and back-off on HTTP 429 and 5xx. 50 posts per channel take about 3 requests.
- Only public channels with the web preview enabled are available. Private channels, invite links, groups, users, bots, and channels that disabled the preview return a free `error` row and the run continues.
- Counters are the rounded values Telegram displays (views "66.2K" = 66200; large subscriber counts such as "10.1M").
- With `keywords`, up to 25 pages (about 500 posts) per channel are scanned to find matches.
- Not covered: comments, private chats, exact (unrounded) counters, video or file downloads. Photo URLs point to Telegram's CDN and may stop working after some time.
- Service messages (such as "channel created") are skipped.

### Pricing

Pay per result: **$0.90 per 1,000 rows** (`result` event). Platform usage is billed separately by Apify. Removed/free rows are not charged.

Channel info rows and error rows are free.

### Legal

Not affiliated with Telegram. Uses the public Telegram channel web preview (t.me/s) only. Data licence/terms: https://telegram.org/tos

Posts belong to their channel owners. Use the data in line with Telegram's terms and applicable privacy and copyright law.

### FAQ

**Do I need a Telegram account, API ID or bot token?**
No. The Actor reads the same public preview page that anyone can open in a browser.

**Why does a channel return an error row?**
The channel is private, does not exist, is a user, bot or group, or its owner disabled the web preview. Error rows are free.

**Can I get posts older than a date or only recent ones?**
Set `maxPostsPerChannel` high to go far back in history, or `sinceDays` to stop at a date. Paging is newest to oldest.

**Why are view counts round numbers?**
Telegram shows rounded counters in the preview ("5.87M"). The Actor converts them to numbers but cannot recover the exact value.

**Can I run it on a schedule to watch channels?**
Yes. Combine `sinceDays: 1` with a daily Apify schedule, or use `keywords` to receive only matching posts.

# Actor input Schema

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

Public Telegram channel usernames or links, e.g. "durov", "@telegram" or "https://t.me/s/tginfo". Only public channels with the web preview enabled can be read; private channels, groups, users and bots return a free error row.

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

How many posts to return per channel, newest first. The preview shows about 20 posts per page, so 50 posts take about 3 requests (one per second). Default 50.

## `sinceDays` (type: `integer`):

Stop paging back once posts are older than this many days. 0 (default) means no date limit; maxPostsPerChannel still applies.

## `keywords` (type: `array`):

Keep only posts whose text, link preview or poll question contains at least one of these words (case-insensitive). With keywords set, up to 25 pages (about 500 posts) per channel are scanned to find matches. Leave empty to keep every post.

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

Add one free row per channel (rowType "channel") with its title, description, subscriber count, photo and media counters. Default true.

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

Maximum number of billable post rows for the whole run, across all channels. Channel info rows and error rows are free and not counted. Default 200.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "maxPostsPerChannel": 50,
  "sinceDays": 0,
  "keywords": [],
  "includeChannelInfo": true,
  "maxItems": 200
}
```

# Actor output Schema

## `results` (type: `string`):

All rows in the default dataset.

# 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",
        "telegram"
    ],
    "keywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("ailu_studio/telegram-channel-posts").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",
        "telegram",
    ],
    "keywords": [],
}

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ailu_studio/telegram-channel-posts"
        }
    }
}
```

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/HPJlDA0TuGtkA9ArU/builds/f6rsjP7LIuoHYEAUg/openapi.json
