# Telegram Channel Scraper – Posts, Search & Monitoring (`bitofacoder/telegram-channel-scraper`) Actor

Scrape public Telegram channels via the open t.me/s/ web preview — posts, in-channel keyword search, channel info, and an incremental monitor mode that returns only new messages each run. No login, no bot token.

- **URL**: https://apify.com/bitofacoder/telegram-channel-scraper.md
- **Developed by:** [Bobby](https://apify.com/bitofacoder) (community)
- **Categories:** Social media, News, AI
- **Stats:** 4 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.00 / 1,000 results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Telegram Channel Scraper – Posts, Search & Monitoring

Pull posts from any **public** Telegram channel — text, views, dates, forwards, links, and media — as clean CSV/JSON. Then keep it fresh: **monitor mode** returns only the messages posted since your last run, so a scheduled task becomes a live feed instead of a re-scrape.

No login, no bot token, no phone number. Reads Telegram's public `t.me/s/<channel>` web preview.

### What you can do

| Mode | What it does | Fields it uses |
|---|---|---|
| **Channel posts** | Messages from one or more public channels, newest-first. | `channels`, `maxItems` |
| **Search** | In-channel keyword search across each channel. | `channels`, `searchQueries`, `maxItems` |
| **Channel info** | Title, subscribers, description, post/photo/video counts. | `channels` |
| **Monitor** *(incremental)* | Only messages posted **since the last run**, per channel. State persists between runs. | `channels`, `maxItems`, `monitorStoreName` |

### Example input

Latest 100 posts from two channels:

```json
{
  "mode": "channel",
  "channels": ["telegram", "https://t.me/durov"],
  "maxItems": 100
}
```

Track new posts on a schedule (each run returns only what's new):

```json
{
  "mode": "monitor",
  "channels": ["durov", "telegram"],
  "maxItems": 50
}
```

Watch crypto channels for announcement keywords:

```json
{
  "mode": "search",
  "channels": ["binance", "coingecko"],
  "searchQueries": ["listing", "partnership"],
  "maxItems": 50
}
```

`@name`, `name`, `t.me/name`, and `https://t.me/s/name` all work (case-insensitive).

### Scheduled monitoring

To turn `monitor` mode into a live feed: in the Apify Console go to **Schedules → Create**, set an interval (e.g. every 15 minutes), and point it at this Actor with `mode: "monitor"`. Each run stores only the messages posted since the last one. Keep the **same `monitorStoreName`** across runs so the watermark persists, and give a **different** name to each separate monitor task. Pipe the dataset to a webhook/Slack/Sheet via Apify integrations to get alerts.

### Output (per message)

```json
{
  "type": "message",
  "channel": "telegram",
  "id": 123,
  "url": "https://t.me/telegram/123",
  "text": "…",
  "date": "2026-06-22T15:01:34+00:00",
  "views": 1200000,
  "author": null,
  "isForwarded": false,
  "forwardedFrom": null,
  "tags": ["update"],
  "tagList": "update",
  "mentions": [],
  "links": ["https://…"],
  "firstLink": "https://…",
  "media": [{ "type": "photo", "url": "https://…" }],
  "mediaCount": 1,
  "linkPreview": { "url": "…", "siteName": "…", "title": "…", "description": "…" }
}
```

`tagList`, `firstLink`, and `mediaCount` are flat mirrors of the array fields, so CSV/Excel exports are usable without unpacking JSON.

### What this does — and doesn't — return

To keep expectations honest:

- ✅ **Public channel content**: text, date, view count, author signature, forwards, replies, hashtags, mentions, outbound links, media URLs, and link-preview cards. Plus in-channel search, channel info, and incremental monitoring.
- ❌ **Public channels only** — those visible at `t.me/s/<channel>`. Private channels, groups, and user accounts are **not** accessible (the Actor rejects invite links with a clear message).
- ❌ **No member lists, DMs, poll voters, or per-user reactions.** Those require a logged-in account / bot token, which this Actor deliberately does not use. View counts are included; reaction counts are not exposed by the public preview.
- ⚠️ **History depth depends on the public preview.** The Actor pages backward via `?before=`, but coverage is whatever Telegram serves to its web preview — not guaranteed to reach a channel's very first post.

### Notes

- **Proxy is off by default.** The preview pages are public; enable Apify Proxy (or set `requestDelayMs` to ~300–1000) only for large drains or many channels, to spread the per-IP rate limit.
- **`maxTotalItems`** caps results (and charges) across the whole run, independent of the per-channel `maxItems`.
- One bad/private/typo'd channel is logged and skipped — it won't abort the run or lose the other channels' data.
- **How it works / longevity:** this reads Telegram's public `t.me/s/` HTML preview (no API). That preview has been stable for years, but it is an undocumented endpoint — a Telegram markup change can require a parser update (you'll see a "no posts parsed / markup may have changed" warning), and if Telegram ever added JavaScript bot-protection to it, an HTTP-only scraper like this would need a browser-based rework. No reactions or poll-voter data is exposed by the preview.

### Pricing

Pay per result — you're charged once per message (or channel-info record) stored to the dataset. Cap any run with `maxTotalItems`.

# Actor input Schema

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

What to scrape. Each mode uses a different set of fields below.

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

Public channel usernames or t.me links, e.g. \['telegram', 'https://t.me/durov']. The @ and URL parts are optional — 'telegram', '@telegram', and 't.me/telegram' all work.

## `searchQueries` (type: `array`):

Used by 'search' mode. Keywords or phrases to search for inside each channel above.

## `monitorStoreName` (type: `string`):

Named key-value store that holds the per-channel watermark (the last message ID seen) across runs. Use a DISTINCT name per monitor — two monitors sharing one name will overwrite each other's state. Don't run two monitors with the same name at the same time.

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

Upper limit of messages per channel (per query, in search mode). Kept low by default for a fast, cheap first run — raise it to pull more history (the public preview reaches roughly the last few hundred to ~1,000 messages).

## `maxTotalItems` (type: `integer`):

Hard ceiling on the total number of results — and pay-per-result charges — across ALL channels in one run. Leave empty for no overall cap.

## `requestDelayMs` (type: `integer`):

Optional pause before each page fetch. Leave at 0 for normal runs. Raise to ~300–1000 for large drains or many channels without a proxy, to avoid rate limiting.

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

Off by default — t.me/s/ preview pages are public. Enable Apify Proxy to rotate IPs and spread the rate limit on large runs.

## Actor input object example

```json
{
  "mode": "channel",
  "channels": [
    "telegram"
  ],
  "searchQueries": [
    "update"
  ],
  "monitorStoreName": "telegram-monitor-state",
  "maxItems": 20,
  "requestDelayMs": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

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

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

# Run the Actor and wait for it to finish
run = client.actor("bitofacoder/telegram-channel-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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"
  ],
  "searchQueries": [
    "update"
  ]
}' |
apify call bitofacoder/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=bitofacoder/telegram-channel-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/KAS5bV3Puy3EcKJJK/builds/Kli6qlmiPx8lYjEos/openapi.json
