# Telegram Channel & Group Info Scraper (Bulk) (`apt_marble/telegram-channel-profile-scraper`) Actor

Turn a list of Telegram handles into clean rows: channel, group, bot, user or sticker set, with exact subscriber and member counts, online numbers, name, description, picture and verified badge. Handles nobody uses are reported with the reason, never as blank rows.

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

## Pricing

$1.50 / 1,000 profile 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 Channel & Group Info Scraper (Bulk)

Paste a list of Telegram handles and get one clean row back for every single one: whether it is a channel, a group, a bot, a user or a sticker set, exactly how many subscribers or members it has, how many of them are online right now, its name, description, picture, verified badge and a stable numeric id you can join on later. Handles that nobody uses come back marked as such, with the reason — never as a silent blank row. Built for anyone who has a spreadsheet of Telegram handles and needs to know what is actually behind them.

### What you can do with it

- **Qualify an advertising list before you spend.** Check the real audience size of every channel a seller pitched you, in one go.
- **Clean a messy handle list.** Sort a pile of pasted names into channels, groups, bots, users and handles that simply do not exist any more.
- **Track growth week by week.** Run it on a schedule and keep a size history for the channels you care about, with the precise number rather than a rounded one.
- **Enrich competitor research.** Pull names, descriptions, pictures and verified badges for hundreds of channels at once.
- **Prioritise deeper work.** Rank discovered handles by audience size and only dig into the ones worth it.
- **De-duplicate properly.** Several different handles can point at the same channel, and nothing on the surface says so — but every channel row carries that channel's own stable id, so grouping by it collapses the duplicates.

### What you get

One row per handle. An abridged real row:

```json
{
  "requestedHandle": "@durov",
  "handle": "durov",
  "entityType": "channel",
  "isAvailable": true,
  "unavailableReason": null,
  "title": "Pavel Durov",
  "description": "Founder of Telegram.",
  "isVerified": true,
  "audienceCount": 11352734,
  "audienceCountIsExact": true,
  "subscriberCount": 11352734,
  "subscriberCountIsExact": true,
  "memberCount": null,
  "onlineCount": null,
  "countsLine": "11 352 734 subscribers",
  "channelId": -1006503122,
  "hasPublicPosts": true,
  "latestPostId": 538,
  "latestPostDate": "2026-08-04T15:20:04.000Z",
  "scrapedAt": "2026-08-05T09:14:02.118Z"
}
```

A group comes back the same shape, with `memberCount` and `onlineCount` filled in instead of `subscriberCount`. A handle nobody uses comes back as `"isAvailable": false` with `"unavailableReason": "not_found"`.

### Input reference

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `handles` | array of strings | — (required) | The handles to identify, one per line. Accepts `@name`, a plain name, or a full Telegram link; a link to a single post is accepted too and resolves to the channel it belongs to. Private invite links are skipped, because nothing about them is public. |
| `fetchExactCounts` | boolean | `true` | Keep it on to report the precise audience number, down to the last person. Turn it off for a lighter, faster run when a rounded figure is close enough — every row states which of the two it carries. |
| `country` | string | `us` | The country the run browses from. A few channels are restricted in some places, so this can change what a handle resolves to. |
| `handlesAtOnce` | integer | `16` | How many handles to work through simultaneously. Higher finishes a long list sooner; 32 is the ceiling. |

### Output fields

| Field | Type | Description |
| --- | --- | --- |
| `requestedHandle` | string | Exactly what you pasted, so you can join the results back to your own list. |
| `handle` | string | The tidied handle the row is about. |
| `url` | string | Link to the profile on Telegram. |
| `entityType` | string | `channel`, `group`, `bot`, `user`, `sticker_set` or `unknown`. |
| `isAvailable` | boolean | Whether anything public was found behind this handle. |
| `unavailableReason` | string | null | Why not, when nothing was found: `not_found`, `restricted`, `no_public_profile` or `unreadable`. |
| `title` | string | null | Display name. |
| `description` | string | null | The bio or channel description, as written. |
| `avatarUrl` | string | null | Direct address of the profile picture. `null` when there is no real picture. |
| `isVerified` | boolean | Whether Telegram shows the verified badge. |
| `isScam` | boolean | Whether Telegram flags the profile as a scam. |
| `audienceCount` | number | null | Subscribers for a channel, members for a group — one column to sort a mixed list by. |
| `audienceCountIsExact` | boolean | `true` when the number is precise, `false` when it is the rounded figure Telegram published. |
| `subscriberCount` | number | null | Subscribers, for channels. |
| `subscriberCountIsExact` | boolean | Whether that subscriber number is precise. |
| `memberCount` | number | null | Members, for groups. |
| `memberCountIsExact` | boolean | Whether that member number is precise. |
| `onlineCount` | number | null | Members online at the moment of collection, for groups. |
| `countsLine` | string | null | The audience line exactly as Telegram published it. |
| `channelId` | number | null | The channel's own stable numeric id — the only identifier that survives a handle change. |
| `hasPublicPosts` | boolean | Whether this channel publishes its posts publicly. |
| `latestPostId` | number | null | Identifier of the most recent public post. |
| `latestPostDate` | string | null | When that post went out (ISO 8601). |
| `photoCount`, `videoCount`, `linkCount`, `fileCount` | number | null | How much of each kind of content the channel has published, when Telegram states it. |
| `scrapedAt` | string | When this row was collected (ISO 8601). |

