# Telegram Channel Scraper (`datalayer/telegram-channel-intelligence`) Actor

Telegram puts a view count on every public channel post and a subscriber count in the header. Other scrapers return the messages and drop the counter, so a bought audience looks real. This returns both, plus median views as a share of subscribers, reach trend, cadence and links by domain.

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

## Pricing

from $0.70 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 — Views & Engagement Rate

Scrape posts from any **public Telegram channel** and get the number every other
Telegram scraper throws away: **how many people actually saw each post**.

Telegram publishes a view counter on every post in a public channel, and a
subscriber counter in the channel header. Divide one by the other and you have
the only honest measure of a channel's reach. Every scraper on the store returns
the messages and drops the counter.

No account, no API token, no phone number, no MTProto. This reads the same
logged-out public web preview any browser can see.

### What you get that a normal Telegram scraper cannot give you

**Engagement rate.** Median views as a percentage of subscribers. A channel with
600,000 subscribers and a median of 3,000 views has an engagement rate of 0.5%
— the audience is not there. That single number is why this actor exists.

Measured live on 2026-08-14: `@durov` sits at **54% engagement** (11.2M
subscribers, 6.06M median views) and `@telegram` at **14%**. Both healthy. A
bought audience typically lands under 1%.

**Median, not mean.** One viral post drags a mean far above what a typical post
reaches, which is exactly the distortion you are trying to see through.

**Reach trend.** Median views of the newer half of the sample against the older
half. Reach decays long before the subscriber count does, because subscribers
rarely unsubscribe — they just stop opening it. `@durov` measured **−66%** across
the sampled window while his subscriber count kept climbing.

**Best posting hour.** The UTC hour whose posts reach the highest median views,
counting only hours with at least two posts so a single lucky post cannot win.

**What the channel actually promotes.** Every outbound link is extracted and
rolled up by domain. Links back into Telegram are excluded — they are the
platform's own chrome, not a promoted destination.

**Forward attribution.** When a post is a forward, the source channel is
captured, so you can see whose content a channel amplifies.

### Who uses this

- **Crypto and market intel** — verifying whether a channel's audience is real
  before paying for a placement.
- **Brand and PR monitoring** — tracking mentions and their genuine reach.
- **OSINT and research** — mapping which channels amplify which sources.
- **Media buyers** — comparing channels on reach per subscriber rather than on
  a subscriber count anyone can inflate.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `channels` | array | `["durov"]` | Accepts `durov`, `@durov`, `t.me/durov` or the full URL |
| `maxPostsPerChannel` | integer | `100` | Telegram serves 20 posts per page; 100 is five fetches |
| `includePosts` | boolean | `true` | Turn off for analysis rows only |
| `includeAnalysis` | boolean | `true` | The engagement analysis row |
| `since` | string | `""` | ISO date; paging stops once a whole page falls before it |
| `searchText` | string | `""` | Keep posts containing this text |
| `minViews` | integer | `0` | Keep posts at or above this view count |
| `withLinksOnly` | boolean | `false` | Keep only posts carrying an outbound link |

Filters shape the **post rows only**. The analysis is always computed on every
post collected — otherwise a `minViews: 10000` filter would make every channel
look like it reaches 10,000 people.

### Output

Three row types, distinguished by `type`.

**`channel`** — title, description, subscriber count, lifetime photo/video/link
counts. Free; it is context for the analysis, not a second product.

**`channel_analysis`** — `medianViews`, `engagementRate`, `engagementBand`,
`viewsTrendPercent`, `postsPerDay`, `bestHourUtc`, `topPostUrl`,
`mediaSharePercent`, `linkSharePercent`, `forwardSharePercent`,
`topLinkDomains`, `topForwardSources`, `topHashtags`.

**`post`** — `postUrl`, `date`, `text`, `views`, `linkDomains`, `hashtags`,
`mentions`, `mediaTypes`, `isForwarded`, `forwardedFrom`, `isReply`.

Engagement bands: `dead` under 1%, `low` 1–10%, `healthy` 10–30%, `high` 30–70%,
`exceptional` above 70%.

### Reliability

- **Counters are abbreviated.** Telegram renders `11.5M`, not `11,500,000`. Those
  are resolved to integers, and `viewsAreRounded` tells you when the underlying
  figure was abbreviated and is therefore accurate to about three significant
  figures rather than exact.
- **A private channel returns HTTP 200 with an empty page**, not a 404. Verified
  2026-08-14 across several handles. Those are reported as skipped with a reason
  rather than silently returning nothing.
- One channel failing never fails the run.

### Limits

- **Public channels only.** Private channels and groups are invisible to the web
  preview by design, and there is no way to ask it for one.
- View counts are Telegram's own and are abbreviated above 1,000.
- Reactions and comment counts are not in the public preview.

### Pricing

Pay per event. $0.001 per post, $0.05 per channel analysis. The channel row is
free.

# Actor input Schema

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

Public Telegram channels. Accepts durov, @durov or https://t.me/durov. Private channels and groups are not readable from the public web preview and will be reported as skipped.

## `since` (type: `string`):

ISO date, e.g. 2026-01-01. Paging stops once a whole page falls before this date. Leave empty for no lower bound.

## `searchText` (type: `string`):

Keep only posts whose text contains this string, case-insensitive. The analysis is still computed on every post.

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

Keep only posts at or above this view count. 0 means no filter. The analysis is still computed on every post.

## `withLinksOnly` (type: `boolean`):

Keep only posts carrying an outbound link. Useful for tracking what a channel promotes.

## `includePosts` (type: `boolean`):

Return one row per post. Turn off to get only the per-channel engagement analysis.

## `includeAnalysis` (type: `boolean`):

Return the engagement analysis row per channel: median views, engagement rate against subscribers, reach trend, best posting hour.

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

How far back to walk. Telegram serves 20 posts per page, so 100 is five page fetches. Capped at 2000.

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

Optional. Telegram's public preview does not require a proxy; add one only if you are running at high volume.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "since": "",
  "searchText": "",
  "minViews": 0,
  "withLinksOnly": false,
  "includePosts": true,
  "includeAnalysis": true,
  "maxPostsPerChannel": 100,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `analysis` (type: `string`):

One row per channel: median views, engagement rate against subscribers, reach trend, best posting hour.

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

Every post with views, links, hashtags, media types and forward attribution.

## `all` (type: `string`):

Channels, analyses and posts together.

## `csv` (type: `string`):

Everything as a CSV download.

## `runSummary` (type: `string`):

Counts, and any channel that could not be read.

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

// Run the Actor and wait for it to finish
const run = await client.actor("datalayer/telegram-channel-intelligence").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": 100,
}

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

```

## MCP server setup

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

```

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/YmO6cRSVkMA7oEmgB/builds/iyteRc9rER0eJkPEt/openapi.json
