# Telegram Profile Scraper: Channel, Group, Bot & User Info (`hyperbach/telegram-profile-scraper`) Actor

Telegram scraper for profiles in bulk: from usernames or t.me links, each telegram channel, group, bot or user with the exact subscriber or member count, title, bio, photo, verified, numeric id, newest post. Optional channel posts with views, comments and keyword search. No login. Misses free.

- **URL**: https://apify.com/hyperbach/telegram-profile-scraper.md
- **Developed by:** [Hyperbach](https://apify.com/hyperbach) (community)
- **Categories:** Social media, Lead generation, News
- **Features:** Limited permissions
- **Stats:** 11 total users, 8 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.15 / 1,000 profiles

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Profile Scraper: Channel, Group, Bot & User Info

**Telegram channel, group, bot and user profiles from a list of usernames or t.me links: the exact subscriber or member count as Telegram writes it, the type, title, description, photo, verified mark, numeric id and newest post — one row per entity, and a free row that says why for a name that does not exist.** Add the newest posts on request, newest first, with views as shown, reactions (paid Stars included), media, link previews, polls, buttons and comments; the creation date and the linked discussion group; and date windows and in-channel search. No login, no Telegram account, no API key, proxies handled for you.

**From $2.25 per 1,000 rows (`profile`) on the Free plan, $1.90 on Starter; every row and add-on is priced under Pricing below.** The default input returns about 3 rows for about $0.00675. A value that names nothing is a free row that says why.

![A Telegram profile page in a browser: an invented channel, Tidewater Radio Club, with a verified mark, 48 213 subscribers and a one-line description. Beside it, the row this Actor returns for the profile, with count, count_type and count_text highlighted: the exact subscriber count as a number, what it counts and the line as the page writes it, then the rounded figure, the entity type, the verified mark and the description.](https://api.apify.com/v2/key-value-stores/dcLLNGq99686o2A4v/records/hero-telegram-profile-scraper.webp)

### Why this Telegram profile scraper

- **Exact counts, labelled.** The profile page writes the exact count ("10 493 438 subscribers"); the channel preview rounds it ("10.5M"). Every row carries the exact `count` with `count_type` and the line as written, next to the rounded `subscribers_shown`. Groups get members and members online, bots their monthly users.
- **The right type, and honest misses.** Every row says `channel`, `group`, `bot` or `user`. Telegram answers a missing username with a normal-looking page — and answers a rate-limited reader with the same page. Each "not found" here was read on two different exits before it was written, and costs nothing. On our 100-name test list: 60 channels, 10 groups, 8 users and 22 missing names, the same split as a separate read of the source.
- **Newest posts first.** Telegram's web preview lists posts oldest first; in our test of the lane, 9 rival runs returned the oldest posts of the page as "the latest". Here posts come newest first and walk back as far as you ask, with views exactly as shown and flagged when rounded (no "2" for 2.09M), reactions with paid Stars, media links, link previews, polls, buttons, forwards and replies. Ready-made: [Telegram channel photos and videos: links from the newest posts](https://apify.com/hyperbach/telegram-profile-scraper/examples/telegram-channel-photos-and-videos-with-links).
- **Facts other public-page tools do not give.** The numeric id of every public channel (and of public groups), in Telegram's and in the Bot API's form; the creation date from the channel's first post ("Channel created"); the linked discussion group; comments of each post. Ready-made: [Vet Telegram channels: creation date, id, views per post](https://apify.com/hyperbach/telegram-profile-scraper/examples/vet-telegram-channels-creation-date-id-and-engagement).
- **Built for lists.** Usernames, @handles, profile, preview, post and invite links in one list; the same entity given three ways is one row.
- **Light and fast.** No browser and no Telegram account: one small page per profile. 100 names in about a minute; misses and rows for values that are not usernames are free.

### Who it's for

- **Marketers and growth teams** — vet channels and groups before a placement: exact subscribers or members, posting rate, views and reactions of the newest posts, verified mark.
- **Researchers and analysts** — a list of channels with their newest posts, dates, views as shown and reactions, or a date window of a channel's history, in one row shape.
- **Developers and agents** — typed fields, one row per input in input order, a free labelled row for every miss, and a run summary to check a pipeline against.

### Quick start

**Profiles of a channel, a bot and a group**

```json
{
  "usernames": [
    "durov",
    "BotFather",
    "https://t.me/tginfochat"
  ]
}
```

**The 20 newest posts of each channel**

```json
{
  "usernames": [
    "durov",
    "telegram"
  ],
  "maxPostsPerChannel": 20
}
```

**Posts of a date window, with comments**

```json
{
  "usernames": [
    "tginfo"
  ],
  "postsNewerThan": "2026-09-01",
  "postsOlderThan": "2026-10-01",
  "maxCommentsPerPost": 10
}
```

**Deep profile: creation date, ids, linked discussion group**

```json
{
  "usernames": [
    "durov",
    "tginfo",
    "tginfochat"
  ],
  "deepProfile": true
}
```

### Output

One `profile` row per entity you named (or a free row saying why there is none) and, when you ask for posts, one `post` row per post (`row_type` says which):

| field | meaning |
|---|---|
| `row_type` | `profile` = one per unique input (the entity's record, or a free row saying why there is none); `post` = one per post of a channel. |
| `input` | The value as you gave it (the first form, when the same entity was given several ways). On post rows: the channel's input. |
| `input_index` | The input's 1-based position in your list. |
| `inputs_merged` | Later values of your list that named the same entity (`@durov` and its link after `durov`); read once, one row. |
| `profile_status` | `ok` = a public channel, group, bot or user (charged). Free rows: `not_found` (no public entity has the name — read on two exits, because Telegram serves a missing name and a rate-limited reader the same page), `invalid` (not a Telegram username or link), `failed` (Telegram answered something else; read again later). |
| `status_detail` | Why a row has no record, in words (null on `ok` rows). |
| `scraped_at` | When the row was read (UTC). |
| `username` | The public username, as Telegram writes it (null for an invite link). |
| `entity_type` | `channel`, `group`, `bot` or `user`, read from the page: the count line (subscribers, members, monthly users), then the title and the button. |
| `title` | The channel's, group's or bot's title, or the user's name. |
| `description` | The description or bio, line breaks and emoji kept. |
| `profile_url` | The profile's public page (`t.me/<username>`). |
| `avatar_url` | The profile photo on Telegram's CDN (a signed link that changes on each read; null when there is no photo). |
| `verified` | Telegram's verified mark. |
| `warning_label` | Telegram's own warning label on the profile, as its page shows it next to the title (`FAKE`, `SCAM`); null when there is none. It is never part of `title`. |
| `invite_link` | The invite link you gave, for a group or channel read through one. |
| `count` | The entity's headline count, exact as Telegram writes it on the profile page: subscribers of a channel, members of a group, monthly users of a bot (null for a user). |
| `count_type` | What `count` counts: `subscribers`, `members` or `monthly_users`. |
| `count_text` | The count line exactly as the page shows it ("10 493 438 subscribers", "5 365 members, 1 163 online"). |
| `counts_precision` | `exact` when `count` is the page's exact figure. |
| `subscribers` | A channel's subscribers, exact. |
| `subscribers_shown` | The subscriber count as the channel's preview rounds it ("10.5M"). |
| `members` | A group's members, exact. |
| `members_online` | A group's members online at the moment of the read. |
| `monthly_users` | A bot's monthly users, exact. |
| `chat_id` | The numeric id of a channel (from its public posts) or of a public group (`deepProfile`, from its first message), in Telegram's own form (1006503122). Null for bots and users: no logged-out page shows theirs. |
| `bot_api_id` | The same id in the Bot API's form (-100 followed by `chat_id`). |
| `has_preview` | The channel shows its posts on the web (its `/s/` preview); only such channels have post rows. |
| `preview_url` | The channel's web preview. |
| `photos_count` | Photos the channel posted, from its preview (rounded above 999, as `counters_shown` says). |
| `videos_count` | Videos the channel posted (rounded above 999). |
| `files_count` | Files the channel posted (rounded above 999; 0 when the preview shows no files counter). |
| `links_count` | Links the channel posted (rounded above 999). |
| `counters_shown` | The preview's counters exactly as written (subscribers "10.5M", photos "1.38K", …). |
| `counters_precision` | `exact` when every counter is written in full, `rounded` when one is written rounded ("1.38K" above 999). |
| `latest_post_id` | The channel's newest post (the last one on its preview: the preview lists posts oldest first). |
| `latest_post_at` | When the newest post was published (UTC). |
| `latest_post_url` | The newest post's link. |
| `created_at` | `deepProfile`: when the channel or group was created, or the earliest date the page shows (see `created_at_source`). |
| `created_at_source` | `channel_created` = post 1, Telegram's "Channel created" message (exact); `first_post` = post 1 is an ordinary post; `oldest_post` = post 1 is gone, the oldest post left (the channel existed by then); `first_message` = a group's first message (the group existed by then); `post_1_embed` = a channel without a public preview: the date of its post 1 in Telegram's embed widget, which does not show whether post 1 is the "Channel created" message (the channel existed by then). |
| `linked_chat_id` | The id of a channel's linked discussion group (`deepProfile` or comments), from the comments widget; on post rows, the group the comments came from. |
| `engagement_posts` | How many of the newest posts (the preview's first page, service messages left out) the engagement fields are computed over. |
| `avg_views` | Average views of those posts, as the page shows them (rounded above 999). |
| `median_views` | Median views of those posts. |
| `avg_reactions` | Average reactions per post (all emoji and paid Stars together). |
| `view_rate` | Median views ÷ subscribers. On post rows: the post's views ÷ the channel's subscribers. |
| `engagement_rate` | Average reactions ÷ average views. |
| `posts_per_week` | Posting rate over the span of those posts. |
| `dominant_script` | The writing system most letters of the description and the newest posts are in (`Latin`, `Cyrillic`, `Arabic`, `Han`, …); links and handles left out. |
| `posts_requested` | Posts asked for this channel (`maxPostsPerChannel`, or 100 when a post filter set it). |
| `posts_delivered` | Post rows this run delivered for the channel. |
| `posts_note` | Why a channel delivered fewer posts than asked (no older post, no public preview, Telegram's search ended, the newest an earlier run delivered, `maxItems` or your charge limit reached), and how comments were read. |
| `deep_note` | `deepProfile`: what could not be read and why — a bot's or a user's id and creation date (Telegram's web pages do not show them; they need a Telegram login, which this Actor does not use), a group whose first message is gone, a channel whose post 1 is gone (`created_at` is then a bound), a channel without a public preview (its id and date come from its post 1's embed; no linked group), an invite link. |
| `markdown` | `includeMarkdown`: the profile and its delivered posts as Markdown. |
| `run_tag` | Your `runTag`, on every row. |
| `channel_title` | On post rows: the channel's title. |
| `channel_subscribers` | On post rows: the channel's exact subscriber count at the time of the run. |
| `post_id` | The post's number in its channel. Deduplicate on `username` + `post_id`. |
| `post_url` | The post's public link. |
| `published_at` | When the post was published (UTC). |
| `is_edited` | The post carries Telegram's "edited" mark. |
| `author_signature` | The author's signature, when the channel signs its posts. |
| `text` | The post's text, line breaks and emoji kept. |
| `text_length` | Characters in `text`. |
| `hashtags` | Hashtags of the post. |
| `mentions` | @usernames the post mentions. |
| `links` | Links in the post's text. |
| `views` | Views as the page shows them, as a number (2.09M → 2090000); `views_precision` says when it is rounded. Telegram shows exact views only to its apps. |
| `views_shown` | Views exactly as written ("2.09M"). |
| `views_precision` | `exact` (under 1,000) or `rounded`. |
| `reactions` | Each reaction: emoji (or the custom emoji's id), count, `paid` for Telegram Stars, the count as written and its precision. |
| `reactions_total` | All reactions of the post together. |
| `reactions_precision` | `rounded` when a reaction count is written rounded on the page ("21.4K"), so `reactions_total` and `paid_stars` are sums of rounded values; `exact` otherwise. |
| `paid_stars` | The paid Star reactions of the post. |
| `forwarded_from` | The name of the chat a forwarded post came from. |
| `forwarded_from_url` | The original post's link, when public. |
| `reply_to_post_id` | The post this one replies to. |
| `media` | Photos (every photo of an album), videos (with duration), GIFs (`gif`: a looping clip, no duration), round videos, voice notes, music files (title, performer), documents (name, size) and stickers, with their CDN links (signed, valid for a while). A video too big for Telegram's web page has its thumbnail and duration, `too_big: true` and no file link (open the post in the app). |
| `media_types` | The kinds of media in the post. |
| `media_not_supported` | True when Telegram's web page does not show the post's media ("Please open Telegram to view this post"): open the post in the app. |
| `link_preview` | The link preview: URL, site, title, description, image. |
| `poll` | A poll: question, type, voters (and as written, with its precision: "71.7K" is rounded), options with their share in percent. |
| `buttons` | Inline buttons under the post: row, text, URL. |
| `matched_search` | Your `postsSearch`, on the posts it found. |
| `comments_count` | How many comments the post has (the widget's own count). |
| `comments_status` | `ok`, `not_available` (Telegram's "Discussion is not available": no public comments for the post), or `not_asked` (the channel's newest 5 posts had no discussion, so older posts were not asked; see `posts_note`). |
| `comments` | `maxCommentsPerPost`: the comments: author name, handle (when public), text or media (a sticker, photo or video), date, the comment it replies to and reactions. |

Example record:

```json
{
  "row_type": "profile",
  "input": "durov",
  "input_index": 1,
  "inputs_merged": [
    "@durov",
    "https://t.me/durov"
  ],
  "profile_status": "ok",
  "status_detail": null,
  "scraped_at": "2026-10-06T20:16:43Z",
  "username": "durov",
  "entity_type": "channel",
  "title": "Pavel Durov",
  "description": "Founder of Telegram.",
  "profile_url": "https://t.me/durov",
  "avatar_url": "https://cdn4.telesco.pe/file/M-TyKlOPLmDf65MjCoCy6WQAAGHv6kEmgSsdHU-98ome4oWbTEAAdsuraeQEFiRHGYWIUdPJKRaT73TGs6QuHxgyF6ELLDS6PE4se9WXx_WYQhf5X4F2XD6TY6CxLUtmn8OWc58w_oT25iNx30RAk6tVioIfIwAsc8m0htRA1pHh3jA6Y6t0Xtjrh6tLu-vBy7F_RmNql9LDoerXArtXkH_UK-BJY7fkel3g8TshvwhElfyOxkVO74VaxMA_HHFf1IBfvLD9wsrsmwjQ8RPbf3jLfw5p-1CaCIuZRyyPAicwDZS4Qqxz3kCPCbCo5V-kNvxg8VU8FH-KnyymhAoVqQ.jpg",
  "verified": true,
  "warning_label": null,
  "invite_link": null,
  "count": 10491495,
  "count_type": "subscribers",
  "count_text": "10 491 495 subscribers",
  "counts_precision": "exact",
  "subscribers": 10491495,
  "subscribers_shown": "10.5M",
  "members": null,
  "members_online": null,
  "monthly_users": null,
  "chat_id": 1006503122,
  "bot_api_id": -1001006503122,
  "has_preview": true,
  "preview_url": "https://t.me/s/durov",
  "photos_count": 102,
  "videos_count": 46,
  "files_count": 0,
  "links_count": 200,
  "counters_shown": {
    "subscribers": "10.5M",
    "photos": "102",
    "videos": "46",
    "links": "200"
  },
  "counters_precision": "exact",
  "latest_post_id": 548,
  "latest_post_at": "2026-09-11T16:04:02Z",
  "latest_post_url": "https://t.me/durov/548",
  "created_at": "2015-10-28T18:20:58Z",
  "created_at_source": "channel_created",
  "linked_chat_id": null,
  "engagement_posts": 20,
  "avg_views": 5529500.0,
  "median_views": 4390000.0,
  "avg_reactions": 132263.4,
  "view_rate": 0.4184,
  "engagement_rate": 0.02392,
  "posts_per_week": 1.51,
  "dominant_script": "Latin",
  "posts_requested": 2,
  "posts_delivered": 2,
  "posts_note": null,
  "deep_note": null,
  "markdown": "# Pavel Durov (@durov)\n\nchannel · 10,491,495 subscribers · verified\n\nFounder of Telegram.\n\nid 1006503122 · created 2015-10-28 · latest post 2026-09-11\n\n## Posts\n\n- [2026-09-11](https://t.me/durov/548) · 2.1M views: 🤝 Telegram has become the sponsor of Codeforces — the largest competitive programming platform in the world.  ⚡CodeForces organizes over 100 coding contests every year and has 4 million contestants signed up.  🌱 Since 2010, I've supported Codeforces through the companies I sta…\n- [2026-09-06](https://t.me/durov/547) · 2.99M views: 💸 In just one month, Telegram has awarded over $2,222,000 to some of the brightest minds on our planet.  🎨 This week alone we distributed $222,000 among the winners of our two latest competitions — the Design Contest and the Digital Freedom Contest.  On top of that, over the p…\n",
  "run_tag": null,
  "channel_title": null,
  "channel_subscribers": null,
  "post_id": null,
  "post_url": null,
  "published_at": null,
  "is_edited": null,
  "author_signature": null,
  "text": null,
  "text_length": null,
  "hashtags": null,
  "mentions": null,
  "links": null,
  "views": null,
  "views_shown": null,
  "views_precision": null,
  "reactions": null,
  "reactions_total": null,
  "reactions_precision": null,
  "paid_stars": null,
  "forwarded_from": null,
  "forwarded_from_url": null,
  "reply_to_post_id": null,
  "media": null,
  "media_types": null,
  "media_not_supported": null,
  "link_preview": null,
  "poll": null,
  "buttons": null,
  "matched_search": null,
  "comments_count": null,
  "comments_status": null,
  "comments": null
}
```

### Pricing

**Pay only for the rows a run delivers**, with no start fee and no platform usage billed to you. Prices fall with your Apify plan.

| per 1,000 | Free | Starter | Scale | Business |
|---|---|---|---|---|
| Profile (`profile`) | $2.25 | $1.90 | $1.55 | $1.15 |
| Post (`post`) | $1.50 | $1.275 | $1.05 | $0.75 |
| Comment on a post row (`comment`) | +$0.50 | +$0.425 | +$0.35 | +$0.25 |

A profile is **$2.25 per 1,000 entities found** on the Free plan and $1.15 on Business; a post $1.50 per 1,000 on Free. The deep profile (creation date, group id, linked discussion group) is part of the profile price. `+` comments are charged per comment delivered on a post row with `maxCommentsPerPost`. **Free:** the row for a name that does not exist (read on two exits), a value that is not a Telegram username or link, a page Telegram did not serve, and a run that failed before its first row. `maxItems` is exact; before the first request the log says the most the run can cost at your plan's prices. Enterprise plans have their own rates — the Actor's Pricing tab shows the price for your plan.

### Usage patterns

- **A list check** — Paste hundreds of usernames or links: each comes back as one row in your order (`input_index`), with its type and exact count, or as a free `not_found` / `invalid` row. Duplicates in any form are read once (`inputs_merged`). Ready-made: [Telegram subscriber and member counts for a list of usernames](https://apify.com/hyperbach/telegram-profile-scraper/examples/telegram-subscriber-and-member-counts-for-a-list).
- **A channel's posts** — `maxPostsPerChannel` with `postsNewerThan` / `postsOlderThan` reads a channel's posts newest first; an old window is reached by a jump on post ids, not by reading every newer page. `postsSearch` uses Telegram's own channel search; `maxCommentsPerPost` adds comments. Ready-made: [Scrape a Telegram channel: its profile and 20 newest posts](https://apify.com/hyperbach/telegram-profile-scraper/examples/scrape-a-telegram-channel-profile-and-newest-posts), [Telegram channel posts from the last 7 days, with comments](https://apify.com/hyperbach/telegram-profile-scraper/examples/telegram-channel-posts-from-the-last-7-days-with-comments), [Search Telegram channel posts for a keyword](https://apify.com/hyperbach/telegram-profile-scraper/examples/search-telegram-channels-posts-for-a-keyword).
- **For a language model** — `includeMarkdown` adds the profile and its posts as one Markdown text per profile row, ready for a prompt or a note.

### Input configuration

| field | type | default | what it does |
|---|---|---|---|
| `usernames` | `array` | `[]` | Telegram channels, groups, bots and users, one per line, in any form: `durov`, `@durov`, `https://t.me/durov`, `t.me/s/durov`, `telegram.me/durov`, a post link (`t.me/durov/548` reads the channel) or an invite link (`t.me/+AbC…`, `t.me/joinchat/…`). One row per entity: the same entity given twice is read once. A value that is not a Telegram username or link comes back as a free row that says why. |
| `maxPostsPerChannel` | `integer` | `0` | How many of each channel's posts to deliver as post rows, newest first; 0 = the profile row only (it still carries the newest post's id and date). Only channels with a public preview have posts on the web; groups, bots and users have none. With a post filter (dates, search or comments) and 0 here, up to 100 posts per channel are read. |
| `postsNewerThan` | `string` | `""` | Only posts published on or after this date: `2026-09-01`, or a span back from today such as `30 days`, `2 weeks`, `6 months` (UTC). With `Posts per channel` at 0, up to 100 posts per channel are read. |
| `postsOlderThan` | `string` | `""` | Only posts published before this date (same forms as `Posts from`). The Actor jumps to that point of the channel by post id instead of reading every newer post. |
| `postsSearch` | `string` | `""` | Only posts that Telegram's own channel search finds for this text (a word, a phrase or a `#hashtag`), newest first. Telegram's search is shallow on large channels: the row says how many posts it returned. |
| `maxCommentsPerPost` | `integer` | `0` | Comments to add to each post row (`comments`: author, handle, text, date, reactions), from the channel's linked discussion group; 0 = none. A channel without public comments says so on each post (`comments_status`). Charged per comment delivered. |
| `deepProfile` | `boolean` | `false` | One or two more requests per channel or group: the creation date (a channel's first post, Telegram's "Channel created" message; a group's first message), the numeric id of a public group, and a channel's linked discussion group (its id). Each fact says where it came from (`created_at_source`). |
| `includeMarkdown` | `boolean` | `false` | Add a `markdown` text to each profile row: the profile and its delivered posts as Markdown, for a language model's context or a note. |
| `maxItems` | `integer` | `0` | The most profile and post rows this run delivers (and charges), together. 0 = no limit. Free rows (not found, not a username) do not count. |
| `runTag` | `string` | `""` | A label copied onto every row (`run_tag`), to tell runs apart in a shared dataset. |
| `feedback` | `string` |  | A note to the people who maintain this Actor: what you needed and did not get. For example a missing field, a wrong or empty result, or the Actor you switch from or to, and why. The run does not read it: the rows and the charges stay the same. A person reads every note. |

### Errors

**A run does not fail because of your input.** When an input cannot be used — a date that is not a date, an id in the wrong form, two settings that contradict each other — the run ends **Succeeded**, charges nothing, and says what to change:

- the status message starts with `INPUT REJECTED`;
- the dataset holds one row, `{"error": true, "code": "…", "message": "…"}`, and no results;
- the key-value store record `ERROR` holds the same object.

From code, check `error` on the first row before reading results.

A search that matches nothing is not an error: the dataset is empty and nothing is charged. A run that ends **Failed** is a fault on our side, never your input; it charges nothing, and we are alerted.

### FAQ

**Why is a name "not found" here when I can open it in the app?**

The row says what the public web page shows. A user who hides their username from the web, a private group or channel, or a name used only inside the app has no public page. Every miss was read on two exits, so it is not a rate limit.

**Are the views exact?**

Telegram shows exact views only inside its apps; the public pages round them above 999 ("2.09M"). `views` is that value as a number and `views_precision` says `rounded`; `views_shown` keeps the text. The subscriber and member counts on the profile row are exact.

**Can I get bot commands, a user's id, last seen or premium status?**

No. Telegram serves those only to a logged-in account, and this Actor does not log in. The numeric id is given for public channels and groups, where the public pages carry it.

**What is charged?**

A profile row of an entity that was found, a post row, and each comment on a post row. Rows for names that do not exist, values that are not usernames, pages Telegram did not serve, and a run that failed before its first row are free; a run that stops early (a timeout, an abort) is charged for the rows it delivered. `maxItems` caps profile and post rows together. The prices are in the Pricing section above.

**Does it read private channels or join groups?**

No. It reads what Telegram shows to anyone on the web: profile pages, the public preview of channels, the public comments widget. An invite link gives the title and member count its page shows.

**Can an AI agent use it (MCP)?**

Yes. Apify's MCP server (`https://mcp.apify.com`) offers Store Actors as tools to Claude, Cursor and other MCP clients: add this Actor and the agent sends `usernames` and gets the rows back. The synchronous API (`run-sync-get-dataset-items`) does the same in one HTTP call.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/telegram-profile-scraper').call({"usernames": ["durov", "BotFather", "https://t.me/tginfochat"]});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/telegram-profile-scraper').call(run_input={'usernames': ['durov', 'BotFather', 'https://t.me/tginfochat']})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/telegram-profile-scraper --input '{"usernames": ["durov", "BotFather", "https://t.me/tginfochat"]}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~telegram-profile-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"usernames": ["durov", "BotFather", "https://t.me/tginfochat"]}'
```

#### AI agents and MCP

Apify's MCP server offers this Actor as a tool to Claude, Cursor, VS Code and any other MCP client. Connect `https://mcp.apify.com?tools=hyperbach/telegram-profile-scraper` and ask, for example: "Get the profiles of the Telegram channel durov, the bot BotFather and the group tginfochat". The agent fills the input from the table above and reads the rows from the run's dataset. The input names are the ones in the table above. An agent or a person can add `feedback` to any run and say what was needed and not delivered: a missing field, a wrong or empty result, the Actor switched from or to, and why. The run returns the same rows at the same price, and a person reads every note.

#### n8n, Make and Zapier

The Apify node in n8n, the Apify app in Make and the Apify integration in Zapier run this Actor on a schedule or from a trigger and read its dataset. A webhook on the run's end (Integrations on the Actor page) sends the rows on to your own endpoint. Nothing to install on our side.

### Support

**Disagree with a charge?** Write to apify@hyperbach.com with the run ID. We will ask Apify to compensate you, and the charge comes back to your Apify account as platform credit.

**Did it do the job?** A short [review on the Store](https://apify.com/hyperbach/telegram-profile-scraper/reviews), at any rating, helps the next buyer choose.

### See also

- **[YouTube Channel Search Scraper](https://apify.com/hyperbach/youtube-channel-search-scraper)** — the YouTube channels a keyword finds, with subscribers, the exact video and view counts and the country.
- **[Threads Scraper](https://apify.com/hyperbach/threads-scraper)** — Threads profiles with the exact follower count, their posts and replies with view counts, and keyword and tag search, without a login.
- **[Substack Scraper](https://apify.com/hyperbach/substack-scraper)** — the posts of a Substack newsletter with the full text of free posts, the date, the paywall flag and the likes.
- **[Weibo Scraper](https://apify.com/hyperbach/weibo-scraper)** — Weibo posts, profiles, search, comments and the hot-search boards, without a login.
- **[Pinterest Search Scraper](https://apify.com/hyperbach/pinterest-search-scraper)** — the pins of a Pinterest search, with the link each pin points to, the image and the pinner.
- **[Spotify Scraper](https://apify.com/hyperbach/spotify-scraper)** — the tracks of a Spotify playlist or album and an artist's top tracks and releases, with play counts.

*This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.*

# Actor input Schema

## `usernames` (type: `array`):

Telegram channels, groups, bots and users, one per line, in any form: `durov`, `@durov`, `https://t.me/durov`, `t.me/s/durov`, `telegram.me/durov`, a post link (`t.me/durov/548` reads the channel) or an invite link (`t.me/+AbC…`, `t.me/joinchat/…`). One row per entity: the same entity given twice is read once. A value that is not a Telegram username or link comes back as a free row that says why.

## `maxPostsPerChannel` (type: `integer`):

How many of each channel's posts to deliver as post rows, newest first; 0 = the profile row only (it still carries the newest post's id and date). Only channels with a public preview have posts on the web; groups, bots and users have none. With a post filter (dates, search or comments) and 0 here, up to 100 posts per channel are read.

## `postsNewerThan` (type: `string`):

Only posts published on or after this date: `2026-09-01`, or a span back from today such as `30 days`, `2 weeks`, `6 months` (UTC). With `Posts per channel` at 0, up to 100 posts per channel are read.

## `postsOlderThan` (type: `string`):

Only posts published before this date (same forms as `Posts from`). The Actor jumps to that point of the channel by post id instead of reading every newer post.

## `postsSearch` (type: `string`):

Only posts that Telegram's own channel search finds for this text (a word, a phrase or a `#hashtag`), newest first. Telegram's search is shallow on large channels: the row says how many posts it returned.

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

Comments to add to each post row (`comments`: author, handle, text, date, reactions), from the channel's linked discussion group; 0 = none. A channel without public comments says so on each post (`comments_status`). Charged per comment delivered.

## `deepProfile` (type: `boolean`):

One or two more requests per channel or group: the creation date (a channel's first post, Telegram's "Channel created" message; a group's first message), the numeric id of a public group, and a channel's linked discussion group (its id). Each fact says where it came from (`created_at_source`).

## `includeMarkdown` (type: `boolean`):

Add a `markdown` text to each profile row: the profile and its delivered posts as Markdown, for a language model's context or a note.

## `maxItems` (type: `integer`):

The most profile and post rows this run delivers (and charges), together. 0 = no limit. Free rows (not found, not a username) do not count.

## `runTag` (type: `string`):

A label copied onto every row (`run_tag`), to tell runs apart in a shared dataset.

## `feedback` (type: `string`):

A note to the people who maintain this Actor: what you needed and did not get. For example a missing field, a wrong or empty result, or the Actor you switch from or to, and why. The run does not read it: the rows and the charges stay the same. A person reads every note.

## Actor input object example

```json
{
  "usernames": [
    "durov",
    "BotFather",
    "https://t.me/tginfochat"
  ],
  "maxPostsPerChannel": 0,
  "postsNewerThan": "",
  "postsOlderThan": "",
  "postsSearch": "",
  "maxCommentsPerPost": 0,
  "deepProfile": false,
  "includeMarkdown": false,
  "maxItems": 0,
  "runTag": ""
}
```

# Actor output Schema

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

All scraped records in the default dataset. One `profile` row per entity you named (or a free row saying why there is none) and, when you ask for posts, one `post` row per post (`row_type` says which):

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

How the run went: profiles and posts charged, free rows by status, types found, misses confirmed on a second exit, and the source counters (requests, exits, bytes).

# 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 = {
    "usernames": [
        "durov",
        "BotFather",
        "https://t.me/tginfochat"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/telegram-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 = { "usernames": [
        "durov",
        "BotFather",
        "https://t.me/tginfochat",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/telegram-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 '{
  "usernames": [
    "durov",
    "BotFather",
    "https://t.me/tginfochat"
  ]
}' |
apify call hyperbach/telegram-profile-scraper --silent --output-dataset

```

## MCP server setup

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