# Telegram Channel Posts Scraper (`techymaki/telegram-channel-posts`) Actor

Telegram channel scraper for public channels: posts and messages with views, reactions, media links and full history by date. No login, phone number or API key. $1 per 1,000 posts.

- **URL**: https://apify.com/techymaki/telegram-channel-posts.md
- **Developed by:** [TechyMaki Labs](https://apify.com/techymaki) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.

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 Scraper

Scrape posts from **public** Telegram channels: text, date, views, reactions, forwarded-from, reply target, media links, text links and link previews. Walks the full channel history back in time, filters by date range, and takes many channels in one run. No Telegram account, login, API key or phone number.

Use it as a **Telegram channel scraper or Telegram messages scraper** for public channels: monitor news and crypto channels, archive a channel's history, or feed posts into sentiment and research pipelines. $1 per 1,000 posts.

### What it can and cannot do

- It reads the public web preview Telegram itself publishes at `https://t.me/s/<channel>`. You get what Telegram exposes publicly there, nothing more.
- **Public channels only.** Private channels, groups, invite links and channels that disabled the web preview return one `{ "channel", "error" }` item. Error items are never charged.
- Pages are fetched politely (3 concurrent requests, short delay). If t.me ever answers 403/429 the actor stops instead of trying to get around it.
- Telegram hides some things in the preview, and the actor reports `null` or an empty list for them instead of guessing:
  - The source of a forwarded post is often blank in the preview. `forwardedFrom` is then `{ "name": null, "url": null }`: you still learn the post is a forward.
  - Very large videos have no playable URL (`url: null`), only a thumbnail.
  - Polls, documents and audio files are not exposed in the preview HTML we tested against, so they are not extracted (see Known limits).
- Subscriber counts and view counts come from Telegram's rounded display values (`1.2K`, `3.4M`), so they are approximate for big numbers.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `channels` | array | required | Usernames (`durov`), `@durov`, or links (`https://t.me/durov`, `https://t.me/s/durov`). Duplicates are removed. |
| `maxPostsPerChannel` | integer | 100 | Newest first. The actor follows `?before=` pagination (about 20 posts per page) until the cap. |
| `dateFrom` | string | none | ISO date or date-time, UTC. Paging stops once posts are older. |
| `dateTo` | string | none | ISO date or date-time, UTC. A date-only value includes the whole day. |
| `includeChannelInfo` | boolean | true | One extra item per channel. |

```json
{ "channels": ["durov", "https://t.me/bloomberg"], "maxPostsPerChannel": 60, "dateFrom": "2026-09-01" }
```

### Output

Items are of three kinds. Channel info (once per channel, first):

```json
{
  "type": "channel",
  "channel": "bloomberg",
  "title": "Bloomberg",
  "description": "The first word in business news.",
  "subscribers": 185000,
  "photoUrl": "https://cdn5.telesco.pe/file/L0xcvHXl…jpg",
  "verified": true,
  "scrapedAt": "2026-10-26T13:00:19.797Z"
}
```

Posts, newest first (real item from a local run; text, reactions and the photo URL shortened here):

```json
{
  "type": "post",
  "channel": "bloomberg",
  "postId": 3224,
  "url": "https://t.me/bloomberg/3224",
  "date": "2026-09-03T03:10:11.000Z",
  "text": "EXCLUSIVE: TikTok developer ByteDance secures a $30 billion loan in Asia’s second-largest dollar-denominated borrowing this year. Read more: https://bloom.bg...",
  "views": 79900,
  "reactions": [
    { "emoji": "❤", "count": 180, "customEmojiId": null, "paid": false },
    { "emoji": "🔥", "count": 143, "customEmojiId": null, "paid": false }
  ],
  "forwardedFrom": null,
  "replyToPostId": null,
  "media": [],
  "links": ["https://bloom.bg/4dbLUqz"],
  "linkPreview": {
    "url": "https://bloom.bg/4dbLUqz",
    "siteName": "Bloomberg.com",
    "title": "ByteDance Gets $30 Billion Loan, Asia’s Second-Largest This Year",
    "description": "ByteDance Ltd., the developer of TikTok, has secured a $29.6 billion loan, according to people familiar with the matter, in Asia’s second-largest dollar-denominated borrowing this year."
  },
  "edited": false,
  "scrapedAt": "2026-10-26T13:00:19.797Z"
}
```

Field notes:

- `media[]` items are `{ "type": "photo" | "video" | "voice" | "sticker", "url" }`; videos also carry `thumbnailUrl`. Albums produce several items.
- `reactions[]`: standard emoji have `emoji`; custom emoji have `customEmojiId` and `emoji: null`; Telegram Stars reactions have `paid: true` and `emoji: "⭐"`.
- `forwardedFrom` is `null` for original posts.
- Missing values are `null` (no text on a photo-only post, no view count on a service post).
- A full real output of 5 channels, including an error item, is in `samples/sample-output.json`.

### Pricing (pay per event)

| Event | Price |
|---|---|
| `post` (one scraped post) | $0.001 (that is $1 per 1,000 posts) |
| `channel-info` (one channel info item) | $0.002 |
| `apify-actor-start` (once per run, Apify's standard start event) | $0.00005 |

Platform compute is included. Error items are free. The actor stops when your spending limit for the run is reached.

### Performance

A local run with 4 channels at 60 posts each (240 posts, 3-4 pages per channel) took about 14 seconds on a home connection. Cloud timings and memory are not measured yet.

### Known limits

- Only what `t.me/s/<channel>` shows. Channels can switch the preview off at any time; then you get the error item.
- Polls, documents and audio posts were not found in the sample pages we parsed, so those media types are not extracted. If a post consists only of an unsupported attachment, `text` is `null` and `media` is empty.
- Telegram can change its preview markup. The parser is tested against saved real pages, so a change shows up as a failing test first.
- Use it on public information and follow Telegram's terms and the laws that apply to you. Post text can contain personal data; you decide what you may do with it.

# Actor input Schema

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

Public Telegram channels: usernames (durov), @names (@durov) or t.me links (https://t.me/durov, https://t.me/s/durov). Private channels and groups are not available.

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

Newest posts first. The actor pages back through the channel history until this many posts are collected.

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

ISO date or date-time (UTC), for example 2026-10-01. Paging stops once posts are older than this.

## `dateTo` (type: `string`):

ISO date or date-time (UTC). A date-only value includes that whole day. Newer posts are skipped.

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

Add one item per channel with title, description, subscriber count and photo.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "maxPostsPerChannel": 100,
  "includeChannelInfo": true
}
```

# Actor output Schema

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

No description

# 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"
    ]
};

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

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

```

## MCP server setup

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