Telegram Channel Search Scraper — Fresh Posts by Keyword avatar

Telegram Channel Search Scraper — Fresh Posts by Keyword

Pricing

$2.00 / 1,000 per matching post returneds

Go to Apify Store
Telegram Channel Search Scraper — Fresh Posts by Keyword

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

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

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 searcht.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 with before=<postId>. The keyword hits come back wrapped in Telegram's own <mark class="highlight"> markup, and the Actor lifts them into matchedTerms so 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, channelPhoto and the channel's photo/video/link counters — is denormalised onto every post row. Sort your CSV by channelSubscribers and you are done; no second lookup, no second Actor, no second bill.
  • A date window that makes the run cheaper, not just smaller. afterDate accepts YYYY-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 expandViaForwards on (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 carry discoveredVia: "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

  1. 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 maxChannelsPerKeyword and why maxChannelsPerKeyword counts channels it could actually search, not names it tried. Unreachable handles are logged (@name: no public preview … skipped, not billed) and never billed.
  2. Set minSubscribers on any keyword worth money. The same index is stuffed with 3-subscriber spam channels that will happily match a broad keyword. minSubscribers: 1000 removes that long tail before billing. On the shipped default it is 0, which is deliberate — a first click should show you the whole shape of the lane, spam included.
  3. 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.
  4. reactions / reactionsTotal are best-effort. Telegram's public preview renders reaction pills on some posts and not others. When none are rendered, reactions is [] and reactionsTotal is null — never a fake 0. views is the reliable engagement number on a channel post.
  5. author is 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.
  6. Keyword search happens per channel, so breadth is your choice. maxPostsPerChannel: 5 with maxChannelsPerKeyword: 50 spreads a keyword thinly across many channels;
    maxPostsPerChannel: 200
    with maxChannelsPerKeyword: 5 goes 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.

GroupFields
Post identitypostId, postUrl, channelUsername
Contenttext, textHtml, matchedTerms, author
TimepublishedAt, postAgeDays, isEdited, scrapedAt
Engagementviews, reactions[] (emoji, count), reactionsTotal
MediamediaType, hasMedia, photos[], videoCount, documents[]
Outboundlinks[], linkPreviewUrl, linkPreviewTitle, mentions[], hashtags[]
ThreadingforwardedFromUsername, forwardedFromTitle, forwardedFromUrl, replyToUrl
PollisPoll, pollQuestion
Channel contextchannelTitle, channelUrl, channelSubscribers, channelDescription, channelVerified, channelPhoto, channelPhotosCount, channelVideosCount, channelLinksCount
Provenancekeyword, 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&amp;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&amp;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:

  • matchedTerms is 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.
  • channelUsername is the casing Telegram stamps on the post; channelUrl uses the casing from the channel header. In the row above that is Bitco_info and t.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: null means "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.
  • postAgeDays is measured at scrape time, to one decimal place, from publishedAt. It is a convenience for sorting; publishedAt is the fact.
  • text is null on a caption-less media post. Check mediaType / hasMedia before filtering on text length.
  • views counts views of the post, which on a big channel is routinely larger than channelSubscribers (forwards and public links count too). That is Telegram's number, unmodified.

Input

FieldTypeDefaultWhat it does
🔎 What to search
keywordsstring 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.
channelsstring 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.
afterDatestring""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.
beforeDatestring""Drop posts newer than this. A bare YYYY-MM-DD includes that whole day.
💰 Limits & cost
maxResultsPerKeywordinteger50Hard row cap per keyword. 50 = $0.10, 500 = $1.00.
maxChannelsPerKeywordinteger10Channels searched per keyword (max 500). 10 is a sample; 50–100 is a real sweep. Channels with no preview cost nothing.
maxPostsPerChannelinteger5Newest-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.
channelMaxMessagesinteger20Feed mode only: newest N posts per listed channel (max 5000). 20 = one preview page.
maxItemsinteger0Overall cost cap across everything. 0 leaves the per-keyword cap in charge.
🎚️ Filters
minSubscribersinteger0Skip posts from channels below this, read live from the channel header. Applied before billing.
expandViaForwardsbooleantrueAlso queue and search channels found as a "Forwarded from" source. Their rows carry discoveredVia: "forwarded-from".
matchInChannelMetabooleanfalseRequire the keyword in the channel's own title or description too — a stricter "channels about X" pull.
⚙️ Advanced
maxConcurrencyinteger5Parallel channel fetches. 5 measured clean through Apify proxies; 10 is the ceiling.
proxyConfigurationproxyApify proxy on, no group pinnedApify 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.

RunPostsCost
The shipped default50$0.10
One keyword, 50 channels × 5 posts250$0.50
A weekly 2-keyword monitor500$1.00
A deep sweep1,000$2.00
  • You are charged per delivered row, once. Rows are de-duplicated run-wide on channelUsername/postId before 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 under minSubscribers, rows removed by matchInChannelMeta, 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.
  • maxItems is 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 — pass channels directly 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 maxChannelsPerKeyword and by promoting forward-sourced channels, but a broad keyword still needs minSubscribers to 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.
  • reactions are inconsistent — see gotcha 4. Use views for 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 afterDate forward, or de-duplicate on postUrl on your side.
  • A date that cannot be parsed is ignored, not an error. afterDate/beforeDate accept YYYY-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.

StepSurfaceWhat is taken
1. Discoverylyzem.com/search?q=<kw>&f=channelsChannel 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. Wideningthe forwardedFromUsername of every matching postLive channels the index has not catalogued, queued to the front (expandViaForwards).
2. Freshnesst.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 identitythe same t.me/s/<username> response, when it falls back to the plain channel cardSubscriber 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 afterDate or 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" and minSubscribers on 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 journalismforwardedFromUsername plus expandViaForwards maps 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 links and mentions already extracted for a network view.
  • AI / RAG pipelines — clean JSON with stable ids (postUrl), ISO timestamps, both plain text and textHtml, 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.


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.