ActorStack.dev
Social mediaNewsLead generationv0.1.12updated 16 September 2026

Telegram Channel Scraper — Posts, Reach & Keyword Search

No login. No phone number. No bot token.

Reads the public preview pages Telegram serves to any anonymous visitor, so there is no account to create, no phone number to burn and no session to expire. Full post text, view counts, reactions, links, inline buttons and exact subscriber counts, plus keyword search that runs on Telegram's side rather than on yours.

oswaldocarabano/telegram-channel-scraper

input.json
{
  "mode": "search",
  "channels": ["durov", "https://t.me/s/tgbeta"],
  "searchTerms": ["react", "typescript"],
  "exactMatch": true,
  "maxMessagesPerChannel": 200
}
Version
v0.1.12
Memory
512 MB
Browser
none
Proxy
None — direct, with an optional fallback

Short answer

The Telegram Channel Scraper Actor extracts posts from public Telegram channels without a login, a phone number, a bot token or an API key, by reading the same t.me preview pages Telegram serves to anonymous visitors. It returns post text at a 99.6% fill rate measured over 893 posts across 9 channels, view counts at 99.2%, links at 80.7%, and exact subscriber counts rather than the rounded 11.1M the preview displays. Keyword search runs on Telegram's side: finding 20 posts mentioning a term took 1 request and 24 KB against 25 requests and 712 KB for reading the channel. Pricing is $0.001 per post or search result and $0.002 per channel record, and error rows are never charged.

Key points

  • No login, no phone number, no bot token and no API key: it reads the public preview pages Telegram serves to any anonymous visitor, so there is no session to expire and no account to ban.
  • Keyword search runs on Telegram's side. The same 20 matching posts cost 1 request and 24 KB through search, against 25 requests and 712 KB by reading the channel's history.
  • Exact subscriber counts. The preview shows a rounded `11.1M`; one extra 4 KB request returns `11143438`, and both numbers are delivered so the conversion can be audited.
  • Fill rates measured over 893 posts across 9 channels: text 99.6%, views 99.2%, links 80.7%, t.me handles 43.2%, reactions 11.0%, emails 3.0% overall and 9.0% in job channels.
  • Telegram's search stems rather than matches substrings, so `hiring` returns posts saying `hire a cab`. Those false positives are removed before delivery and are never charged.
  • A channel that cannot be read gets a free diagnostic row saying why — it does not exist, or it is a group, a bot, a user account, or a channel with its web preview switched off.
On this page11 sections

What it does

Reads the public preview pages Telegram serves to any anonymous visitor, so there is no account to create, no phone number to burn and no session to expire. Full post text, view counts, reactions, links, inline buttons and exact subscriber counts, plus keyword search that runs on Telegram's side rather than on yours.

output — one row
{
  "channel_username": "durov",
  "telegram_channel_id": "1006503122",
  "message_id": "421",
  "url": "https://t.me/durov/421",
  "datetime": "2026-08-14T09:31:04+00:00",
  "text": "Telegram now supports…",
  "views": 3140000,
  "views_raw": "3.1M",
  "reactions_total": 48213,
  "links": ["https://telegram.org/blog/"],
  "link_domains": ["telegram.org"],
  "mentions": [],
  "tme_handles": [],
  "hashtags": [],
  "emails": [],
  "has_photo": true,
  "has_video": false,
  "forwarded_from": null,
  "is_edited": false,
  "from_cache": false,
  "data_age_hours": 0
}

Why this one

Anonymous by design, not by limitation

Scrapers that sign in with a phone number can reach more — private channels, attachments, member lists — and they get the account banned, at which point the pipeline stops. This Actor reads what Telegram publishes to the open web. There is nothing to authenticate, so there is nothing that expires halfway through a run, and the same input works the same way in six months.

Search that costs a request, not a crawl

