Telegram Channel Search Scraper — Fresh Posts by Keyword
Pricing
$2.00 / 1,000 per matching post returneds
Telegram Channel Search Scraper — Fresh Posts by Keyword
Search Telegram public channels by keyword and get the NEWEST matching posts from Telegram's own preview: channel title, @username, subscribers, description, post text, exact date, views, media, links and forward source. No login, no browser, 256 MB. Date filters for monitoring.
Pricing
$2.00 / 1,000 per matching post returneds
Rating
0.0
(0)
Developer
Scrapers Delight
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 days ago
Last modified
Categories
Share
Search public Telegram channels by keyword and get one clean row per matching post, not a channel directory. Every row carries the post and the channel it lives in: channelUsername, channelTitle, channelSubscribers, channelDescription, channelVerified, postId, postUrl, text, textHtml, matchedTerms, publishedAt, postAgeDays, views, reactions, reactionsTotal, author, mediaType, photos, videoCount, documents, links, mentions, hashtags, linkPreviewUrl, forwardedFromUsername, replyToUrl, isPoll, pollQuestion, isEdited — plus the keyword it matched and how the channel was discovered. No login, no phone number, no bot token, no Chromium: plain HTTP in 256 MB.
The freshness claim, and why it holds. Third-party Telegram search sites index posts once and
then rot — spot-checked on 2026-08-22 against the index this Actor uses for channel discovery,
most dated post hits on that index were years old. So this Actor takes only channel
identities from that index and then asks Telegram's own in-channel search —
t.me/s/<channel>?q=<keyword> — for the posts. Telegram answers newest-first with the exact UTC
timestamp, view count, media and forward source. That is the whole trick: the index only has to
know a channel exists; Telegram supplies the posts, so "what was posted about this keyword this
week" is answerable without an account.
{"keywords": ["bitcoin"],"maxResultsPerKeyword": 50,"maxChannelsPerKeyword": 10,"maxPostsPerChannel": 5}
Click Try for free and hit Start — that is the input the Actor ships with. It caps a first click at 50 posts = $0.10.
The wedge: fresh posts, channel context on every row, and a date window that stops the crawl
- Telegram's own search, not a stale third-party post index. Every post in your dataset comes
from
t.me/s/<channel>?q=<keyword>, which is Telegram's server-side search inside that channel's public preview. It returns the newest 20 matches per page and paginates backwards withbefore=<postId>. The keyword hits come back wrapped in Telegram's own<mark class="highlight">markup, and the Actor lifts them intomatchedTermsso you can see exactly what Telegram matched. - A row is useful on its own. Most Telegram scrapers give you either a channel list or a
message dump you then have to join back to a channel. Here the channel's preview header —
channelTitle,channelUsername,channelSubscribers,channelDescription,channelVerified,channelPhotoand the channel's photo/video/link counters — is denormalised onto every post row. Sort your CSV bychannelSubscribersand you are done; no second lookup, no second Actor, no second bill. - A date window that makes the run cheaper, not just smaller.
afterDateacceptsYYYY-MM-DD, a full ISO timestamp, or a relative window like"7 days","24 hours","2 weeks". Because Telegram serves matches newest-first, the Actor stops paginating a channel the moment its matches fall out of your window instead of walking the archive and throwing rows away. Out-of-window posts are dropped before billing. This is the setting that turns the Actor into a monitor. - Discovery that follows live channels, not just catalogued ones. With
expandViaForwardson (the default), any channel that appears as a "Forwarded from" source inside a matching post is queued and searched too — and it goes to the front of the queue, because a channel that was just forwarded is provably alive, unlike an index entry. Those rows carrydiscoveredVia: "forwarded-from"so you can tell them apart from"search-index"and"input". - Filters that run before the meter.
minSubscribers(read live from the channel header),matchInChannelMeta(the keyword must also appear in the channel's own title or description) and the date window all remove rows before anything is charged. Channels with no public preview cost nothing at all.
Read this before you buy rows
- The public discovery index is full of dead, tiny and private handles. Spot-checked
2026-08-22: most of the first 10 "bitcoin" hits had no public preview — private channels,
groups, or handles that never opened a preview. That is why the Actor pulls a candidate pool
several times larger than
maxChannelsPerKeywordand whymaxChannelsPerKeywordcounts channels it could actually search, not names it tried. Unreachable handles are logged (@name: no public preview … skipped, not billed) and never billed. - Set
minSubscriberson any keyword worth money. The same index is stuffed with 3-subscriber spam channels that will happily match a broad keyword.minSubscribers: 1000removes that long tail before billing. On the shipped default it is0, which is deliberate — a first click should show you the whole shape of the lane, spam included. - Private channels and groups have no public preview, and nothing can change that. There is no input that makes them readable; reading them requires a Telegram account that has joined them. They are skipped, reported in the log with their subscriber/member count where the public card publishes it, and never charged.
reactions/reactionsTotalare best-effort. Telegram's public preview renders reaction pills on some posts and not others. When none are rendered,reactionsis[]andreactionsTotalisnull— never a fake0.viewsis the reliable engagement number on a channel post.authoris a post signature, not a person. It is filled only when the channel has Telegram's "sign messages" setting on. It is not the channel owner's account, and this Actor collects nothing about the human behind a channel.- Keyword search happens per channel, so breadth is your choice.
maxPostsPerChannel: 5withmaxChannelsPerKeyword: 50spreads a keyword thinly across many channels;withmaxPostsPerChannel: 200maxChannelsPerKeyword: 5goes deep into a few. Same money, very different dataset.
What you get
One row per unique public Telegram post, de-duplicated run-wide on channelUsername/postId
before anything is billed. publishedAt is Telegram's own timestamp with a UTC offset;
scrapedAt is ISO-8601 UTC.
| Group | Fields |
|---|---|
| Post identity | postId, postUrl, channelUsername |
| Content | text, textHtml, matchedTerms, author |
| Time | publishedAt, postAgeDays, isEdited, scrapedAt |
| Engagement | views, reactions[] (emoji, count), reactionsTotal |
| Media | mediaType, hasMedia, photos[], videoCount, documents[] |
| Outbound | links[], linkPreviewUrl, linkPreviewTitle, mentions[], hashtags[] |
| Threading | forwardedFromUsername, forwardedFromTitle, forwardedFromUrl, replyToUrl |
| Poll | isPoll, pollQuestion |
| Channel context | channelTitle, channelUrl, channelSubscribers, channelDescription, channelVerified, channelPhoto, channelPhotosCount, channelVideosCount, channelLinksCount |
| Provenance | keyword, discoveredVia |
mediaType is one of text, photo, video, document, voice, sticker, poll.
discoveredVia is one of search-index (the keyword discovery index), forwarded-from (found
inside a matching post) or input (a channel you listed yourself).
The dataset ships with a saved table view: Keyword, Channel, Channel title, Subscribers, Published (UTC), Post, Views, Reactions, Media, Forwarded from, Found via, Post URL. The full field set above is in the JSON/CSV export; the view is just the 12 columns worth eyeballing in the Console.
Which fields are always there, and which are not
Rather than quote a fill percentage from one keyword and let you plan against it, here is the
mechanism — and every run prints its own measured field fill in the log, on that run's rows
(Field fill on N rows: …), so you can check the exact numbers for your keyword. Note: the fill
line counts a field as filled only when it is non-null, non-empty and not false, so boolean
fields (channelVerified, isPoll, isEdited, hasMedia) read below 100% even though the key is
always present.
Always present on a delivered row (the parser sets them on every row it emits): keyword
(except in feed mode, where it is null by design), channelUsername, channelUrl, postId,
postUrl, mediaType, hasMedia, videoCount, isPoll, isEdited, channelVerified,
discoveredVia, scrapedAt.
Present whenever Telegram publishes them — near-universal on real channels, null where the
channel or post genuinely lacks them: channelSubscribers, channelDescription, channelPhoto,
views, text and textHtml (a caption-less media post has no text at all), publishedAt /
postAgeDays (null on the rare post whose preview markup carries no timestamp — such rows are
still delivered and pass the date filter), channelTitle (null when the channel header title
does not parse and the channel came from your own channels list).
Genuinely conditional — expect null or [] on most rows, and that is correct, not a gap:
matchedTerms (empty in feed mode, where there is no keyword to highlight), reactions /
reactionsTotal (see gotcha 4), author (signed channels only), photos / documents (media
posts only), linkPreviewUrl / linkPreviewTitle (posts with a link card), links, mentions,
hashtags (only what the post actually contains), forwardedFromUsername / forwardedFromTitle /
forwardedFromUrl (forwards only), replyToUrl (replies only), pollQuestion (polls only),
channelPhotosCount / channelVideosCount / channelLinksCount (Telegram shows these counters on
some channels and not others).
How to run it
1. Keyword sweep across the public channel space (the usual choice)
{"keywords": ["bitcoin", "airdrop"],"maxChannelsPerKeyword": 50,"maxPostsPerChannel": 5,"minSubscribers": 1000,"maxResultsPerKeyword": 250}
Each keyword discovers its own channel pool, then every channel with a public preview is searched
with that keyword. minSubscribers: 1000 throws away the spam tail before billing. Total here is
capped at 250 rows per keyword = $0.50 per keyword.
2. Keyword monitor — the last 7 days only
{"keywords": ["rug pull", "presale live"],"afterDate": "7 days","maxChannelsPerKeyword": 100,"maxPostsPerChannel": 20,"minSubscribers": 500,"maxItems": 500}
afterDate accepts the relative form, so the input never needs editing: save it as a Task,
attach a daily or weekly Schedule, and every run pulls the window ending now. Pagination stops
per channel as soon as its matches go older than the window, so a tight window is both faster and
cheaper. maxItems is the hard ceiling on the whole run.
3. Keywords inside channels you already trust
{"keywords": ["hiring", "we are looking for"],"channels": ["durov", "@telegram", "https://t.me/s/bloomberg"],"maxPostsPerChannel": 50,"afterDate": "30 days"}
When channels is set and keywords is set, index discovery is skipped entirely. Note that
expandViaForwards still applies: set it to false if you want the keywords searched only
inside the channels you named, otherwise channels seen as a "Forwarded from" source are queued and
searched too. Handles are accepted as name, @name, t.me/name or t.me/s/name.
4. Feed mode — the newest posts from named channels, no keyword
{"keywords": [],"channels": ["bloomberg", "cointelegraph"],"channelMaxMessages": 100}
Leave keywords empty and the Actor stops searching and simply returns the newest
channelMaxMessages posts from each listed channel (20 = one preview page). keyword and
matchedTerms are empty on these rows; everything else is identical.
5. A window between two dates
{"keywords": ["etf approval"],"afterDate": "2026-07-01","beforeDate": "2026-07-31","maxChannelsPerKeyword": 40,"maxPostsPerChannel": 30}
A bare YYYY-MM-DD in beforeDate includes that whole day. Both bounds are applied to
publishedAt before billing.
Scheduling and integrations
Save any input as a Task (Console → the Actor → Save as Task), then attach an Apify Schedule. The dataset is available over the REST API and through the standard Apify integrations (Zapier, Make, n8n, webhooks, MCP) like any other Actor. From the API:
curl -X POST "https://api.apify.com/v2/acts/scrapersdelight~telegram-channel-search-scraper/runs?token=YOUR_TOKEN" \-H 'Content-Type: application/json' \-d '{"keywords":["bitcoin"],"afterDate":"7 days","minSubscribers":1000,"maxItems":200}'
Sample row
A row from a 2026-08-22 run, with matchedTerms and postAgeDays added from the current build;
text and textHtml truncated for the README.
{"keyword": "bitcoin","channelUsername": "Bitco_info","channelUrl": "https://t.me/bitco_info","channelTitle": "Bitcoin info","channelSubscribers": 7040,"channelVerified": false,"channelDescription": "🌐Best bitcoin information🥇📈 Bitcoin and Cryptocurrency earning Guru 💰💌On business issues and proposal write to the📩@magicmanage ⬇️Click Here more #news📊https://www.instagram.com/bitcoin.info.9","channelPhoto": "https://cdn4.telesco.pe/file/dvuwps1hwZHEcDV4BSFi9rXEHsagfztDu15AtNbGnurCC5_vLQu--51aEPY_s4IeBQuEL4fmA-0hZle3RYdc2nDqrLdlLgcABTNKb5KQdynUszRHgUZMjNbZm7cpliykc6chOhgftlhkZNN22K1s6p01T2FKE_re9msLlR0tA61OE2CqlUrk9PbJ_ErPjzwy0Fe9eAahwZzG5DKKm02dOREV955RbVi8SszyD4GCqfHkzbdKULGrUEyljzjgVcVX9ye5T3_Jb2GjETirUXTeIPwhdi03qq89J35d3X08m7M0D3muwAf0_veVc5aOCREgP_tOHMo63ncVrgMcUe3iWg.jpg","channelPhotosCount": 4270,"channelVideosCount": 112,"channelLinksCount": 11800,"discoveredVia": "search-index","postId": 16595,"postUrl": "https://t.me/Bitco_info/16595","text": "Bitcoin tags $65K as S&P 500 rebounds from 2-week lows on US-Iran rhetoric\nBitcoin reached $65,000 as the S&P 500 rebounded from two-week lows, despite diverging from US stocks amid escalating US-Iran tensions. …","textHtml": "<mark class=\"highlight\"><b>Bitcoin</b></mark><b> tags $65K as S&P 500 rebounds from 2-week lows on US-Iran rhetoric</b><br><br><mark class=\"highlight\">Bitcoin</mark> reached $65,000 as the S&P 500 rebounded … ","matchedTerms": ["Bitcoin"],"publishedAt": "2026-08-18T16:44:32+00:00","postAgeDays": 4,"views": 241,"reactions": [],"reactionsTotal": null,"author": null,"forwardedFromTitle": null,"forwardedFromUsername": null,"forwardedFromUrl": null,"replyToUrl": null,"mediaType": "text","hasMedia": false,"photos": [],"videoCount": 0,"documents": [],"linkPreviewUrl": null,"linkPreviewTitle": null,"links": ["https://instagram.com/bitcoin.info.9/","https://instagram.com/bitcoin.info/"],"mentions": [],"hashtags": [],"isPoll": false,"pollQuestion": null,"isEdited": false,"scrapedAt": "2026-08-22T17:28:17.811Z"}
Fields people misread:
matchedTermsis what Telegram highlighted, in Telegram's casing — here"Bitcoin"for the keyword"bitcoin". It is the honest record of what the match actually was, so a keyword that Telegram stemmed or matched as a phrase shows up as the surface form in the post, not as your input string.channelUsernameis the casing Telegram stamps on the post;channelUrluses the casing from the channel header. In the row above that isBitco_infoandt.me/bitco_info. Telegram handles are case-insensitive — de-duplicate on the lowercased handle if you join across runs. (Inside a run, the Actor already does exactly that.)reactionsTotal: nullmeans "no reaction pills were rendered", not "zero reactions". A real zero would be a rendered pill with a count of zero, which Telegram does not do.postAgeDaysis measured at scrape time, to one decimal place, frompublishedAt. It is a convenience for sorting;publishedAtis the fact.textisnullon a caption-less media post. CheckmediaType/hasMediabefore filtering on text length.viewscounts views of the post, which on a big channel is routinely larger thanchannelSubscribers(forwards and public links count too). That is Telegram's number, unmodified.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
| 🔎 What to search | |||
keywords | string list | ["bitcoin"] (prefill) | One keyword or phrase per line. Each one discovers channels, then is searched inside every channel found. A multi-word entry is matched by Telegram as a phrase. Empty + channels set = feed mode. |
channels | string list | [] | Public handles or t.me links. With keywords: index discovery is skipped and the keywords are searched inside these — plus any channel expandViaForwards adds, unless you turn it off. Without keywords: newest posts from each. Private channels and groups are skipped and never billed. |
afterDate | string | "" | Drop posts older than this. YYYY-MM-DD, an ISO timestamp, or "7 days" / "24 hours" / "2 weeks". Also stops pagination early — the monitor setting. |
beforeDate | string | "" | Drop posts newer than this. A bare YYYY-MM-DD includes that whole day. |
| 💰 Limits & cost | |||
maxResultsPerKeyword | integer | 50 | Hard row cap per keyword. 50 = $0.10, 500 = $1.00. |
maxChannelsPerKeyword | integer | 10 | Channels searched per keyword (max 500). 10 is a sample; 50–100 is a real sweep. Channels with no preview cost nothing. |
maxPostsPerChannel | integer | 5 | Newest-first cap per channel (schema max 2000, but pagination stops at 50 pages ≈ 1,000 posts per channel per run). Telegram serves 20 matches per page; above 20 the Actor pages further back. |
channelMaxMessages | integer | 20 | Feed mode only: newest N posts per listed channel (max 5000). 20 = one preview page. |
maxItems | integer | 0 | Overall cost cap across everything. 0 leaves the per-keyword cap in charge. |
| 🎚️ Filters | |||
minSubscribers | integer | 0 | Skip posts from channels below this, read live from the channel header. Applied before billing. |
expandViaForwards | boolean | true | Also queue and search channels found as a "Forwarded from" source. Their rows carry discoveredVia: "forwarded-from". |
matchInChannelMeta | boolean | false | Require the keyword in the channel's own title or description too — a stricter "channels about X" pull. |
| ⚙️ Advanced | |||
maxConcurrency | integer | 5 | Parallel channel fetches. 5 measured clean through Apify proxies; 10 is the ceiling. |
proxyConfiguration | proxy | Apify proxy on, no group pinned | Apify Proxy is on by default with no group pinned (Apify's automatic pool). Both t.me and the discovery index answered over Apify Proxy in our 2026-08-22 test run; switch to RESIDENTIAL if HTTP errors show in the log. |
Run defaults: 256 MB memory, 3600 s timeout.
Pricing
$0.002 per post returned — $2 per 1,000 — charged on the post-scraped event. There is no
monthly fee from this Actor and no second event: one row, one charge.
| Run | Posts | Cost |
|---|---|---|
| The shipped default | 50 | $0.10 |
| One keyword, 50 channels × 5 posts | 250 | $0.50 |
| A weekly 2-keyword monitor | 500 | $1.00 |
| A deep sweep | 1,000 | $2.00 |
- You are charged per delivered row, once. Rows are de-duplicated run-wide on
channelUsername/postIdbefore the charge, so the same post matching two of your keywords is delivered and billed once. - Everything the Actor discards is free. Channels with no public preview, private channels and
groups, posts outside
afterDate/beforeDate, channels underminSubscribers, rows removed bymatchInChannelMeta, and service messages are all dropped before the meter — they never appear in your bill. - Rows are pushed and charged in the same call, so a run that hits your maximum charge stops on a whole row and ends SUCCEEDED with a status message telling you it stopped there — never a half-billed dataset, and never a platform abort mid-channel.
maxItemsis your hard cost cap. Set it and multiply by $0.002 to know the ceiling before you click Start.
Honest limits
- Private channels, groups and channels with no public preview cannot be read. This is Telegram's boundary, not a missing feature: those posts only exist behind a logged-in account that has joined. The Actor detects them from the public card, logs "no public preview … posts cannot be read without a login; skipped, not billed", and moves on. There is no input that changes this and no plan to add one.
- Keyword discovery depends on a third-party index. Channel identities come from a public
Telegram search index (
lyzem.com). If that index is down or answers with an error, the run logs "Discovery index … -> HTTP …" and that keyword yields nothing — passchannelsdirectly and the Actor never touches the index at all. Modes 3 and 4 above are fully independent of it; modes 1, 2 and 5 use the index for channel discovery. Telegram itself has no public global post-search endpoint to use instead; that is why the split exists. - Discovery quality is the index's, not ours. Spot-checked 2026-08-22, most of the first 10
"bitcoin" hits had no public preview, and the surviving channels included 3-subscriber spam.
The Actor compensates by pulling a candidate pool several times larger than
maxChannelsPerKeywordand by promoting forward-sourced channels, but a broad keyword still needsminSubscribersto be worth reading. - You get the channels the index and the forward graph reach, not "all of Telegram". Telegram publishes no channel directory. Two runs of the same keyword weeks apart can surface different channels as the index and the forward graph change.
- Per-channel depth is capped. A single channel is paginated at most 50 preview pages per keyword (Telegram serves 20 matches per page), so about 1,000 matching posts is the most one channel can yield in one run. Split a deep archive pull across date windows.
- No comments, no reactions-by-user, no member lists, no channel analytics. Telegram's public preview does not publish them, and neither does this Actor.
reactionsare inconsistent — see gotcha 4. Useviewsfor engagement.- No search operators. Telegram's in-channel search takes a keyword or a phrase. There is no
OR, no-exclude, no field prefix. Run several keywords instead; duplicates across them are de-duplicated before billing. - A time-budget stop is possible on a big sweep. The Actor reserves the last ~25 seconds of the run to push and charge what it has, then stops fetching and ends SUCCEEDED with "Stopped early to stay inside this run's time limit." Raise the run timeout or lower the caps to get the rest.
- Dedupe is per run. Across scheduled runs, move
afterDateforward, or de-duplicate onpostUrlon your side. - A date that cannot be parsed is ignored, not an error.
afterDate/beforeDateacceptYYYY-MM-DD, an ISO timestamp, or"7 days". Anything else is logged as Could not parse date "…" — ignored and the run proceeds with no date window, so it can bill far more rows than you expected. Check the log line on the first run of a new schedule. - Proxies. Apify Proxy is on by default with no group pinned (Apify's automatic pool); both t.me and the index answered over it in our 2026-08-22 test run. RESIDENTIAL is one switch away if HTTP errors show in the log. A preview page is attempted up to 3 times, each on a fresh proxy session, before the channel is skipped and logged.
How it works
Two public, logged-out surfaces, both plain HTTP — no Chromium, no login, no cookies, no bot token, no phone-number account. Telegram's own APIs would need one of the last two; the public web preview needs neither.
| Step | Surface | What is taken |
|---|---|---|
| 1. Discovery | lyzem.com/search?q=<kw>&f=channels | Channel identities only (t.me/<username>, title, description). Never its post index — spot-checked 2026-08-22, most dated post hits on it were years old. |
| 1b. Widening | the forwardedFromUsername of every matching post | Live channels the index has not catalogued, queued to the front (expandViaForwards). |
| 2. Freshness | t.me/s/<username>?q=<kw>[&before=<postId>] | Telegram's own in-channel search: newest 20 matches per page, exact UTC datetime, views, media, forward source, reactions; pages older with before=. The same page's header gives title / subscribers / description / verified. |
| 2b. Fallback identity | the same t.me/s/<username> response, when it falls back to the plain channel card | Subscriber count and description are still read from it, so the log can say what was skipped instead of dropping it silently. |
Channels are fetched in parallel (maxConcurrency, default 5, ceiling 10). Every fetch is gated on
the run's wall-clock budget and its timeout clamped to what remains, so the emit path always has
time to deliver.
Reliability contract. The run never ends FAILED for a transient reason — a time budget, an
empty keyword list, an index hiccup, an unhandled throw. All of those end SUCCEEDED with a
status message naming the cause. unhandledRejection and uncaughtException are both trapped and
converted into a clean exit for the same reason: a FAILED run is permanent on the public scorecard
and feeds Apify's auto-maintenance flag, and none of those causes mean your data is wrong.
The duplicate-leak check. Before the run is allowed to finish, the Actor asserts that rows
pushed == unique channel/postId keys seen. If they diverge the run stops immediately and the
status message names the assertion, so you can see a billing discrepancy rather than have it
hidden — note the rows already pushed are already in the dataset.
Every run logs its own numbers: HTTP ok/failed split by surface, channels searched, channels
with rows, channels with no public preview, channels that failed (channels dropped by an anti-bot
challenge page currently surface only as a worker error warning), and the exact count dropped by
each filter (duplicates, date window, minSubscribers, matchInChannelMeta) — plus the measured
field fill across the rows it delivered.
When a run ends without the rows you expected
Every one of these ends SUCCEEDED with a status message, so a scheduled monitor on a quiet keyword is never flagged as broken:
- 0 posts → the message names the cause: t.me preview pages could not be fetched /
the discovered channels have no public preview / nothing matched inside the date window —
widen
afterDateor add channels / no matching posts — try a broader keyword or pass channels directly. Nothing is charged. - Charge cap reached → "Stopped at your maximum charge for this run — N post(s) delivered and charged. Raise 'Maximum total charge' for the rest." Every delivered row was billed; no partial rows.
- Run time budget → "Stopped early to stay inside this run's time limit." Raise the timeout or lower the caps.
- No input at all (
{}from an API call) → the Actor runs the documented sample, keyword"bitcoin", and says so in the log and the status message, rather than failing.
Who buys this
- Crypto, trading and airdrop desks — Telegram is where the announcement lands first. A
keyword monitor with
afterDate: "24 hours"andminSubscriberson a real floor is an alerting feed, not an archive dig. - Brand and threat monitoring — your brand, product or domain mentioned across public channels, with the channel's subscriber count on the same row so you can triage by reach immediately.
- OSINT and journalism —
forwardedFromUsernameplusexpandViaForwardsmaps how a claim propagates between public channels, with exact timestamps and view counts on every hop. - Market and competitor research — what public channels in a vertical are actually saying this
week, with
linksandmentionsalready extracted for a network view. - AI / RAG pipelines — clean JSON with stable ids (
postUrl), ISO timestamps, both plaintextandtextHtml, keyword provenance on every row, de-duplicated, nothing to unpick.
FAQ
Does this need a Telegram account, a phone number, a bot token or an API key?
No. It reads only what a logged-out visitor sees on t.me. No account of yours is used or asked
for, and nothing behind a login is touched.
How fresh are the posts? As fresh as Telegram's own in-channel search, which is the same surface the channel's public preview serves — the newest matches come back first, with an exact UTC timestamp. That is the entire reason the Actor uses a third-party index only to find channels and never to find posts.
Can it read private channels or groups? No — they have no public preview. They are detected, logged, skipped and never billed.
Can I search a keyword only inside channels I care about?
Yes: put them in channels alongside your keywords — the index is never contacted. Set
expandViaForwards to false as well if you want only those channels, since by default a
channel seen as a "Forwarded from" source is queued and searched too.
Can I get a channel's latest posts without a keyword?
Yes: leave keywords empty, fill channels, and set channelMaxMessages.
How do I make a scheduled monitor cheap?
afterDate: "24 hours" (or "7 days"), a real minSubscribers, and a maxItems ceiling.
Pagination stops per channel the moment the window closes, and every filtered row is free.
Will two runs bill me twice for the same post?
Within a run, never — dedupe is on channelUsername/postId and the count is asserted before the
run is allowed to finish. Across runs, move afterDate forward, or de-duplicate on postUrl your
side.
Do I need a residential proxy? Not by default: Apify Proxy is on with no group pinned (Apify's automatic pool), and both surfaces answered over it in our 2026-08-22 test run. RESIDENTIAL is available in the proxy input if you ever see HTTP errors in the log.
Why did I get spam channels?
Because the public discovery index is full of them and minSubscribers defaults to 0. Set it to
500 or 1000 — those rows are then removed before billing.
Will a run ever succeed with zero rows? Yes, deliberately, with a status message naming the cause and no charge — so a quiet keyword on a schedule does not look like a broken Actor.
Legal & fair use
This Actor reads public Telegram channel previews as a logged-out visitor. It does not log in, does not use any Telegram account, bot token, phone number or API key, and collects nothing behind authentication. Private channels and groups are skipped by design.
Neither t.me nor telegram.org serves a robots.txt at all — both return HTTP 404 (checked
2026-08-26); the same is true of the discovery index used for channel names. There is therefore no
crawl directive to honour or breach, and the Actor instead paces itself with a low default
concurrency (5) and per-request retry backoff.
Telegram's Terms of Service govern how Telegram content may be used. Channel posts are user-generated content and can contain personal data, handles and contact details — you are responsible for how you use what you collect, including GDPR/CCPA obligations and the rules on unsolicited contact.
Telegram® is a trademark of its owner. This Actor is not affiliated with, endorsed by, or connected to Telegram Messenger Inc.
Feedback
Missing a field, or want a filter that is not here? Open an issue on the Issues tab — and if the Actor earns it, a review on the Reviews tab helps other buyers find it.