# Telegram Members Scraper (`khadinakbar/telegram-members-scraper`) Actor

Scrape active members from public Telegram groups/channels via public web preview. Use for community research and lead lists. Do not use for private groups or full hidden rosters (login-only). Returns username, name, profile URL, messagesSeen, group context. $0.005 per member.

- **URL**: https://apify.com/khadinakbar/telegram-members-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Social media, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 telegram member founds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Members Scraper

Extract **active members visible on the public Telegram web** from group and channel URLs — username, display name, profile URL, discovery source, activity signals, and source-group context. No Telegram login, cookies, or Bot API key. Built for community research, lead lists from public chats, and AI agents that start with a `t.me` link.

This Actor reads Telegram's public preview pages (`t.me/<username>` and `t.me/s/<username>`). It collects people who appear as message authors, forwarded-from accounts, or `@mentions`, then returns one flat row per distinct account. Prefer chats that open a public message preview in a browser. For channel **message history** (text, views, reactions), continue with [Telegram Channel Scraper](https://apify.com/khadinakbar/telegram-channel-scraper) after you already know the username. When you only have a topic keyword first, discover public chats with [Telegram Keyword Search Scraper](https://apify.com/khadinakbar/telegram-keyword-search-scraper), then pass matching `t.me` URLs here.

### Best fit for this Actor

- You have a **public** `@username` or `https://t.me/...` group/channel and want a bounded list of **active** people visible on the public web.
- You need flat rows with `username`, `profileUrl`, `discoverySource`, and `sourceGroupMemberCount` for enrichment or outreach.
- Best yield comes from public channels (and groups) whose `/s/` message preview is open in a normal browser.

### Community research workflow: from a public channel to active members

A research desk pastes `https://t.me/durov` with `maxMembers` set to 50. The Actor walks the public `/s/` preview pages, dedupes authors, forwards, and mentions, and returns rows ranked by `messagesSeen`. They keep accounts with usernames, export CSV, and optionally re-run with `enrichProfiles: true` for bios. When a chat has a public profile but no public message preview, the Actor finishes as `VALID_EMPTY` with zero billed rows and records the public member/subscriber count in `RUN_SUMMARY.perGroup` so the batch stays interpretable.

### Quick start input

```json
{
  "groupUrls": ["https://t.me/durov"],
  "maxMembers": 5,
  "includeMentions": true,
  "enrichProfiles": false
}
```

`maxMembers` is a hard cap per group after dedupe, so an agent can bound spend before calling.

### Input reference

| Field | Type | What it controls |
|---|---|---|
| `groupUrls` | array (required) | Public usernames, `@handles`, or `t.me` links. Up to 20. Invite/`joinchat`/`+` links are rejected with `INVALID_INPUT`. |
| `maxMembers` | integer | Billed rows per group. Default 50, prefill 5, max 500. |
| `enrichProfiles` | boolean | Fetch each username's public `t.me` bio/photo. Default false. |
| `includeMentions` | boolean | Also collect `@mentions` from preview text. Default true. |
| `includeBots` | boolean | Keep bot-looking accounts. Default false. |
| `proxyConfiguration` | object | Optional proxy override. Default path uses Apify Residential. |

### What data you receive

Each dataset item is one discovered member. Empty/invalid chats leave the dataset empty; use `OUTPUT.outcome` and `RUN_SUMMARY.perGroup` for reasons.

| Field | Description |
|---|---|
| `username`, `displayName`, `profileUrl` | Public identity |
| `bio`, `photoUrl`, `isVerified`, `isBot` | Profile enrich fields (when enabled / inferred) |
| `discoverySource` | `message_author`, `forwarded_from`, or `mention` |
| `messagesSeen`, `lastActiveAt`, `lastMessageId` | Activity signals from the preview scan |
| `sourceGroupUsername`, `sourceGroupTitle`, `sourceGroupUrl`, `sourceGroupType`, `sourceGroupMemberCount` | Provenance |
| `matchFound`, `outcome`, `clearReason` | Billing / honesty contract |

```json
{
  "username": "example_user",
  "displayName": "Example User",
  "profileUrl": "https://t.me/example_user",
  "bio": null,
  "photoUrl": null,
  "isVerified": false,
  "isBot": false,
  "discoverySource": "mention",
  "messagesSeen": 1,
  "lastActiveAt": "ISO-8601 timestamp",
  "lastMessageId": "123",
  "sourceGroupUsername": "durov",
  "sourceGroupTitle": "Durov's Channel",
  "sourceGroupUrl": "https://t.me/durov",
  "sourceGroupType": "channel",
  "sourceGroupMemberCount": null,
  "scrapedAt": "ISO-8601 timestamp",
  "matchFound": true,
  "outcome": "FOUND",
  "clearReason": null
}
```