Reading a whole channel's history to find the posts mentioning one term is the expensive way to do it: measured at 25 requests and 712 KB for 20 results in a job channel. Telegram's own search endpoint returns the same 20 in 1 request and 24 KB. You pay per relevant row rather than per page walked past, which is why search mode and messages mode carry the same price per row.

Both numbers, never just the rounded one

Every abbreviated figure Telegram displays is delivered as a pair. `views` is the typed value and `views_raw` is the string as shown; `subscribers` is exact where the preview said `11.1M`. A rounded number passed off as a measurement is the failure mode of every reach report built on this data, and delivering both is what makes the rounding auditable rather than invisible.

Diagnostics instead of an empty row

When a handle does not resolve, most tools return nothing and leave you guessing. This one spends an extra 4 KB request to say which of five things happened: no such channel, a group, a bot, a user account, or a public channel whose web preview is switched off. Those rows are free, because a diagnostic is not a delivery.

The identifier that survives a rename

`telegram_channel_id` is Telegram's internal id and it is on every row. A channel that changes its `@username` breaks any dataset keyed on the handle; keyed on the internal id, the history stays joined.

Use cases

  • Track what a set of public channels published, with view counts and reactions as the reach signal.
  • Find posts mentioning a technology, a company or a product across many channels, without knowing the channel names, using discover mode over the curated catalogue.
  • Build a reach report with exact subscriber counts rather than the rounded figures the preview displays.
  • Monitor job channels for postings mentioning a stack, and read the contact handles the post carries.
  • Measure how a story propagates, using `forwarded_from` and the post datetime.

Input

Every field has a default, and the defaults are deliberately small so a first run is cheap enough to inspect before you commit to a sweep. This table mirrors the Actor's own input schema field for field.

FieldDefaultWhat it does
modestring"messages"What to do`messages` reads a channel's posts, `search` returns only posts matching your keywords with the filtering done on Telegram's side, `discover` searches the curated catalogue by niche so you need no channel names, `channel-info` returns metadata and the exact subscriber count only.
channelsstring[][]ChannelsHandles or t.me URLs. `durov`, `@durov`, `t.me/durov` and `https://t.me/s/durov` all mean the same channel. Not needed in discover mode.
searchTermsstring[][]Search termsRequired in search and discover modes. Telegram filters on its side, which is far cheaper than reading a whole channel.
exactMatchbooleantrueExact match onlyRemoves the stemming false positives Telegram's search returns — a query for `hiring` matching `hire a cab`. Costs nothing, and a discarded candidate is never charged.
nichestring"all"Niche (discover mode)Which part of the curated catalogue to search: jobs, business, crypto, tech, news, marketing, education, health, law, lifestyle, culture, sport, social, other, or all.
maxChannelsinteger40Maximum channels (discover mode)How many catalogue channels to search, largest first. Each one costs a request.
maxMessagesPerChannelinteger200Maximum posts per channelA hard cap, so a large channel cannot run up an unexpected bill. Discover mode defaults to 20 instead, because it opens many channels at once.
newestFirstbooleantrueNewest posts firstTurning it off walks the channel forward from the oldest post, which is what makes a long backfill resumable across runs.
sinceDatestring""Only posts afterISO date. Walking newest-first stops as soon as it passes this date, so you are not charged for posts you did not want.
untilDatestring""Only posts beforeISO date.
includeExactSubscribersbooleantrueExact subscriber countOne extra 4 KB page turns the preview's rounded `11.1M` into `11143438`. Ignored in discover mode.
includeContactFieldsbooleantrueInclude contact entitiespersonal dataEmails, phone numbers, @mentions and t.me handles found in the post text, each in its own column. Turn it off and those columns come back empty; the post text is returned in full either way.
maxCacheAgeDaysnumber1Accept cached pages up to (days)Every row declares `from_cache` and `data_age_hours`, so a cached row never passes as a fresh one. 0 forces a fresh fetch.
maxConcurrencyinteger5Parallel requestsCapped at 5 deliberately. These are public pages served to anonymous visitors and the Actor stays well inside polite crawling limits.
proxyFallbackbooleantrueProxy fallbackRequests go direct, which costs nothing and is what works — no rate limiting has been observed on these pages. This retries through a sticky proxy session if Telegram starts refusing, rather than silently returning fewer rows.

