# Telegram Comments Scraper (`apt_marble/telegram-comments-scraper`) Actor

Get every public comment under any Telegram post: author name, @handle, text, timestamp, reactions, photos and the comment each one answers — one row each, ready for CSV, Excel or JSON. Point it at a list of posts or a whole channel. No Telegram account, phone number or login needed.

- **URL**: https://apify.com/apt\_marble/telegram-comments-scraper.md
- **Developed by:** [Hamza](https://apify.com/apt_marble) (community)
- **Categories:** Social media, News
- **Stats:** 6 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.50 / 1,000 comment scrapeds

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 Comments Scraper — Discussion Threads

Every public Telegram post can carry a conversation underneath it, and that
conversation is usually the most useful thing on the page: real people, real
opinions, real names. This actor turns any public Telegram post into a clean
table of its comments — who wrote each one, their public @handle, what they
said, when, what reactions it collected, any photos, and which comment it was
answering. Give it a list of posts, or just a channel, and it works through the
newest posts for you. It is built for analysts, community managers, researchers
and marketers who want the discussion rather than the announcement.

### What you can do with it

- **Read the room.** Pull the full discussion under a product launch, a policy
  announcement or a news post and see what people actually said.
- **Track sentiment over time.** Sweep a channel's recent posts on a schedule
  and watch how the tone of its audience moves week to week.
- **Find the voices that matter.** Sort by reactions to surface the comments
  the community itself upvoted, and the accounts behind them.
- **Rebuild whole threads.** Every comment carries the identifier of the
  comment it answers, so a single group-by reassembles the conversation tree.
- **Compare competitors.** Run the same sweep across rival channels and compare
  how much discussion each post generates.
- **Feed a model.** Export as JSON or CSV and use the text for classification,
  summarisation or topic clustering.

### What you get

One row per comment. An abridged real row:

```json
{
  "rowType": "comment",
  "channel": "tginfo",
  "postId": 4430,
  "commentId": 1828198,
  "authorName": "CHET -----",
  "authorUsername": "chet_roller",
  "hasAvatar": true,
  "text": "Ну у меня нет времени возиться ещё и с телеграмом. Опция есть — я воспользовался…",
  "date": "2026-08-02T15:38:23.000Z",
  "isReply": true,
  "replyToCommentId": 1828192,
  "reactions": [{ "emoji": "🤝", "customEmojiId": null, "isPaid": false, "count": 2 }],
  "reactionCount": 2,
  "photoCount": 0,
  "threadCommentCount": 42,
  "threadCommentCountIsExact": true,
  "scrapedAt": "2026-08-05T12:00:00.000Z"
}
```

Posts whose comments are switched off produce one short row of their own
instead, so nothing in your output is a silent gap:

```json
{
  "rowType": "post_status",
  "channel": "varlamov",
  "postId": 60000,
  "status": "comments_disabled",
  "commentsDisabled": true,
  "commentCount": 0
}
```

### Input reference

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `postUrls` | list of text | one example post | The Telegram posts to collect comments from, one per line. Use Telegram's own **Copy Link** on a post and paste the result. |
| `channel` | text | empty | A channel username such as `@tginfo`, or its link. The actor takes that channel's most recent posts and collects the comments under each one. Works on its own or alongside `postUrls`. |
| `maxPosts` | number | `10` | How many recent posts to check when you supply a channel. Maximum 200. Posts with no open discussion cost almost nothing to check. |
| `maxCommentsPerPost` | number | `200` | Upper limit per post. When a discussion is bigger than this, you receive the most recent comments up to the limit. Maximum 5000. |
| `onlyTopLevelComments` | true/false | `false` | Keep only comments written straight to the post and drop the ones answering another comment. `maxCommentsPerPost` applies first, so a post whose newest comments are mostly replies can return only a few rows. |
| `reportPostsWithoutComments` | true/false | `true` | Add one short status row for every post with no open discussion. Switch off to receive comment rows only. |
| `country` | choice | `United States` | Which country Telegram is browsed from. A few channels are restricted in some countries, so pinning this keeps repeat runs consistent. |
| `postsAtOnce` | number | `16` | How many posts are worked through side by side. Maximum 32. |

### Output fields

| Field | Type | Description |
| --- | --- | --- |
| `rowType` | text | `comment` for a comment, `post_status` for a post with no open discussion. |
| `channel` | text | The channel the post belongs to. |
| `postId` | number | The post the comment sits under. |
| `postUrl` | text | Direct link to that post. |
| `commentId` | number | Stable identifier for this comment. |
| `commentUrl` | text | Direct link that opens this comment in Telegram. |
| `authorName` | text | The commenter's display name. |
| `authorUsername` | text | Their public @handle, or `null` when they do not publish one. |
| `authorUrl` | text | Link to their public profile, when they have a public @handle. |
| `authorAvatarUrl` | text | Their profile picture, or `null` when they publish none. |
| `hasAvatar` | true/false | Whether a real profile picture was published. |
| `text` | text | The comment itself, with line breaks preserved. `null` for a photo-only comment. |
| `date` | date | When the comment was posted, in UTC. |
| `isReply` | true/false | Whether this comment answers another comment. |
| `replyToCommentId` | number | The comment it answers, or `null`. |
| `reactions` | list | One entry per reaction: the emoji, its count, whether it was a paid reaction, and a code for custom emoji that have no character of their own. `null` when Telegram publishes no reactions for the comment. |
| `reactionCount` | number | All reactions added together, or `null` when none are published. |
| `photos` | list | Pictures attached to the comment, each with a direct link. |
| `photoCount` | number | How many pictures the comment carries. |
| `discussionId` | text | Identifier of the discussion the comment belongs to — the same for every comment under a channel's posts. |
| `threadCommentCount` | number | How many comments Telegram states the post has. |
| `threadCommentCountIsExact` | true/false | `false` when Telegram published that figure in rounded form. |
| `status` | text | On a status row: `comments_disabled` or `channel_not_found`. |
| `commentsDisabled` | true/false | On a status row: whether the post simply has no open discussion. |
| `scrapedAt` | date | When the row was collected. |

### Pricing

You pay per row written to your dataset — nothing else.

| Event | Charged when | Price per 1,000 |
| --- | --- | --- |
| Comment scraped | Every row the run produces | **$2.50** |

**Worked example.** You sweep a channel's 20 most recent posts. Sixteen of them
have open discussions averaging 60 comments each, and four have comments
switched off. That is 960 comment rows plus 4 status rows — 964 rows, about
**$2.41**. Switch `reportPostsWithoutComments` off and you pay for the 960
comment rows only, about $2.40.

Set `maxCommentsPerPost` to control the ceiling on a busy post, and set a
charging limit on the run if you want a hard cap on spend. When that limit is
reached the run stops cleanly and tells you so.

### 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 empty.
- Comments only exist where the channel owner has switched them on. A post with
  no open discussion is reported as such — it is never returned as a thread that
  happens to have zero comments.
- A post that is no longer available and a post with comments turned off look
  identical from the outside, so both are reported the same way.
- Commenters who do not publish an @handle are returned with their display name
  only. Telegram publishes nothing else about them.
- Reaction and comment counts are a snapshot at the moment of collection and
  keep changing afterwards.
- Telegram publishes very large counts in rounded form, so the biggest figures
  are approximate rather than exact. Every row states which kind it received.
- When you cap the comments per post, you receive the most recent ones up to
  that cap, not a sample from across the discussion.
- Some channels are restricted in some countries, so the same post can return a
  different result depending on the country the run browses from.
- Speed depends on the size of the job and on Telegram's own response times. No
  fixed throughput is promised.
- **Lawful use.** Comments are written by ordinary people, and their names and
  handles are personal data. Telegram's terms govern automated access, and you
  are responsible for using anything you collect lawfully, in line with the
  source site's terms and with applicable privacy law. This actor only ever
  reads what Telegram already publishes to the public.

### FAQ

**Do I need a Telegram account?**
No. Nothing is connected, nothing is signed in, and no account of yours is
involved at any point.

**Does it need my phone number or login?**
No. There is nothing to authorise and nothing to paste in. You supply post links
or a channel name, and that is all.

**Can I schedule it?**
Yes. Point it at a channel, set how many recent posts to check, and schedule it
daily or hourly from Apify. Each run returns what is published at that moment,
so you can track how a discussion grows.

**Is the data complete?**
For a post with an open discussion, the actor works through the whole thread
from the newest comment back to the first, unless you cap it. What it cannot
give you is anything Telegram does not publish publicly: private channels,
comments on posts where discussion is switched off, deleted comments, or details
about a commenter beyond what they have chosen to show.

**What happens to posts with comments switched off?**
They are reported with `status: "comments_disabled"` so you can see the post was
checked. Switch `reportPostsWithoutComments` off if you would rather your
dataset contained comments only.

**Can I collect comments from a group chat?**
No. Only the discussion attached to a public channel post is publicly viewable.
Group conversations themselves are not.

**What formats can I export?**
Anything Apify offers — CSV, Excel, JSON, XML — or read the dataset straight
from your own tooling.

# Actor input Schema

## `postUrls` (type: `array`):

One Telegram post per line, in the form https://t.me/<channel>/<number>. Copy the link straight from the post. Leave this empty if you would rather point the actor at a channel and let it pick the most recent posts itself.

## `channel` (type: `string`):

A channel username such as @tginfo, or its link. The actor takes that channel's most recent posts and collects the comments underneath each one. You can use this on its own or together with the list of post links above.

## `maxPosts` (type: `integer`):

Only used when you supply a channel above. The actor works back from the newest post. Posts whose comments are switched off cost almost nothing to check.

## `maxCommentsPerPost` (type: `integer`):

Upper limit on the comments collected from a single post. When a discussion is bigger than this limit, you get the most recent comments up to the limit. Busy posts can carry hundreds.

## `onlyTopLevelComments` (type: `boolean`):

Keep just the comments written straight to the post, and drop the ones that answer another comment. Useful when you want distinct opinions rather than back-and-forth. The per-post limit above is applied first, so a post whose newest comments are mostly replies can return only a few rows.

## `reportPostsWithoutComments` (type: `boolean`):

Adds one short row for every post whose comments are switched off or no longer available, so your output shows the post was checked instead of leaving a silent gap. Switch it off to receive comment rows only.

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

Which country Telegram is browsed from. Discussions read the same almost everywhere, but a few channels are restricted in some countries, so pinning this keeps repeat runs consistent.

## `postsAtOnce` (type: `integer`):

How many posts are worked through side by side. Higher finishes a large list sooner; lower is gentler and easier to follow in the log.

## Actor input object example

```json
{
  "postUrls": [
    "https://t.me/tginfo/4430"
  ],
  "channel": "@tginfo",
  "maxPosts": 10,
  "maxCommentsPerPost": 100,
  "onlyTopLevelComments": false,
  "reportPostsWithoutComments": true,
  "country": "us",
  "postsAtOnce": 5
}
```

# 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 = {
    "postUrls": [
        "https://t.me/tginfo/4430"
    ],
    "maxPosts": 10,
    "maxCommentsPerPost": 100,
    "onlyTopLevelComments": false,
    "reportPostsWithoutComments": true,
    "country": "us",
    "postsAtOnce": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/telegram-comments-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 = {
    "postUrls": ["https://t.me/tginfo/4430"],
    "maxPosts": 10,
    "maxCommentsPerPost": 100,
    "onlyTopLevelComments": False,
    "reportPostsWithoutComments": True,
    "country": "us",
    "postsAtOnce": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/telegram-comments-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 '{
  "postUrls": [
    "https://t.me/tginfo/4430"
  ],
  "maxPosts": 10,
  "maxCommentsPerPost": 100,
  "onlyTopLevelComments": false,
  "reportPostsWithoutComments": true,
  "country": "us",
  "postsAtOnce": 5
}' |
apify call apt_marble/telegram-comments-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apt_marble/telegram-comments-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/zwbJF3mVQlbops7nU/builds/8rJVZWLqJmzdq3gEv/openapi.json
