# Telegram Channel Scraper - Posts, Views, Media (`s-r/telegram-channel-scraper`) Actor

Scrape posts from any public Telegram channel: full text, publish time, view counts, media flags, forwards and outbound links, plus channel title and subscriber count. No login, no bot token.

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

## Pricing

Pay per event

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?

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

Read posts from any **public Telegram channel**: the full text, when it was
published, how many people saw it, whether it carries photo or video, what it
forwards, and every external link in it. Plus the channel's title and subscriber
count.

No login. No bot token. No API registration.

### What a run looks like

Pass `durov` and you get its posts with view counts attached:

| Post | Published | Views | Text |
|---|---|---|---|
| durov/546 | 2026-08-31 16:30 | 1,130,000 | Gram Wallet in Telegram is ready… |
| durov/545 | 2026-08-30 09:10 | 1,320,000 | A high-school student just made $20,000… |
| durov/544 | 2026-08-24 21:21 | 2,460,000 | Two years ago, I was detained in Paris… |
| durov/543 | 2026-08-18 17:37 | 3,170,000 | Telegram has applied for the .gram domain… |

Alongside every row: `channel_title` and `subscribers`, so a post's reach can be
read against the channel's size rather than in isolation.

### Views come back twice, and that is deliberate

Telegram displays view counts abbreviated: `1.2K`, `3.17M`. Kept as text, they
sort alphabetically, which puts **1.2K below 999** and quietly ruins any "top
posts" ranking.

So each row carries both: `views_text` exactly as Telegram shows it, and
`views` as an integer you can sort, sum and average. `3.17M` becomes
`3,170,000`.

### Public channels only, and it says so when it hits one

This is the sharpest edge in the data. A private channel, a group or a username
nobody uses does **not** return an error. Telegram serves a perfectly valid page
titled "Telegram: Contact @name", with no posts in it.

Read carelessly, that is indistinguishable from a channel that exists and has
never posted. Those two facts are entirely different, and this Actor separates
them: the page is recognised and reported as `not_a_public_channel`, naming the
channel, while every other channel in the same run continues normally.

### Text keeps its shape

Post text arrives as markup, with `<br/>` for line breaks and a wrapper around
every emoji. Strip the tags first and a fifteen-paragraph post becomes one
unreadable line.

Breaks are converted before tags are removed, so paragraphs survive and emoji
stay where the author put them. That matters if you are feeding posts into
anything that reads them, summarises them or shows them to a person.

### Fields

- **Identity**: `channel`, `message_id`, `post`, `url`
- **Content**: `text`, `text_length`, `posted_at`
- **Reach**: `views`, `views_text`, `subscribers`, `channel_title`
- **Attribution**: `author`, `signature`, `forwarded_from`, `is_forwarded`
- **Media**: `has_photo`, `has_video`, `has_link_preview`, `is_reply`
- **Links**: `outbound_links` — external links only, Telegram's own excluded, so
  what is left is where the channel actually sends its readers

### Input reference

| Field | Type | Default |
|---|---|---|
| `channels` | public channel names | `["durov","telegram"]` |
| `limit` | 1-5000 | 100 |
| `retries` | 1-8 | 4 |

A `t.me/durov` link or `@durov` both work and are reduced to the name. `limit`
is split across the channels you give, so 100 over two channels is roughly 50
each.

Paging walks **backwards through message ids**: each request asks for the posts
older than the lowest id seen so far. There is no page count and no total, so a
run ends when a page returns nothing new.

### Typical uses

- **Competitive and market monitoring.** Track what channels in your sector
  publish and which posts actually travel. `views` against `subscribers` is the
  engagement rate.
- **News and signal collection.** Many announcements land on Telegram first.
  Schedule the Actor and join on `post` to catch only what is new.
- **Content research.** `text` plus `views` shows which topics a channel's
  audience responds to.
- **Link and campaign tracking.** `outbound_links` shows where a channel drives
  traffic, and `is_forwarded` with `forwarded_from` maps how a message spreads
  between channels.
- **Archiving.** Posts with their timestamps and view counts as they stood, for
  channels that edit or delete.

### Notes

Everything here is what Telegram publishes openly for sharing and previewing.
Nothing private is reachable and nothing here tries to be.

`views` is absent on some posts, which is the channel's own setting rather than
a scrape failure, and comes back `null` rather than zero.

Subscriber counts are as shown at the time of the run, and Telegram rounds them:
a channel of 10.9M is reported as 10,900,000, not to the person.

# Actor input Schema

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

Public channel names, for example durov. A t.me link or an @name works too and is reduced to the name.

## `limit` (type: `integer`):

How many posts to return in total, split across the channels you give.

## `retries` (type: `integer`):

Retries with backoff before a page is reported as an error.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "limit": 100,
  "retries": 4
}
```

# Actor output Schema

## `posts` (type: `string`):

One row per channel post.

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

Counts, view coverage and how many posts carry media.

## `errors` (type: `string`):

Failures with a code and a redacted message.

# 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"
    ],
    "limit": 100,
    "retries": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/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",
    ],
    "limit": 100,
    "retries": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/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"
  ],
  "limit": 100,
  "retries": 4
}' |
apify call s-r/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

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