# Telegram Channel Analytics & Engagement Report (`apt_marble/telegram-channel-analytics`) Actor

Turn any public Telegram channel into a performance report: median views, views per subscriber, reaction rate, posting frequency, best hour and weekday to post, content mix and top posts. One row per channel, stating the post count and date span every figure is based on.

- **URL**: https://apify.com/apt\_marble/telegram-channel-analytics.md
- **Developed by:** [Hamza](https://apify.com/apt_marble) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 93.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$8.00 / 1,000 channel reports

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 Analytics & Engagement Report

Paste a list of public Telegram channels and get one clean performance report per channel — median and mean views, how many of the channel's subscribers actually see a post, reaction rate, how often it publishes, the best hour and weekday to post, what kind of content it publishes, and its best-performing posts. It is built for marketers sizing up placements, agencies reporting on client channels, analysts tracking competitors, and anyone who has ever been handed a subscriber number and asked "yes, but does anyone read it?".

Every figure is computed from posts the actor actually read, and every report states how many posts it is based on and what date span they cover — so nothing is implied and nothing is extrapolated.

### What you can do with it

- **Vet a channel before you pay for a placement.** A big subscriber number means nothing on its own; the views-per-subscriber ratio tells you what a post there is really worth.
- **Benchmark yourself against competitors.** Run your channel and five rivals in one go and compare median views, reaction rate and posting frequency side by side.
- **Find your best time to publish.** The report ranks every hour and weekday by how well posts published then actually performed, in the time zone you choose.
- **Track performance over time.** Schedule a weekly run and each report becomes a dated snapshot you can chart.
- **Build a media kit or a placement price list.** One row per channel, ready for a spreadsheet.
- **See what a channel actually publishes** — the split between plain text, photos, videos and albums, its most-used tags, and where it sends people.

### What you get

One row per channel. Abridged sample:

```json
{
  "channel": "varlamov",
  "title": "Илья Варламов",
  "isVerified": true,
  "subscriberCount": 238921,
  "subscriberCountIsExact": true,

  "postsAnalyzed": 300,
  "serviceMessagesExcluded": 0,
  "firstPostDate": "2025-12-20T16:27:36.000Z",
  "lastPostDate": "2026-08-05T13:08:12.000Z",
  "daysCovered": 227.86,
  "activeDays": 207,
  "basisSummary": "300 posts analysed, published 2025-12-20 to 2026-08-05 (227.86 days).",

  "viewsMedian": 303000,
  "viewsMean": 252228,
  "viewsMin": 22100,
  "viewsMax": 428000,
  "viewCountsAreRounded": true,

  "viewToSubscriberRatioPercent": 126.82,
  "engagementRatioIsApproximate": true,
  "reactionsPublished": true,
  "reactionsMedian": 257,
  "reactionRatePercent": 0.085,
  "topReactions": [
    { "emoji": "👍", "totalCount": 83379, "postCount": 300 },
    { "emoji": "👎", "totalCount": 8183, "postCount": 300 }
  ],

  "postsPerDay": 1.32,
  "postsPerWeek": 9.22,
  "medianHoursBetweenPosts": 11.4,
  "bestPostingHour": 13,
  "bestPostingHourLabel": "13:00–13:59",
  "bestPostingHourBasis": "median_views",
  "bestPostingWeekday": "Saturday",
  "timezone": "UTC",

  "mediaMix": { "text": 128, "photo": 91, "video": 49, "album": 30 },
  "forwardedPostCount": 6,
  "topLinkDomains": [{ "domain": "youtu.be", "count": 132 }],

  "topPostsByViews": [
    {
      "messageId": 9871,
      "date": "2026-03-14T09:31:02.000Z",
      "viewCount": 428000,
      "reactionCount": 1204,
      "mediaType": "video",
      "textPreview": "Почему в этом городе не осталось ни одного трамвая…"
    }
  ],
  "scrapedAt": "2026-08-05T13:41:07.000Z"
}
```

### Input reference

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| Telegram channels | list of text | — | The channels to analyse. A channel link or a @username, one per line. Each channel produces one report. Required. |
| Posts per channel to analyse | number, 10–2000 | 200 | How many of the channel's most recent posts the report is based on. More posts gives a steadier picture of typical performance; fewer is faster and cheaper and reflects right now. |
| Reporting time zone | text | `UTC` | The zone used for the best hour, the best weekday and the hourly breakdown — for example `America/New_York`, `Europe/London` or `Europe/Moscow`. An unrecognised zone falls back to UTC and says so in the log. |
| Top posts to include | number, 1–20 | 5 | How many best-performing posts to attach to each report, by views and by reactions. |
| Browse from | select | United States | The country the run browses from. A few channels are restricted in some countries, so this can change what a channel returns. |
| Channels analysed in parallel | number, 1–32 | 16 | How much work the run does at once. Raise it to get through a long list sooner. |

### Output fields

| Field | Description |
| --- | --- |
| `channel`, `channelUrl`, `title`, `description` | Who the report is about, and a link to the channel. |
| `channelId` | Telegram's own numeric id for the channel — stable even when a channel answers on more than one name, so it is the right key to join or dedupe on. |
| `isVerified` | Whether Telegram shows the verified mark. |
| `subscriberCount` / `subscriberCountIsExact` | Subscribers at the moment of collection, and whether that figure is exact or a rounded one published by Telegram. |
| `postsAnalyzed`, `postsRead`, `postsRequested` | How many posts the report is built on, how many were collected, and how many were asked for. |
| `serviceMessagesExcluded` | Automatic notices ("channel photo updated", "pinned …") removed before any average was computed — they carry no views and would drag every figure down. |
| `firstPostDate`, `lastPostDate`, `daysCovered`, `activeDays` | The exact window the report covers, and how many separate days in it had a post. |
| `basisSummary` | The same thing in one human-readable sentence. |
| `newestPostIdAnalyzed`, `oldestPostIdAnalyzed` | The identifiers of the newest and oldest analysed posts. These are identifiers, never a post count. |
| `reachedStartOfHistory` | True when the whole published history of the channel was covered. |
| `viewsMedian`, `viewsMean`, `viewsMin`, `viewsMax`, `viewsTotal` | Reach across the analysed posts. |
| `postsWithViews`, `viewCountsAreRounded` | How many posts published a view figure, and whether any of those figures are rounded by Telegram. |
| `viewToSubscriberRatioPercent` | Median views as a percentage of subscribers — the single number that says whether the audience is real. `meanViewToSubscriberRatioPercent` is the same on the mean. |
| `engagementRatioIsApproximate` | True when the ratio rests on a rounded view or subscriber figure. |
| `reactionsPublished` | False when the channel publishes no reactions at all. Every reaction field is then `null` rather than `0`. |
| `reactionsMedian`, `reactionsMean`, `reactionsTotal`, `postsWithReactions` | Reaction volume across the analysed posts. |
| `reactionRatePercent` | Median reactions as a percentage of median views. |
| `topReactions` | The most-used reactions with their totals. Standard reactions arrive with the emoji itself; custom ones are identified by their own code, and paid ones are flagged as paid. |
| `postsPerDay`, `postsPerWeek`, `medianHoursBetweenPosts` | How often the channel publishes. |
| `bestPostingHour`, `bestPostingHourLabel`, `bestPostingWeekday` | When its posts do best, in your chosen zone. |
| `bestPostingHourBasis`, `bestPostingWeekdayBasis` | `median_views` when performance decided it, `post_volume` when the channel publishes no view figures and only frequency could. |
| `bestPostingHourMedianViews`, `bestPostingWeekdayMedianViews`, `…PostCount` | The evidence behind those picks. |
| `postsByHour`, `postsByWeekday` | The full distributions — 24 hourly slots and seven named days. |
| `mediaMix`, `mediaMixSharePercent` | Posts by kind: plain text, photo, video, album, sticker and so on. |
| `postsWithText`, `postsWithLinks`, `postsWithMedia`, `albumCount`, `editedPostCount`, `replyPostCount` | Content composition. |
| `unviewableMediaCount` | Posts carrying media Telegram does not make viewable outside the app. |
| `forwardedPostCount`, `forwardedPostSharePercent`, `forwardSources` | How much of the channel's output is content forwarded in from other channels, and where from. |
| `topHashtags`, `topLinkDomains` | The tags it uses most and the destinations it sends people to most. |
| `topPostsByViews`, `topPostsByReactions` | Its best posts, each with date, views, reactions, kind and a short text preview. |
| `timezone`, `scrapedAt` | The zone the report was computed in, and when it was collected. |

### Pricing

You pay per channel report.

| What you pay for | Price |
| --- | --- |
| Channel report | **$8.00 per 1,000 reports** — $0.008 each |

Nothing else is billed: the depth you choose, the top-post count and the number of channels analysed at once do not change the price. Channels that cannot be analysed — a name that does not exist, a group, a channel with no public post history — are reported in the log and are **not** charged.

Worked example: analysing 50 channels at 200 posts each produces 50 reports and costs **$0.40**. Re-running the same 50 channels every week for a month costs about **$1.60**. A one-off sweep of 1,000 channels costs **$8.00**.

### Limits & what this actor cannot do

- Only **public** Telegram channels are covered. Private channels, invite-only groups and one-to-one chats are not publicly viewable and are reported as unavailable rather than returned as an empty report.
- **Groups cannot be analysed.** Only channels publish a public post history; a group is reported as having none. Personal profiles, sticker sets and anything else that is not a channel are reported as such.
- Telegram publishes a limited amount of public history, so very large targets return what Telegram makes available rather than an unlimited archive. The report says whether it reached the start of the channel's history.
- View, reaction and subscriber counts are a snapshot at the moment of collection and keep changing afterwards. Two runs a day apart will not match exactly.
- Telegram publishes very large counts in rounded form, so the biggest numbers are approximate rather than exact. Any figure resting on a rounded number is flagged in the report itself.
- Some channels publish no view counts and some publish no reactions. Those fields come back as `null` — meaning "not published" — and never as `0`, which would be a claim the data does not support.
- Some posts carry media Telegram does not make viewable outside the app; they are counted and flagged rather than dropped.
- Some channels are restricted in some countries, so the same channel can return a different result depending on the country the run browses from.
- Telegram does not publish how many times a post was shared, who the subscribers are, or any historical subscriber figure. Nothing here is reconstructed or estimated.
- Speed depends on the size of the job and on Telegram's own response times; no fixed throughput is promised.
- Telegram's terms govern automated access. You are responsible for using the data lawfully and in line with the source site's terms, and for handling any personal data in line with applicable privacy law.

### FAQ

**Do I need a Telegram account?**
No. There is nothing to connect and nothing to sign in to — paste channel names and run it.

**Does it need my phone number or login?**
No. It never asks for a phone number, a password or any credential of any kind, and it only ever reads what Telegram already publishes to everyone.

**Can I schedule it?**
Yes. Schedule it daily, weekly or monthly and each run produces a dated snapshot, which is exactly what you want for tracking a channel over time.

**Is the data complete?**
It is complete for what Telegram publishes publicly, and the report is explicit about its own basis: how many posts it used, what dates they span, how many days of that window had a post, and whether it reached the start of the channel's history. Where Telegram publishes nothing — reactions on a channel that has them switched off, for instance — you get `null`, not a zero.

**Why is the views-per-subscriber ratio above 100%?**
Views are not limited to subscribers: posts get forwarded and shared, so a good channel can be seen far more often than it has subscribers. A ratio well under 100% is the more common signal to look at.

**What counts as a "post" here?**
Anything the channel actually published. A photo album counts once, not once per image, and automatic notices such as "channel photo updated" are excluded from every statistic — they carry no views and would distort the averages.

**Can I get the individual posts too?**
Not from this actor — it deliberately returns one computed report per channel rather than raw posts. Use a post-level Telegram scraper if you need the posts themselves.

# Actor input Schema

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

The public channels to analyse. Paste a channel link or a @username — one per line. Each channel produces one report row.

## `postsToAnalyze` (type: `integer`):

How many of the channel's most recent posts the report is based on. More posts means a steadier picture of typical performance; fewer means a faster, cheaper run focused on right now.

## `timezone` (type: `string`):

The time zone used for the best posting hour, the best weekday and the hourly breakdown. Use a standard zone name such as UTC, America/New\_York, Europe/London, Europe/Moscow or Asia/Dubai.

## `topPostsCount` (type: `integer`):

How many best-performing posts to attach to each report, by views and by reactions.

## `country` (type: `string`):

The country the run browses from. A few channels are restricted in some countries, so this can change what a channel returns.

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

How much work the run does at once. Higher finishes a long list of channels sooner; the default is a comfortable setting for most runs.

## Actor input object example

```json
{
  "channels": [
    "@durov",
    "@telegram"
  ],
  "postsToAnalyze": 100,
  "timezone": "UTC",
  "topPostsCount": 5,
  "country": "us",
  "maxConcurrency": 6
}
```

# Actor output Schema

## `dataset` (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"
    ],
    "postsToAnalyze": 100,
    "timezone": "UTC",
    "topPostsCount": 5,
    "country": "us",
    "maxConcurrency": 6
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/telegram-channel-analytics").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",
    ],
    "postsToAnalyze": 100,
    "timezone": "UTC",
    "topPostsCount": 5,
    "country": "us",
    "maxConcurrency": 6,
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/telegram-channel-analytics").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"
  ],
  "postsToAnalyze": 100,
  "timezone": "UTC",
  "topPostsCount": 5,
  "country": "us",
  "maxConcurrency": 6
}' |
apify call apt_marble/telegram-channel-analytics --silent --output-dataset

```

## MCP server setup

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

```

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/MWuS0O7TwfpC5etwb/builds/r34w0avrSZHdbioMi/openapi.json