### Pricing

You pay per row, and nothing else.

| What you pay for | Price |
| --- | --- |
| Profile scraped — one row per handle | **$1.50 per 1,000** |

Worked example: a list of **2,000 handles** produces 2,000 rows and costs **$3.00**. Every handle produces exactly one row, including the ones that turn out to be unavailable — that answer is usually the reason people run this in the first place — so your bill is simply the length of your list. There is no charge for entries that were rejected before the run started, such as private invite links or text that is not a handle at all.

### Limits & what this actor cannot do

- Only **public** Telegram channels, groups, bots, users and sticker sets 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.
- Subscriber, member and online numbers are a snapshot at the moment of collection and keep changing afterwards.
- Telegram publishes very large numbers in rounded form on some surfaces. Where that is what was available, the row says so through `audienceCountIsExact`, so a rounded figure is never presented as a precise one.
- Groups do not publish a readable post history, so a group row has no `latestPostDate` and no `channelId`. Group name, description, member count and online count are all reported.
- Some channels switch their public post display off. They are still real channels and are reported as such, with `hasPublicPosts` set to `false`.
- Bots, users and sticker sets do not publish an audience size at all, so those columns stay empty rather than showing a made-up zero.
- Nothing here reveals who is inside a channel or group. Telegram publishes no public list of subscribers or members, and this actor does not attempt one.
- Some channels are restricted in some countries, so the same handle can resolve differently depending on the country the run browses from.
- Speed depends on the length of your list 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. Nothing is needed but the list of handles.

**Does it need my phone number or login?**
No. There is no sign-in step of any kind, and you are never asked for a phone number, a password or anything else personal.

**Can I schedule it?**
Yes. Set it to run daily, weekly or monthly on Apify and each run appends fresh rows, which is how you build an audience-size history for a set of channels.

**Is the data complete?**
It is everything Telegram publishes publicly about a handle, and nothing more. Names, descriptions, pictures, verified badges and audience sizes are all covered; anything Telegram keeps inside the app — member lists, private channels, chat history — is not available to anyone from the outside and is not returned here.

**What happens to a handle that does not exist?**
It still gets a row, marked `isAvailable: false` with the reason. That is deliberate: "this handle is free" and "this handle is a bot" are different answers, and both are worth knowing.

**Why do two handles sometimes give identical rows?**
Because one channel can answer on several handles. Group your results by `channelId` and the duplicates collapse into one.

**Can it give me the posts as well?**
Not in this actor — it answers "what is this handle?" for a long list, quickly and cheaply. Post collection is a separate job.

# Actor input Schema

## `handles` (type: `array`):

The handles you want identified — one per line. Paste them as @name, as a plain name, or as a full Telegram link. Post links work too: the actor takes the channel from them. Private invite links are skipped, because nothing about them is public.

## `fetchExactCounts` (type: `boolean`):

Keep this on to report the precise subscriber number, down to the last person. Turn it off for a faster, lighter run when a rounded figure is close enough — every row states which of the two it carries, so you always know what you are looking at.

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

The country the run should browse from. A few channels are restricted in some places, so this can change what a handle resolves to.

## `handlesAtOnce` (type: `integer`):

How many handles to resolve simultaneously. Higher finishes a long list sooner; 16 is a good balance and 32 is the ceiling.

## Actor input object example

```json
{
  "handles": [
    "@durov",
    "@telegram",
    "@tginfochat",
    "@BotFather"
  ],
  "fetchExactCounts": true,
  "country": "us",
  "handlesAtOnce": 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 = {
    "handles": [
        "@durov",
        "@telegram",
        "@tginfochat",
        "@BotFather"
    ],
    "fetchExactCounts": true,
    "country": "us",
    "handlesAtOnce": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/telegram-channel-profile-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 = {
    "handles": [
        "@durov",
        "@telegram",
        "@tginfochat",
        "@BotFather",
    ],
    "fetchExactCounts": True,
    "country": "us",
    "handlesAtOnce": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/telegram-channel-profile-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 '{
  "handles": [
    "@durov",
    "@telegram",
    "@tginfochat",
    "@BotFather"
  ],
  "fetchExactCounts": true,
  "country": "us",
  "handlesAtOnce": 5
}' |
apify call apt_marble/telegram-channel-profile-scraper --silent --output-dataset

```

## MCP server setup

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