You can download the dataset as JSON, CSV, Excel, or HTML from the run's Output tab.

### Public-data contract

- **Active/visible members.** Authors, forwards, and mentions from the public `/s/` preview — the identities Telegram shows without login.
- **Preview availability varies.** Many groups keep `/s/` closed; those chats finish `VALID_EMPTY` with the public member count when the profile card provides it.
- **Channels** often attribute posts to the channel itself; forwards and mentions are usually the richest public signal.
- **Invite links** finish as `INVALID_INPUT` with zero `member-found` charges.
- **Unknown usernames** finish `VALID_EMPTY`.

### Why agents choose this Actor

- Bounded cost: `$0.005` per saved member plus a `$0.00005` start event, with `maxMembers` as the ceiling.
- One schema for authors, forwards, and mentions, with `discoverySource` provenance.
- Multi-chat batches can return `PARTIAL` when some chats hide previews while others yield members.
- Terminal `OUTPUT.outcome` values (`COMPLETE`, `PARTIAL`, `VALID_EMPTY`, `INVALID_INPUT`, `UPSTREAM_FAILED`) are stable enough to branch on.
- After discovery, pass known channel usernames to [Telegram Channel Scraper](https://apify.com/khadinakbar/telegram-channel-scraper) for post history.

### Agent checklist

1. Pass public `t.me` URLs or `@handles`. Invite/`+` links belong outside this Actor.
2. Set `maxMembers` to the budget you can spend at `$0.005` per member.
3. After the run, read `OUTPUT.outcome`, then the dataset. Preserve `profileUrl` and `sourceGroupUrl`.
4. Treat `VALID_EMPTY` as a truthful no-visible-identity result.
5. Prefer chats that show a working `/s/` preview in a browser for denser member yield.

### Use through the API

```bash
curl "https://api.apify.com/v2/acts/khadinakbar~telegram-members-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "groupUrls": ["https://t.me/durov"],
    "maxMembers": 5,
    "includeMentions": true,
    "enrichProfiles": false
  }'
```

When the Actor completes, read dataset items from the default dataset and the `OUTPUT` record from the default key-value store.

### Use with AI agents through Apify MCP

> Use khadinakbar/telegram-members-scraper with groupUrls=\["https://t.me/durov"] and maxMembers=20. Return usernames, profile URLs, discoverySource, and sourceGroupMemberCount. Then inspect OUTPUT.outcome and keep t.me links as provenance.

Connect through <https://mcp.apify.com>. After the tool call, read the dataset and the `OUTPUT` / `RUN_SUMMARY` records. Preserve source URLs.

### Pricing

This Actor uses Pay per event plus Apify platform usage. Open the live Pricing tab for current event details, and use Apify's run cost controls to keep the workflow aligned with your budget.

Worked examples at the launch prices (`apify-actor-start` $0.00005, `member-found` $0.005):

| Input | Rows billed | Event cost |
|---|---|---|
| One channel, 5 members | 5 | ~$0.025 |
| One group with no public preview | 0 | start event only |
| Two channels, 10 members each | 20 | ~$0.100 |
| Invite / invalid input | 0 | start event only |

Formula: `member-found count × $0.005 + start event + platform usage`. Platform usage is billed to the user on top of events.

### Outcome vocabulary

These values live in the key-value store records `OUTPUT` and `RUN_SUMMARY`.

| Outcome | Meaning |
|---|---|
| `COMPLETE` | Groups were processed and billed members were saved. |
| `PARTIAL` | Some groups were incomplete or the event limit was hit; saved rows are still in the dataset. |
| `VALID_EMPTY` | Groups were reachable and produced no billed members. |
| `INVALID_INPUT` | No valid public Telegram URLs. Fix the input; no `member-found` events are charged. |
| `UPSTREAM_FAILED` | Public Telegram pages returned no usable data for the requested work. |

### Connect the workflow

- Discover public chats by topic with [Telegram Keyword Search Scraper](https://apify.com/khadinakbar/telegram-keyword-search-scraper), then pass matching `t.me` URLs here for active members.
- After you collect public `@usernames`, scrape that channel's public posts with [Telegram Channel Scraper](https://apify.com/khadinakbar/telegram-channel-scraper).

### Best results

- Prefer public channels (and groups) that show a working `t.me/s/<username>` preview in a browser.
- Keep `maxMembers` at 5–20 for first passes to sample yield before scaling.
- Leave `includeMentions` on for denser channel scans; turn it off for author-only lists.
- Enable `enrichProfiles` when you need bio/photo context for outreach.
- Re-run on a schedule and diff `username` values; this Actor is a public-preview snapshot rather than a live Telegram firehose.

### Builder's note

I built this after probing Telegram's public HTML instead of assuming a member-list API. Public profile cards expose title and member/subscriber counts, while identities come from the `/s/` widget messages — owners, forwarded-from names, and mention links. Many groups redirect `/s/` back to the profile card, so the durable product is an honest active-member sample with unbilled empty semantics (no hollow dataset rows), an honest sample rather than a pretend full roster. In my testing, Residential proxies and a bounded `maxMembers` keep quality prefills inside five minutes while keeping billing aligned with real rows.

### FAQ

**Do I need a Telegram account?**
No. The Actor reads public web pages. Runs without a phone number, session, or Bot API token.

**Will I get every member of a large group?**
This Actor returns identities visible on the public web preview. Full login-gated rosters are a different product class.

**What if a group has thousands of members but zero rows?**
Often the public `/s/` preview is closed. The Actor finishes `VALID_EMPTY` and includes the public member count in `RUN_SUMMARY.perGroup` when available.

**Can an AI agent use this?**
Yes. Pass public `groupUrls`, cap `maxMembers`, then inspect `OUTPUT.outcome` and the dataset profile URLs.

### Responsible use

This Actor returns public Telegram web-preview identities for research, monitoring, and enrichment. Use it in line with Telegram terms and applicable laws, and keep it on public listings.

# Actor input Schema

## `groupUrls` (type: `array`):

Public Telegram groups or channels as usernames, @handles, or t.me links. Example: https://t.me/DatascienceChats. Up to 20 unique chats per run. Invite/joinchat/+hash private links are rejected — this is not a login-session roster dump.

## `maxMembers` (type: `integer`):

Hard cap of billed member rows saved per group after dedupe. Defaults to 50. Prefill 5 keeps the quality test under five minutes. Maximum 500. Raise for broader discovery; each saved member costs $0.005.

## `enrichProfiles` (type: `boolean`):

When true, fetch each discovered username's public t.me profile for bio, photo, and verified flag. Defaults to false. Adds one HTTP request per username. This is not a private Telegram account enrich.

## `includeMentions` (type: `boolean`):

Also collect @usernames mentioned in public preview messages, not only message authors. Defaults to true. Mentions are weaker signals than authors. Disable to keep author-only rows.

## `includeBots` (type: `boolean`):

Keep accounts that look like bots (username ending in bot or display name containing Bot). Defaults to false so human-looking members stay in focus. Not a Telegram Bot API filter.

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

Optional Apify proxy override. Leave empty to use the Actor default Residential path. Enable only if you need a different group. This is not a Telegram account session.

## Actor input object example

```json
{
  "groupUrls": [
    "https://t.me/durov"
  ],
  "maxMembers": 5,
  "enrichProfiles": false,
  "includeMentions": true,
  "includeBots": false
}
```

# Actor output Schema

## `results` (type: `string`):

Discovered members plus CLEAR/failed rows.

## `output` (type: `string`):

Final outcome, itemsPushed, billedResults, and chargedEventCounts.

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

Per-group totals, preview availability, retries, and warnings.

# 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 = {
    "groupUrls": [
        "https://t.me/durov"
    ],
    "maxMembers": 5,
    "enrichProfiles": false,
    "includeMentions": true,
    "includeBots": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/telegram-members-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 = {
    "groupUrls": ["https://t.me/durov"],
    "maxMembers": 5,
    "enrichProfiles": False,
    "includeMentions": True,
    "includeBots": False,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/telegram-members-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 '{
  "groupUrls": [
    "https://t.me/durov"
  ],
  "maxMembers": 5,
  "enrichProfiles": false,
  "includeMentions": true,
  "includeBots": false
}' |
apify call khadinakbar/telegram-members-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/telegram-members-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/S9c2vpm4w6gryJFoF/builds/pyqbBNxcD1mpLCxto/openapi.json
