# Telegram Channel Monitor — Public Channel Posts Scraper (`latentlark/telegram-channel-monitor`) Actor

Scrape and monitor public Telegram channels with no login or API key. Get posts with text, views, reactions, media, links, forwards and replies. Filter by date or keyword, or schedule it with 'only new posts' for alerts and a webhook.

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

## Pricing

from $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 Monitor — Public Channel Posts Scraper

Scrape and monitor **public Telegram channels**. You don't need a login, phone number or API key. Give it channel names or links. Each post comes back as one clean row with text, date, **views**, **reactions by emoji**, media, links, hashtags, mentions, forwards, replies, polls and edit status.

Schedule it with **Only new posts** to get just the posts published since the last run, with an optional webhook to Slack, Zapier, Make or n8n.

![Telegram Channel Monitor results: one row per post with channel, date, text, views, reactions, media type and post link](https://api.apify.com/v2/key-value-stores/Tv899H9p8Kim9T8M2/records/telegram-posts-results-table.png?signature=1OlV0ZmIQ7qresdZlXzuw)

*Real output from a test run. Built and maintained by LatentLark.*

### Quick start (first run in about 30 seconds)

1. Click **Try for free** (or **Start** in Console). The form is prefilled with the `durov` and `telegram` channels and 20 posts per channel.
2. Click **Start**. You get up to 40 posts in the **Output** tab for less than $0.07.
3. Replace the channels with your own. Usernames, `@names` and `t.me` links all work.
4. For monitoring, set **What to scrape** to **Only new posts since last run**, choose a **Monitor name**, then **Save as task** and add a **Schedule** (for example hourly). Add a **Webhook URL** if you want each batch of new posts pushed to Slack, Zapier, Make or n8n.

**How the first run looks** (fill the form, click Start, get results):

![Walkthrough: the prefilled form, click Start, then the posts table in the Output tab](https://api.apify.com/v2/key-value-stores/Tv899H9p8Kim9T8M2/records/telegram-posts-walkthrough.gif?signature=1mREwr5dCzWnS6rxxgZWv)

### What you can do with it

- **News and crypto alerts:** watch breaking-news, exchange and project channels and get new posts within minutes of your schedule.
- **Brand and keyword monitoring:** track mentions of your product, ticker or competitor across many channels with include and exclude keywords.
- **Market and media research:** export a channel's history with views and reactions to find the posts with the most engagement.
- **AI and RAG pipelines:** feed clean post text, links and dates into LLM summaries, sentiment analysis or a vector database.
- **Channel due diligence:** pull exact subscriber counts, descriptions and verified status for a list of channels.

### Input

| Field | What it does |
|---|---|
| `channels` | Usernames (`durov`), `@names` or links (`https://t.me/durov`, `https://t.me/s/durov`, `https://t.me/durov/123`). There is no limit on how many. |
| `mode` | `full` (newest first, back through history), `lastNDays`, `fromDate` (date range with `fromDate` / `toDate`), or `onlyNew` (monitoring). |
| `maxPostsPerChannel` | Cap per channel and run (default 100; 0 = no limit). |
| `includeKeywords` / `excludeKeywords` | Case-insensitive match on the text, link previews, polls and file names. |
| `messageTypes` | e.g. `photo`, `video`, `album`, `document`, `audio`, `voice`, `poll`, `link`, `text`. Empty = all. |
| `minViews` | Only keep posts with at least this many views. |
| `includeChannelProfile` | Adds one profile row per channel with the exact subscriber count, description, photo and verified flag. |
| `monitorKey` | Name of the saved state for `onlyNew` (use one per monitor). |
| `webhookUrl` | Optional: POST the run summary and up to 100 new posts as JSON when the run finishes. |

#### Example inputs

Export the last 30 days of two channels with channel profiles:

```json
{
  "channels": ["durov", "https://t.me/s/tginfo"],
  "mode": "lastNDays",
  "lastNDays": 30,
  "maxPostsPerChannel": 500,
  "includeChannelProfile": true
}
```

Scheduled keyword monitor with a webhook (replace the webhook URL with your own):

```json
{
  "channels": ["durov", "@telegram", "https://t.me/s/tginfo"],
  "mode": "onlyNew",
  "monitorKey": "telegram-news",
  "includeKeywords": ["update", "privacy"],
  "includeChannelProfile": false,
  "webhookUrl": "https://hooks.example.com/telegram"
}
```

### Output

One dataset item per post:

```json
{
  "type": "post",
  "channel": "durov",
  "channelTitle": "Pavel Durov",
  "postId": 552,
  "url": "https://t.me/durov/552",
  "date": "2026-10-11T14:52:40+00:00",
  "text": "Designers × AI ...",
  "views": 1450000,
  "viewsRaw": "1.45M",
  "reactions": [{ "emoji": "👍", "count": 15000, "kind": "emoji" }, { "emoji": "⭐", "count": 6850, "kind": "paid" }],
  "reactionsTotal": 33190,
  "mediaType": "video",
  "media": [{ "type": "video", "thumbnailUrl": "https://cdn4.telesco.pe/...", "duration": "1:15" }],
  "links": ["https://t.me/designers"],
  "hashtags": [],
  "mentions": ["designers"],
  "forwardedFrom": null,
  "replyTo": null,
  "poll": null,
  "linkPreview": null,
  "edited": false,
  "authorSignature": "Pavel Durov",
  "scrapedAt": "2026-10-25T11:05:13+00:00"
}
```

Posts can also include `document` (file or audio title and size), `poll` (question, type, options with percentages), `linkPreview` (site, title, description, image), `cashtags`, `isNew` (in `onlyNew` mode) and `textHtml` (optional). With `includeChannelProfile`, a row with `"type": "profile"` is added for each channel: `title`, `subscribers`, `description`, `verified`, `entityType` (channel or group), `photoUrl`, and photo, video and link counters. A per-channel `SUMMARY` record (pages fetched, channels without a preview, errors) is written to the run's key-value store.

### How monitoring works

In `onlyNew` mode the Actor remembers the last post ID per channel under your `monitorKey`. The first run returns the latest `firstRunMaxPosts` posts (20 by default) per channel as a baseline. Every later run returns, and charges for, only posts published since then. Schedule it every 15 minutes, hourly or daily.

### Pricing

Pay per event, no subscription:

| Event | Price |
|---|---|
| Actor start (per run, at the default 256 MB memory) | $0.005 |
| Post saved | $0.0015 ($1.50 per 1,000 posts) |
| Channel profile saved | $0.001 ($1 per 1,000 profiles) |

Example: an hourly `onlyNew` check on 20 channels that finds 30 new posts costs about $0.05 per run. Posts dropped by your filters or by `onlyNew` are not charged. Set a **maximum cost per run** in Apify and the Actor stops cleanly when it reaches it.

### Limitations

- Works with **public channels that have a web preview** (`t.me/s/<name>`). Private channels, invite links (`t.me/+...`), groups and comment threads are not available. For names without a preview, the run reports it in `SUMMARY`, and the profile row still works.
- Reactions with custom (premium) emoji come back as a `customEmojiId` without a printable emoji.
- Large videos and some media only open in the Telegram app. You get the thumbnail, duration and post link, and `mediaType` is `unsupported` for posts the web preview cannot show. Media URLs from Telegram's CDN are temporary, so download files soon after the run if you need them.
- View counts are rounded by Telegram (for example 1.45M). Profile subscriber counts are exact.
- Keyword search in `full` and date modes uses Telegram's own channel search, which matches whole words. Turn off `useTelegramSearch` to scan every post for substring matches.
- This Actor reads only public pages that anyone can open in a browser. It does not log in or use the Telegram API.

### FAQ

**Can I get alerts in Slack or email?** Yes. Use `webhookUrl`, or schedule the Actor with `onlyNew` and connect an Apify integration (Slack, email, Zapier, Make).
**Can I export to Google Sheets?** Yes. Use Apify's Google Sheets integration or [LatentLark's Google Sheets Export & Import](https://apify.com/latentlark/sheets-export).
**A channel returned 0 posts. Why?** It is probably private, a group, or has no web preview. Open `https://t.me/s/<name>` in a browser: if you see no posts there, the Actor can't see them either. The `SUMMARY` record lists channels without a preview.
**Why are some reactions missing an emoji?** Those are custom (premium) emoji. You get the `customEmojiId` and the count.
**Found a problem?** Open an issue on the Actor's Issues tab.

### More from LatentLark

- [Career Page Jobs API](https://apify.com/latentlark/ats-career-jobs): every open job from Workday, Personio, Greenhouse, Lever, Ashby and 3 more ATS as clean JSON, with only-new-jobs monitoring.
- [Google Sheets Export & Import (No OAuth)](https://apify.com/latentlark/sheets-export): send any Actor's results to a Google Sheet on a schedule without Google login or expiring tokens.
- [Competitor Ads Monitor](https://apify.com/latentlark/competitor-ads-monitor): track competitor ads from Google Ads Transparency Center and LinkedIn Ad Library, with new-ad alerts.
- [Google Maps Leads Website Audit & Pitch Generator](https://apify.com/latentlark/local-lead-audit): audit local business websites from Google Maps leads (SSL, speed, tracking, booking), find owner contacts and get a one-line cold-email pitch.

# Actor input Schema

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

Public Telegram channels, one per line. Use a username (durov), @username, or a link (https://t.me/durov, https://t.me/s/durov, https://t.me/durov/123). Private invite links (t.me/+...) are not supported.

## `mode` (type: `string`):

Full: newest posts back through history, up to the max. Last N days: posts from the last N days. Date range: posts between two dates. Only new posts: for scheduled monitoring. The first run saves a baseline and later runs return only posts published since the last run.

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

Newest first. This caps cost on big channels. 0 = no limit, so the whole public history is scraped.

## `lastNDays` (type: `integer`):

Used with mode 'Last N days'.

## `fromDate` (type: `string`):

Used with mode 'Date range'. Pick a date (YYYY-MM-DD). Via the API you can also send a relative value like '30 days'.

## `toDate` (type: `string`):

Optional end date for mode 'Date range'. Empty = up to now.

## `monitorKey` (type: `string`):

Only-new mode remembers the last seen post per channel under this name. Use different names for independent monitors.

## `firstRunMaxPosts` (type: `integer`):

In only-new mode, the first run (no saved state) returns at most this many latest posts per channel as a baseline.

## `includeKeywords` (type: `array`):

Only keep posts that contain at least one of these words or phrases. Matching is case-insensitive and also checks link previews, polls and file names. In full and date modes, Telegram's channel search is used first, which is much faster on big channels.

## `excludeKeywords` (type: `array`):

Drop posts containing any of these words or phrases.

## `messageTypes` (type: `array`):

Only keep these kinds of posts. Empty = all.

## `minViews` (type: `integer`):

Only keep posts with at least this many views. 0 = no filter.

## `includeServiceMessages` (type: `boolean`):

System messages such as 'Channel photo updated'.

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

Use t.me's built-in channel search when include keywords are set (not in only-new mode). Turn it off to scan every post instead. That is slower, but it catches substring matches the search can miss.

## `includeChannelProfile` (type: `boolean`):

Add one profile row per channel with title, exact subscriber count, description, photo and verified flag. Works even for channels without a web preview.

## `includeTextHtml` (type: `boolean`):

Add the original formatted HTML of each post (bold, links and so on).

## `webhookUrl` (type: `string`):

Optional. After the run, POST a JSON payload with the run summary and up to 100 new posts. Good for Slack, Zapier, Make or n8n.

## `webhookOnlyWhenNew` (type: `boolean`):

Skip the webhook when nothing new was found.

## `maxConcurrency` (type: `integer`):

How many channels to scrape at the same time.

## `requestDelaySecs` (type: `number`):

A small pause between page requests for one channel. It keeps the scraper polite and unblocked.

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

Usually not needed: t.me works without a proxy. If Telegram throttles the run, it automatically switches to Apify datacenter proxy.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "mode": "full",
  "maxPostsPerChannel": 20,
  "lastNDays": 7,
  "monitorKey": "default",
  "firstRunMaxPosts": 20,
  "minViews": 0,
  "includeServiceMessages": false,
  "useTelegramSearch": true,
  "includeChannelProfile": false,
  "includeTextHtml": false,
  "webhookOnlyWhenNew": true,
  "maxConcurrency": 4,
  "requestDelaySecs": 0.5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

Posts found in this run: channel, date, text, views, reactions, media type and link.

## `summary` (type: `string`):

Per-channel counts, pages fetched, channels without a preview, and errors.

# 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"
    ],
    "mode": "full",
    "maxPostsPerChannel": 20,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("latentlark/telegram-channel-monitor").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",
    ],
    "mode": "full",
    "maxPostsPerChannel": 20,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("latentlark/telegram-channel-monitor").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"
  ],
  "mode": "full",
  "maxPostsPerChannel": 20,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call latentlark/telegram-channel-monitor --silent --output-dataset

```

## MCP server setup

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

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/ZBmn0hK8rEFCASVO6/builds/lCGRoWwVkPdfanv5R/openapi.json
