# Telegram Channel Scraper: Posts, Views, Reactions & Media (`larkmoor/telegram-channel-posts`) Actor

Scrape posts from any public Telegram channel: text, date, views, reactions, photos, video thumbnails, links, hashtags, forwards and channel subscribers. Date range, in-channel search and keyword filters. No login or phone number. $2 per 1,000 posts.

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

## Pricing

from $1.70 / 1,000 posts

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?

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: Posts, Views, Reactions & Media

Turn any public Telegram channel into a clean dataset: one row per post with the full text, publish date, view count, reactions (with counts per reaction), photos, video thumbnails and duration, link previews, links, hashtags, forwarded-from, author signature, and the channel's title, description and subscriber count.

It reads Telegram's own **public web preview** of a channel (`t.me/s/<channel>`), the page Telegram shows to anyone in a normal browser. No Telegram account, no phone number, no API keys, no session files — and nothing private: closed groups, member lists and private chats are never touched.

### What you can use it for

- **Brand and topic monitoring**: track what news, crypto, tech or industry channels post about you, your competitors or a keyword
- **Media and research**: build an archive of a channel's posts with dates and reach for a study, a dashboard or a newsroom
- **Crypto and trading signals**: pull the latest posts from announcement and signal channels on a schedule
- **Influencer and channel analysis**: compare views, reactions and posting rhythm across channels before buying an ad
- **Content curation**: feed new posts from chosen channels into Slack, a newsletter, an RSS reader or an AI summary

### Why this one

- **$2 per 1,000 posts**, $0.001 per run start, no monthly rent — well under the price of the most-used Telegram scraper
- **Works reliably**: plain HTTP against Telegram's public preview, polite pacing and automatic retries; clear log messages when a channel is private or has its preview turned off
- **Rich rows**: views and reactions as numbers (1.2M becomes 1200000), every reaction with its count, media type, photo URLs, video thumbnails, link preview title, forwards and signatures
- **Filters that save money**: a date window (`postedWithinDays` or `dateFrom`) stops paging at the first older post, Telegram's in-channel search (`searchQuery`), and keyword filters — filtered-out posts are free
- **Channel facts on every row**: title, description, verified badge and subscriber count, ready for spreadsheets
- **256 MB memory**: fast and cheap to run

### Input

| Field | What it does |
|---|---|
| `channels` | Channel usernames or links: `durov`, `@telegram`, `https://t.me/durov` |
| `maxPostsPerChannel` | Newest posts first, up to this many per channel |
| `postedWithinDays` / `dateFrom` | Only posts in this date window |
| `searchQuery` | Telegram's own search inside each channel |
| `keywords` | Keep only posts containing any of these words |
| `skipEmptyPosts` | Leave out service posts with no text or media |
| `includeHtml` | Also return the post text as HTML |
| `delaySeconds` | Pause between page requests (default 1 s) |
| `maxResults` | Hard cap on rows for the run |

Example:

```json
{
  "channels": ["telegram", "https://t.me/durov"],
  "postedWithinDays": 30,
  "maxPostsPerChannel": 200
}
```

### Output

```json
{
  "channel": "durov",
  "channelTitle": "Pavel Durov",
  "channelSubscribers": 10300000,
  "channelVerified": true,
  "postId": 546,
  "url": "https://t.me/durov/546",
  "date": "2026-08-31T16:30:15+00:00",
  "text": "Gram Wallet in Telegram is ready and ...",
  "views": 4020000,
  "reactions": [
    {"emoji": "⭐ (paid star)", "customEmojiId": null, "count": 5980},
    {"emoji": null, "customEmojiId": "5936157098181135162", "count": 13600}
  ],
  "reactionsTotal": 80250,
  "mediaType": "video",
  "photos": null,
  "videoThumbnails": ["https://cdn4.telesco.pe/file/..."],
  "videoDuration": "1:15",
  "linkPreviewUrl": null,
  "links": ["https://t.me/wallet"],
  "hashtags": null,
  "forwardedFrom": null,
  "author": "Pavel Durov",
  "edited": false
}
```

