# Telegram Channel Scraper - Posts, Views, Reactions & Media (`wakachobi/telegram-channel-scraper`) Actor

Scrape public Telegram channels without login or API keys: post text, date, views, reactions, photos, videos, files, forwards, replies, link previews, plus channel subscribers and description.

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

## Pricing

$1.50 / 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 Scraper: posts, views, reactions, media and channel info

Scrape any **public Telegram channel** without a Telegram account, phone number, bot token or API key. For every
post you get the text (plain and HTML), date, **view count**, **reactions**, photos, videos, files, polls,
**forwarded-from source**, **reply-to**, link previews and an edited flag. Add channel records with **subscriber
count**, description, photo and verified badge. Filter by date and keywords, including a search through the
channel's whole history.

### What data you get

**Posts** (`type: "post"`)

- Channel username and title, post ID, post URL and publish date (ISO 8601)
- Text as plain text and as HTML (bold, links, quotes kept), plus extracted links, #hashtags and @mentions
- Views and every reaction with its count (unicode emoji, custom emoji ID or paid Telegram Stars), plus a total
- Media type (`text`, `photo`, `video`, `album`, `document`, `voice`, `roundVideo`, `sticker`, `poll`,
  `location`) and **every** photo URL in an album, video file and thumbnail URLs with duration, file names and sizes
- Polls with question, type, options and percentages
- Link preview: URL, site name, title, description and image
- Forwarded-from source (name, username and link to the original post), reply-to post ID, author and snippet
- Edited flag and author signature
- The keyword that matched, when you use keyword filtering

**Channels** (`type: "channel"`)

- Username, title, URL, description (text and HTML), verified, scam and fake flags
- Subscribers and photo, video, file and link counts
- Channel photo URL, latest post ID and date

#### Sample result

```json
{
  "type": "post",
  "channelUsername": "telegram",
  "channelTitle": "Telegram News",
  "postId": 460,
  "url": "https://t.me/telegram/460",
  "date": "2026-08-26T19:12:34+00:00",
  "text": "For all the info from today's update and a look back at 13 years of Telegram features — check out our blog:\nhttps://telegram.org/blog/welcome-messages-buttons-TG-13 …",
  "textHtml": "For all the info from today's update and a look back at <b>13 years of Telegram features</b> …",
  "views": 2040000,
  "isEdited": false,
  "isForwarded": false,
  "forwardedFrom": null,
  "replyTo": null,
  "mediaType": "text",
  "photos": [],
  "videos": [],
  "linkPreview": {
    "url": "https://telegram.org/blog/welcome-messages-buttons-TG-13",
    "siteName": "Telegram",
    "title": "Welcome Messages, Buttons in Messages and Signed Gifts",
    "imageUrl": "https://cdn4.telesco.pe/file/FKjV…jpg"
  },
  "reactions": [],
  "links": ["https://telegram.org/blog/welcome-messages-buttons-TG-13", "https://t.me/telegram/455"],
  "hashtags": [],
  "mentions": []
}
```

```json
{
  "type": "channel",
  "username": "durov",
  "url": "https://t.me/durov",
  "title": "Pavel Durov",
  "description": "Founder of Telegram.",
  "isVerified": true,
  "subscribers": 10300000,
  "photosCount": 102,
  "videosCount": 47,
  "linksCount": 200,
  "photoUrl": "https://cdn4.telesco.pe/file/YPQb…jpg",
  "lastPostId": 552,
  "lastPostDate": "2026-10-11T14:52:40+00:00"
}
```

Reactions look like `{ "emoji": "👍", "customEmojiId": null, "isPaid": false, "count": 369 }`. A forwarded post has
`"forwardedFrom": { "name": "Telegram Contests", "username": "contest", "url": "https://t.me/contest/433" }`.

### How to use

| Field | What it does |
|---|---|
| `channels` | Channel usernames or links: `durov`, `@durov`, `https://t.me/durov`, `https://t.me/s/durov` |
| `maxPostsPerChannel` | Newest posts to save from each channel (default 100). Older history is paged automatically |
| `onlyPostsNewerThan` | Only posts after a date (`2026-01-31`) or within a period (`7 days`, `12 hours`, `3 months`). Paging stops at the cutoff, so you only pay for what you keep |
| `keywords` | Only posts that mention at least one of these words or phrases |
| `useTelegramSearch` | On (default): keywords go through Telegram's in-channel search, which reaches matches anywhere in the history. Off: exact, case-insensitive substring match on the newest posts |
| `scrapePosts` | Turn off for channel records only |
| `includeChannelInfo` | Add one channel record per channel (default on) |
| `resolveForwardedFrom` | Fetch the original source of forwarded posts (one extra request per forward, default on) |
| `maxItems` | Stop after this many results in total (default 50) |

