# Telegram Info Scraper (`solalab_digital/telegram-info-scraper`) Actor

Looks up public Telegram channels, groups, bots and users by username or t.me link: titles, bios, member counts, verification, preview counters, post stats, a liveliness score and an HTML report.

- **URL**: https://apify.com/solalab\_digital/telegram-info-scraper.md
- **Developed by:** [Sankov Vadim](https://apify.com/solalab_digital) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.50 / 1,000 results

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Telegram Info Scraper

Extract Telegram profile data: channels, groups, bots, users. Get member counts, engagement metrics, subscriber trends. 26 fields per target. No token needed.

### Why this Actor

| Feature | What you get |
|---------|------|
| Public data | Channels, groups, bots, users from t.me links or usernames |
| 26 fields | Names, member counts, verification status, post metrics, subscriber deltas |
| Batch processing | 1000 targets per run, 2 concurrent workers (adjustable) |
| Engagement data | Post counts, media breakdowns, frequency, engagement scores |
| Monitoring | Track member count changes across runs using named snapshots |
| No token needed | Works on public HTML only |
| Transparent limits | Clear errors for private/nonexistent targets and rate limits |

### Get started

1. Prepare targets: usernames, @handles, or t.me links (up to 1000)
2. Run the Actor: choose your options (preview posts, include failures, enable monitoring)
3. Download results: CSV or JSON; optional HTML report with filters and sorting

Example:

```
telegram
@BotFather
https://t.me/durov
```

### Input options

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `targets` | string\[] | Yes | Usernames, @handles, t.me or telegram.me URLs. Min 1, max 1000. |
| `fetchPreview` | boolean | No (default: true) | Fetch web preview to extract message counts and latest post date. Channels only; false disables preview requests. |
| `maxPosts` | integer | No (default: 0) | Collect up to N recent posts per target. 0 = no posts in output. Max 500. |
| `includeFailedRows` | boolean | No (default: true) | Include rows for nonexistent, private, or inaccessible targets with status field. false = skip them silently. |
| `compareWithPrevious` | boolean | No (default: false) | Enable monitoring mode: compare current subscriber count against last snapshot. |
| `monitorKey` | string | No (default: "default") | Named snapshot key for monitoring. Separate snapshots per key. |
| `maxConcurrency` | integer | No (default: 2) | Worker threads. Min 1, max 10. Higher = faster but riskier under load. |
| `requestDelayMs` | integer | No (default: 1000) | Pause (ms) between requests per worker. Increase if hitting rate limits. |
| `proxyConfiguration` | proxy object | No | Optional Apify Proxy for rotating IP or avoiding blocks. |

### Output example

```json
{
  "input": "@telegram",
  "status": "ok",
  "entityType": "channel",
  "canonicalUrl": "https://t.me/telegram",
  "sourceUrl": "https://t.me/telegram",
  "telegramId": "1005640892",
  "username": "telegram",
  "usernames": ["telegram"],
  "entityKind": "channel",
  "title": "Telegram News",
  "bio": "The official Telegram channel.",
  "memberCount": 9510000,
  "onlineCount": null,
  "isVerified": true,
  "isScam": null,
  "isFake": null,
  "isRestricted": null,
  "isBot": false,
  "botActiveUsers": null,
  "profilePhotoUrl": "https://cdn1.telesco.pe/file/...",
  "previewUrl": "https://t.me/s/telegram",
  "previewPostCount": 20,
  "latestPostDate": "2026-08-26T19:12:34+00:00",
  "visibleMetricText": "9.51M subscribers",
  "photoCount": 16,
  "videoCount": 228,
  "fileCount": null,
  "linkCount": 378,
  "latestMessageId": 460,
  "scrapedAt": "2026-09-29T10:15:22+00:00",
  "score": 78,
  "postsPerWeek": 5.2,
  "avgViews": 280000,
  "viewsToSubscribers": 0.029,
  "latestPostStale": false,
  "bioLinks": ["https://example.com"],
  "bioMentions": ["@another_channel"],
  "inviteLinks": [],
  "previousMemberCount": 9500000,
  "subscribersDelta": 10000,
  "subscribersDeltaPct": 0.11
}
```

### Connect it

Route results via webhooks. Schedule runs daily, weekly, or custom intervals. Use the Apify API to fetch results. Connect to Zapier (10,000+ apps).

### Output fields

**Entity identification**

- input: Original target string
- entityType: channel, group, bot, user, or null
- entityKind: Raw classification
- canonicalUrl: Normalized t.me link
- sourceUrl: Direct public link

**Profile**

- username: Handle without @
- usernames: Array of known handles
- title: Display name
- bio: Description text (template text removed)
- telegramId: ID number (channels only; null for groups, bots, users)
- isVerified: Has checkmark

**Metrics**

- memberCount: Subscribers or members (parsed from "9.5M" or "4,328")
- onlineCount: Active users now (null if hidden or not a group)
- botActiveUsers: Monthly active (bots only)
- postsPerWeek: Calculated from recent posts (channels with preview)
- avgViews: Average views per post (channels with preview)
- viewsToSubscribers: Ratio of views to subscriber count
- score: Activity score from 0 to 100 (channels only)
- latestPostDate: ISO timestamp of newest post
- latestPostStale: true if last post older than 90 days

**Content**

- photoCount: Photo count in preview (channels only)
- videoCount: Video count in preview (channels only)
- fileCount: File count in preview (channels only)
- linkCount: Link count in preview (channels only)
- previewPostCount: Posts in preview (up to 20)

**Bio extras**

- bioLinks: External URLs from bio
- bioMentions: @usernames in bio
- inviteLinks: Invite links (t.me/+hash, joinchat)

**Monitoring**

- previousMemberCount: Last known count
- subscribersDelta: Change since last run
- subscribersDeltaPct: Change percentage
- previousScrapedAt: Last snapshot timestamp

**Metadata**

- scrapedAt: Collection time
- status: ok, not\_found, private, no\_preview, error
- errorMessage: Error details if status != ok
- profilePhotoUrl: Profile picture URL (null if hidden)
- previewUrl: Web preview link (null if disabled)
- isBot: true if bot
- visibleMetricText: Subscriber line ("9.51M subscribers", "4,328 members, 150 online")
- isScam, isFake, isRestricted: Flags (null; vetting pending)

### Known limits

- Telegram ID: only available for channels, not groups, bots, or user profiles
- Groups: no distinction between group and supergroup in public data
- User vs. nonexistent: private profiles and nonexistent users look identical. Check errorMessage.
- Flags: scam, fake, and restricted badges not confirmed (null)
- Preview: groups, bots, and users have no public message previews
- Outdated posts: inactive channels may show old posts in preview. latestPostStale flag marks this.
- Rate limits: 1000 targets in one run may trigger 429. Use requestDelayMs, retries, or Apify Proxy.

### Pricing

Pay per result. See the Actor page for current pricing.

### Questions

**Do I need a Telegram account or API key?**
No. This scrapes public web pages only.

**What if a target is private or doesn't exist?**
If includeFailedRows is true, you get a row with status "not\_found" or "private" and null data. If false, the target is skipped.

**Why do member counts differ?**
Caching and rounding. We fetch exact numbers from the page; web preview rounds to "9.5M".

**Can I get a member list or all messages?**
No. Public HTML doesn't expose membership or full message history. Preview posts only, up to 20.

**What is the score?**
Activity metric combining frequency, recency, and engagement (views vs. member count). Null if fewer than 3 posts or unknown member count.

**How does monitoring work?**
Enable compareWithPrevious. First run: snapshot member count. Next run: compare and calculate delta. Use monitorKey to separate independent snapshots.

**What is in the HTML report?**
Sortable table. Filters by entity type and status. Top-N by score. Export to CSV.

### Changelog

**v1.0** (2026-09-29)
Initial release: 26 standard fields, monitoring, score, bio parsing, post stats. Batch 1000 targets. HTML report with sorting and filters. Errors reported, not dropped.

### Support

Issues or ideas? Open an issue on the Actor page.

Need help? DM @sulalab on Telegram.

### Related Actors

- Telegram Channel Search: keyword search for channels
- LinkedIn Scraper: profile extraction for LinkedIn
- Instagram Scraper: profile and post metrics for Instagram
- Social Media Aggregator: multi-platform entity lookup

# Actor input Schema

## `targets` (type: `array`):

What to look up: a plain username, an @handle, or any t.me / telegram.me link, including links to a single post or to t.me/s/<name>. Up to 1000 entries; duplicates that resolve to the same username are looked up once. Private links (t.me/c/..., t.me/+..., joinchat) have no public page and are never fetched.

## `fetchPreview` (type: `boolean`):

Also open t.me/s/<name> for channels to read post counters, the channel ID, the latest post date and the liveliness score. Turn it off for one request per target and card fields only. Groups, bots and users have no web preview, so this never costs anything for them.

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

Include the last N posts of each channel in its row, with date, views, reactions and text. 0 keeps rows small and adds no requests. Every 20 posts beyond the first page costs one extra request per channel.

## `includeFailedRows` (type: `boolean`):

Off by default: targets that do not exist, are private or failed are reported in the log and in the HTML report, but no row is written and nothing is charged for them. Turn it on to get a row with status and errorMessage for every target you asked for - those rows are billed like any other.

## `compareWithPrevious` (type: `boolean`):

Load the member counts stored by the last run under the same monitor key and add the change to every row. The first run has nothing to compare against and reports nulls, not zeros.

## `monitorKey` (type: `string`):

Name of the snapshot slot, so separate target lists can be tracked independently.

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

How many targets are looked up at the same time.

## `requestDelayMs` (type: `integer`):

Pause each worker takes after every request. Doubled automatically when Telegram answers 429, then eased back down.

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

Optional Apify Proxy. Not needed for small runs; useful if a long list starts drawing 429s or blocks. Without it the actor connects directly.

## Actor input object example

```json
{
  "targets": [
    "telegram",
    "durov",
    "BotFather"
  ],
  "fetchPreview": true,
  "maxPosts": 0,
  "includeFailedRows": false,
  "compareWithPrevious": false,
  "monitorKey": "default",
  "maxConcurrency": 2,
  "requestDelayMs": 1000,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `report` (type: `string`):

Sortable, filterable table of every row with the liveliness cards, monitoring deltas and CSV export.

## `entities` (type: `string`):

One row per looked-up target.

## `unprocessed` (type: `string`):

Targets left over when a charge limit, an abort or the run timeout stopped the run early. Present only in that case.

# 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 = {
    "targets": [
        "telegram",
        "durov",
        "BotFather"
    ],
    "maxPosts": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("solalab_digital/telegram-info-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 = {
    "targets": [
        "telegram",
        "durov",
        "BotFather",
    ],
    "maxPosts": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("solalab_digital/telegram-info-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 '{
  "targets": [
    "telegram",
    "durov",
    "BotFather"
  ],
  "maxPosts": 0
}' |
apify call solalab_digital/telegram-info-scraper --silent --output-dataset

```

## MCP server setup

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