Output and fill rates

A field being in the schema is not the same as it having a value. The percentages below were counted on real runs; the sample sizes are in Measurements. Anything not listed here is not promised.

FieldFilledMeaning
datetimedatetime100%ISO 8601, exact to the second.
telegram_channel_idstring100%Telegram's internal channel id. Survives a channel renaming its `@username`, which the handle does not.
message_idstring100%With `url` and `channel_username`.
textstring99.6%The full post text, never truncated.
viewsinteger99.2%With `views_raw`, the string as Telegram displayed it, so the rounding is auditable.
linksstring[]80.7%With `link_domains`. Read from the link target rather than from the visible text.
has_photoboolean52.4%Whether the post carries a photo. The file itself is not extracted.
tme_handlesstring[]43.2%`https://t.me/...` contacts in the text. More common than @mentions, and given its own column. 64.2% in job channels.
hashtagsstring[]37.4%Hashtags in the post text.
mentionsstring[]30.8%`@handle` mentions. 53.5% in job channels.
has_videoboolean11.6%Whether the post carries a video.
reactionsobject11%With `reactions_total`. Only channels with reactions enabled.
authorstring11%Only on channels that sign their posts.
is_replyboolean11%With `reply_to_url`.
forwarded_fromstring8%The channel a post was forwarded from. The propagation signal.
is_editedboolean5.3%Being re-measured; 5.3% is currently an underestimate and is published as one.
emailsstring[]3%3.0% overall averages news channels at 0% with job channels at 9.0%; one job channel measured alone was 35%. Both numbers are given because the average would mislead.
pollobject0.2%With `has_poll`. Verified with a positive case.
buttonsobject[]not measuredInline buttons such as Apply or Contact. Verified present; the rate is not yet measured.
subscribersinteger100%Exact count on channel rows when subscriber lookup is on, alongside the rounded `subscribers_raw`.
from_cacheboolean100%With `fetched_at` and `data_age_hours`.

Every key is always present. A field that exists but is empty comes back as explicit null, so a parser never has to guess.

Datasets

Different record types go to different datasets, so the main table never carries columns that are blank on most rows.

  • defaultOne row per post in messages and search mode, or one row per channel in channel-info mode.billed
  • channelsChannel metadata with the exact subscriber count, when subscriber lookup is on.billed
  • Diagnostic rowsA channel that could not be read, with the reason it could not.never billed

Pricing

Pay per delivered result. Charges are applied as each row is produced rather than in a lump at the end, so an aborted run bills only for what it actually gave you.

EventPriceNotes
apify-actor-startActor start$0.00001Effectively free. A run that finds nothing costs you nothing.
messagePost$0.001One public post with its full text, view count, reactions, links and entities. Error rows are never charged.
search-resultSearch result$0.001One post matching your keyword. Stemming false positives are removed before delivery and are not charged.
channelChannel record$0.002One channel with its metadata and its exact subscriber count.

Measurements

Each figure is shown with the method that produced it. A benchmark without a method is a marketing claim wearing a number's clothes.

Fill-rate sample

893 posts across 9 channels

No field is published without a measured rate, and the two fields that could not be measured say so rather than defaulting to a promise.

Search versus crawl

1 request and 24 KB against 25 requests and 712 KB

Same 20 matching posts from the same job channel, once through Telegram's search endpoint and once by reading the channel history.

Subscriber precision

`11.1M` displayed, `11143438` returned

One additional 4 KB page per channel. Both the rounded string and the exact integer are delivered.

Attachments found

0 in 893 posts across 29 channels

Searched for documents, voice notes, audio, stickers, locations and round videos. The public preview does not appear to render them at all, so those fields were removed rather than shipped always-false.

Emails by channel type

0% news, 9.0% job channels, 35% in one job channel alone