Example inputs:

```json
{ "channels": ["durov", "telegram"], "maxPostsPerChannel": 200, "maxItems": 500 }
```

```json
{ "channels": ["https://t.me/bbcnews", "nytimes"], "onlyPostsNewerThan": "7 days", "maxItems": 1000 }
```

```json
{ "channels": ["durov", "designers"], "keywords": ["contest", "AI"], "includeChannelInfo": false }
```

```json
{ "channels": ["durov", "telegram", "nytimes"], "scrapePosts": false }
```

### Pricing

**$1.50 per 1,000 results**, charged only for items saved to your dataset (posts and channel records). 10,000 posts
cost about $15. Use `onlyPostsNewerThan` and `maxItems` to cap the spend.

### Use cases

- Monitor news, crypto, finance and brand channels for new posts, schedule it daily
- Measure engagement: views and reactions per post, best posting times, top content
- Track mentions of your brand, product or token with keyword search across channel history
- Build datasets for sentiment analysis, AI training, RAG and summarisation
- Compare channels by subscribers and activity for influencer and competitor research

### FAQ

**Is it legal?** The scraper reads only Telegram's public web preview at `t.me/s/<channel>`, the same pages any
visitor sees in a browser without logging in. It does not log in, use the Telegram API or access private channels,
groups or member lists. You are responsible for how you use the data, including personal data and copyright.

**Which channels work?** Public channels with a username and the web preview enabled. Private channels, groups,
users and bots, and channels that disabled the preview, are skipped with a warning.

**Why are view and reaction counts rounded?** Telegram shows them abbreviated (`3.6M`, `9.99K`). They are converted
to numbers, so large counts are accurate to about three digits.

**Why is `forwardedFrom.name` empty?** The original author hid the forward source in their privacy settings. The
post is still flagged `isForwarded: true`.

**Why is a video URL `null`?** Telegram does not serve large videos on the web preview. You still get the thumbnail,
duration and `isTooBigForPreview: true`. Media URLs are temporary CDN links; download files soon after the run.

**How far back can it go?** To the first post of the channel. Each page is 20 posts and pages are fetched at a
gentle pace, so 1,000 posts take a few minutes.

**Keyword search found a post without the exact word?** Telegram's own search decides what matches and can be broader than an exact substring. Turn off
`useTelegramSearch` for strict substring matching on the newest posts.

### Integrations and API

Run it on a schedule with Apify Schedules and send new posts to Google Sheets, Slack, Make, Zapier or a webhook with
Apify integrations. Call it from any language with the Apify API or client libraries (`apify-client` for
JavaScript and Python), or connect it to an AI agent through the Apify MCP server.

# Actor input Schema

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

Public Telegram channels: usernames ('durov', '@durov') or links ('https://t.me/durov', 'https://t.me/s/durov', 'https://t.me/durov/123').

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

Newest posts to save from each channel. The scraper pages back through history until it has this many, reaches the date cutoff or the first post.

## `onlyPostsNewerThan` (type: `string`):

Skip older posts and stop paging once they are reached. A date (2026-01-31) or a relative period ('7 days', '12 hours', '3 months').

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

Only save posts that mention at least one of these words or phrases. Leave empty to save every post.

## `useTelegramSearch` (type: `boolean`):

On: keywords are searched with Telegram's own in-channel search, which finds matches across the whole history fast. Off: posts are paged newest-first and filtered by exact substring (case-insensitive).

## `scrapePosts` (type: `boolean`):

Turn off to get only one channel-info record per channel (title, description, subscribers, photo, verified).

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

Save one record per channel with title, description, subscriber count, media counters, photo and verified badge (type: 'channel').

## `resolveForwardedFrom` (type: `boolean`):

Telegram's channel page hides the original source of forwarded posts. When on, one extra request per forwarded post fetches the source name and link.

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

Stop after this many results in total (posts plus channel records).

## `proxyConfiguration` (type: `object`):

Datacenter proxy works for Telegram's public pages.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "maxPostsPerChannel": 20,
  "useTelegramSearch": true,
  "scrapePosts": true,
  "includeChannelInfo": true,
  "resolveForwardedFrom": true,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": 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"
    ],
    "maxPostsPerChannel": 20,
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("wakachobi/telegram-channel-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": [
        "durov",
        "telegram",
    ],
    "maxPostsPerChannel": 20,
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("wakachobi/telegram-channel-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": [
    "durov",
    "telegram"
  ],
  "maxPostsPerChannel": 20,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call wakachobi/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

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