Telegram Profile Scraper: Channel, Group, Bot & User Info
Pricing
from $1.50 / 1,000 profiles
Telegram Profile Scraper: Channel, Group, Bot & User Info
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, reactions, comments and keyword search. No login.
Pricing
from $1.50 / 1,000 profiles
Rating
0.0
(0)
Developer
Hyperbach
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
4
Monthly active users
3 days ago
Last modified
Categories
Share
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; date windows and in-channel search; and on a schedule, only new posts and the change of the count since the last run. No login, no Telegram account, no API key, proxies handled for you.
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
countwithcount_typeand the line as written, next to the roundedsubscribers_shown. Groups get members and members online, bots their monthly users. - The right type, and honest misses. Every row says
channel,group,botoruser. 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.
- 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.
- Built for lists and schedules. Usernames, @handles, profile, preview, post and invite links in one list; the same entity given three ways is one row.
onlyNewPostsdelivers only posts published since the last run;onlyChangeddelivers a profile only when it changed; a named memory adds the count's change per day and its history. - 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.
- Monitoring and trust teams — a scheduled list that reports what changed: new posts only, count changes per day, titles and descriptions that moved, names that disappeared.
- 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
{"usernames": ["durov","BotFather","https://t.me/tginfochat"]}
The 20 newest posts of each channel
{"usernames": ["durov","telegram"],"maxPostsPerChannel": 20}
Posts of a date window, with comments
{"usernames": ["tginfo"],"postsNewerThan": "2026-09-01","postsOlderThan": "2026-10-01","maxCommentsPerPost": 10}
Deep profile: creation date, ids, linked discussion group
{"usernames": ["durov","tginfo","tginfochat"],"deepProfile": true}
A daily monitor: new posts and changed profiles only
{"usernames": ["durov","tginfo"],"maxPostsPerChannel": 50,"onlyNewPosts": true,"onlyChanged": 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. |
change_type | With the memory on: new, changed or unchanged against the last time this profile was delivered. |
changed_fields | Which watched fields changed (count, title, description, latest_post_id, …). |
previous_read_at | When the profile was last delivered. |
previous_count | count at that time. |
count_change | count minus previous_count. |
count_change_per_day | count_change per day since the previous read (null under an hour). |
first_read_at | When the memory first read this profile. |
times_read | How many runs read it, this one included. |
count_history | The memory's last 30 reads: time, count, count type, online. |
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:
{"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,"change_type": null,"changed_fields": null,"previous_read_at": null,"previous_count": null,"count_change": null,"count_change_per_day": null,"first_read_at": null,"times_read": null,"count_history": 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) | $3.00 | $2.55 | $2.10 | $1.50 |
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 $3.00 per 1,000 entities found on the Free plan and $1.50 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. With onlyChanged, a profile that did not change is not delivered and not charged. With onlyNewPosts, each channel's profile row is still delivered and charged on every run; only the post rows are limited to new posts. 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 freenot_found/invalidrow. Duplicates in any form are read once (inputs_merged). Ready-made: Telegram subscriber and member counts for a list of usernames. - A channel's posts —
maxPostsPerChannelwithpostsNewerThan/postsOlderThanreads a channel's posts newest first; an old window is reached by a jump on post ids, not by reading every newer page.postsSearchuses Telegram's own channel search;maxCommentsPerPostadds comments. Ready-made: Scrape a Telegram channel: its profile and 20 newest posts, Telegram channel posts from the last 7 days, with comments, Search Telegram channel posts for a keyword. - A schedule — Save a task with your list and
onlyNewPostsand run it daily: each run delivers only the posts published since the last one, and each channel's profile row with today's count (a profile row is charged on every run). AddonlyChangedto skip, free, the profiles that did not change since they were last delivered (a new post is a change);stateStoreNamekeeps the memory under a name you choose and addscount_change,count_change_per_dayandcount_history. Ready-made: Monitor Telegram channels daily: new posts and count changes. - For a language model —
includeMarkdownadds 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, comments or only new posts) 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. |
onlyNewPosts | boolean | false | Deliver only the posts published after the newest post an earlier run of the same task delivered: run a channel list daily and get only its new posts. Each channel's profile row (today's count) is still delivered and charged on every run; add Only changed profiles to skip unchanged ones. Turns the memory on. |
onlyChanged | boolean | false | Deliver (and charge) a profile only when its title, description, type, verified mark, warning label, exact count, newest post or username changed since the last time it was delivered. Every delivered profile carries change_type, the previous count and the change. Turns the memory on. |
stateStoreName | string | "" | Name of the memory between runs. Given, it turns the memory on: every profile row gets the change of its count since the last delivered read, per day, and a history of counts. Empty = derived from your usernames when Only new posts or Only changed profiles is on. |
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. |
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
ERRORholds 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. With onlyNewPosts, each channel's profile row is still charged on every run; with onlyChanged, an unchanged profile is not delivered and not charged. 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
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
from apify_client import ApifyClientclient = 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
$apify call hyperbach/telegram-profile-scraper --input '{"usernames": ["durov", "BotFather", "https://t.me/tginfochat"]}'
REST
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"]}'
Support
This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.