The 3.0% overall figure is an average across both kinds, which is why the split is published next to it.

Cross-language search

180 results across 13 channels for one technology term

The catalogue is mostly non-English, but technical terms stay in the Latin alphabet inside posts in other scripts. Every one of the 180 was a genuine posting for that technology.

What it will not do

Stated plainly so you can judge fit before spending anything.

  • Only public channels. Never groups, never private channels, never invite links — those return a free diagnostic row instead.
  • Attachments are not extracted at all: no documents, voice notes, audio, stickers, locations or round videos. After 893 posts across 29 channels there was not one positive case, so the public preview almost certainly does not render them. Fields that would always be `false` were removed rather than shipped.
  • Discover mode searches a curated catalogue of verified channels that ships with the Actor. It does not reach channels outside that list, and the run log states how many were searched.
  • The catalogue is global and mostly not in English: Telegram's largest job channels are in Arabic, Persian, Chinese and Russian. Searching for a technology name works across languages; searching for an English phrase like `remote` returns less than you would expect.
  • Media URLs carry a token and expire. They are a reference, not a permanent link.
  • `is_edited` at 5.3% is being re-measured and is currently an underestimate.
  • Concurrency is capped at 5 on purpose, which is well inside polite crawling limits rather than as fast as the pages would allow.

Privacy

  • The Actor returns the full text of public channel posts, which may include email addresses, phone numbers, @mentions and t.me handles the author wrote. Output may contain personal data and whoever runs it is the data controller for what they do with it.
  • Set `includeContactFields` to false and those four columns come back empty. The post text is returned in full either way, because a post with its contacts stripped is still the post.
  • It does not cross-reference authors between channels, does not enrich from outside sources, does not build profiles of people, and does not discover channels by following mentions.
  • Cached pages are held for 7 days and archives for 90, both expiring automatically. Every row declares `from_cache`, `fetched_at` and `data_age_hours`.
  • Data policy and removal requests: telegram.actorstack.dev, or privacy@actorstack.dev

See also the data removal process.

Frequently asked questions

Do I need a Telegram account, a phone number or a bot token?
None of them. The Actor reads the public t.me preview pages Telegram serves to any anonymous visitor. That is the design rather than a limitation: scrapers that sign in with a phone number get the account banned and stop working, and there is no session here to expire.
Can it read private channels or groups?
No. Only public channels with their web preview enabled. Pass a group, a bot, a user account, a private channel or an invite link and you get a free diagnostic row saying which of those it was, rather than an empty result.
Can I search without knowing any channel names?
Yes, with discover mode. It searches a curated catalogue of verified public channels grouped by niche, and the run log states how many channels were searched. It does not reach channels outside that catalogue, which is stated up front because a discovery tool that quietly searches 40 places is easy to mistake for one that searches all of Telegram.
Why are there two numbers for views and subscribers?
Because Telegram displays a rounded string. `views_raw` is `3.1M` as shown and `views` is the typed value; on channel rows, one extra 4 KB request turns a displayed `11.1M` into `11143438`. Delivering both is what lets you audit the conversion instead of inheriting somebody else's rounding.
Are attachments included?
No, and not by choice: after 893 posts across 29 channels there was not a single positive case, so the public preview almost certainly does not render documents, voice notes, audio, stickers, locations or round videos at all. Fields that would always be false were removed rather than shipped. A scraper that signs in with a phone number can download files; this one cannot.
What am I not charged for?
Three things: error and diagnostic rows, candidates discarded by exact match, and rows you already received if a run is interrupted and resumed. Charging happens immediately after each row is delivered rather than in a batch at the end, so a run that dies halfway bills exactly what it delivered.
Does the post text include personal data?
It can. Posts are returned exactly as their authors wrote them, and a post may contain email addresses, phone numbers, @mentions and t.me handles. Setting `includeContactFields` to false empties those four columns, and whoever runs the Actor is the data controller for the output either way.

Guides for this Actor