Other columns: `channelDescription`, `videos` (direct video URLs when Telegram includes them in the preview), `documentTitle`, `pollQuestion`, `linkPreviewTitle`, `replyToUrl` and `textHtml` when asked for. `mediaType` is one of text, photo, video, link, document, poll or other.

### How much does it cost?

**$2.00 per 1,000 posts** on the Free plan plus **$0.001 per run**, with automatic discounts on paid Apify plans. Platform compute is included.

Worked examples:

- **Daily monitor of 10 news channels**, about 30 new posts each with `postedWithinDays: 1`: 300 posts, **$0.60 a day**
- **Full history of one channel** with 2,000 posts: **$4.00**
- **Keyword watch** ("airdrop") across 25 crypto channels for the last week: you pay only for matching posts, often **under $0.10**

### FAQ

**Is this legal?**
It reads only what Telegram itself publishes on the open web for public channels — the same `t.me/s/` pages anyone can open without an account, which Telegram does not block with robots.txt. It does not log in, does not use the Telegram API on your behalf, and never touches private groups, member lists or personal chats. You are responsible for how you use the data (for example, respect copyright when republishing posts).

**Why does a channel return nothing?**
Private channels, groups, bots and user accounts have no public preview, and a channel owner can turn the preview off. The log says which case it was.

**Why are some reaction emoji empty?**
Custom (premium) emoji are images in Telegram, so they come with their `customEmojiId` instead of a character. Paid star reactions are marked as such.

**Can I download videos?**
Telegram's preview often shows only a thumbnail for large videos. When it includes the direct video file, it is in `videos`; otherwise `videoThumbnails` and `videoDuration` are filled.

**How far back can it go?**
As far back as the channel's public preview goes, which is usually the whole history. Use a date window to keep runs short.

### Tips

- Schedule it hourly or daily with `postedWithinDays: 1` and send new rows to Slack or a Google Sheet.
- Use `searchQuery` for Telegram's own search (fast, whole history) and `keywords` for exact word filters on the text.
- Sort by `views` or `reactionsTotal` to find a channel's best-performing posts.

### Integrations

Works with Google Sheets, Slack, Zapier, Make, Airtable, webhooks, the Apify API and the Python and JavaScript clients. Download as JSON, CSV, Excel, XML or HTML.

### Changelog

- **2026-10-21** — First release: posts, views, reactions, media, links, hashtags, forwards, channel facts, date window, in-channel search and keyword filters.

# Actor input Schema

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

Public Telegram channel usernames or links: 'durov', '@telegram', 'https://t.me/durov' or 'https://t.me/s/durov'. Only public channels with a web preview work (no private groups).

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

Newest posts first; stop after this many per channel.

## `postedWithinDays` (type: `integer`):

Only posts from the last N days; paging stops at the first older post. Empty = no date limit.

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

Alternative to 'Posted within': only posts on or after this date (UTC).

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

Telegram's own in-channel search (same as the search box on t.me/s/<channel>). Empty = all posts.

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

Keep only posts whose text contains any of these words (case-insensitive). Filtered-out posts are free.

## `skipEmptyPosts` (type: `boolean`):

Leave out service posts (pinned notices, channel photo changes).

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

Also return the post text as HTML (keeps bold, links and line breaks).

## `delaySeconds` (type: `number`):

Pause between page requests to stay polite (minimum 0.5).

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

Hard cap on rows for the whole run (you pay per post).

## Actor input object example

```json
{
  "channels": [
    "telegram",
    "durov"
  ],
  "maxPostsPerChannel": 20,
  "skipEmptyPosts": false,
  "includeHtml": false,
  "delaySeconds": 1,
  "maxResults": 40
}
```

# Actor output Schema

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

All results as JSON items in the run's default dataset (also downloadable as CSV/Excel from the 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": [
        "telegram",
        "durov"
    ],
    "maxPostsPerChannel": 20,
    "maxResults": 40
};

// Run the Actor and wait for it to finish
const run = await client.actor("larkmoor/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": [
        "telegram",
        "durov",
    ],
    "maxPostsPerChannel": 20,
    "maxResults": 40,
}

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

```

## MCP server setup

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