Telegram Scraper - Channel Posts, Keywords & IOCs, No Login avatar

Telegram Scraper - Channel Posts, Keywords & IOCs, No Login

Pricing

Pay per event

Go to Apify Store
Telegram Scraper - Channel Posts, Keywords & IOCs, No Login

Telegram Scraper - Channel Posts, Keywords & IOCs, No Login

Telegram scraper for public channels: posts, views, reactions and media, plus keyword hits, extracted indicators (CVE, IP, hash, wallet, domain, handle) and related channels. No login, no phone number.

Pricing

Pay per event

Rating

0.0

(0)

Developer

daehwan kim

daehwan kim

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Share

Give it public Telegram channel names and get back their posts — plus the indicators inside those posts (CVE, IP, hash, wallet, domain, handle), keyword hit counts and the related channels they forward from, link to and mention. No login, no phone number, no cookies: it reads only the public channel preview.

Free plan (preview)Paid Apify plan (Starter and up)
Channels per run2500
Posts per channelthe 10 newest (fewer when maxPostsPerChannel is below 10)up to 50,000 (as far as the public preview reaches)
Post textfirst 280 characters (textTruncated: true)full text
Links, hashtags and mentions listsfrom the shown text only (linkCount shows the real total)complete
History (lastNDays, sinceDate, before)lockedyes
New posts only (incremental)lockedyes
Fetch posts by URL (postUrls)lockedyes
Indicator rows (CVE, IP, hash, wallet, domain, handle…)1 of each kind + how many more were foundall
Related channel rows3 + how many more candidates were foundall (with name and subscriber count)
Keywords1all, including onlyMatching
Output formatfullfull, minimal, rag
Channel check result (channel row)both channelsall
Price per eventlist priceBRONZE −20%, SILVER −35%, GOLD and up −48%

How big is the difference? Measured on 2026-10-07: for @thehackernews a free run returns 10 posts and at most 3 indicators, while a paid run covers about 279 posts for the last 30 days (28×, projected from the channel's posting rate) and found 43 indicators in the 200 newest posts (14×). For @rybar: about 586 posts in 30 days (59×) and 36 related channels in 166 posts (12×, against 3). For @cveNotify: 247 indicators in 200 posts, 202 of them CVE IDs (41×, against 6).

A free run never fails because of a limit: it ends normally, tells you in the dataset what was shown and what stayed locked (1 of 36 shown), and ignores paid-only inputs with a notice instead of an error.

Unofficial. This Actor is not affiliated with, endorsed by, or sponsored by Telegram. It reads public channel preview pages only. You are responsible for how you use the input and the results, for complying with Telegram's Terms of Service, and with GDPR, PIPA, CCPA and other applicable laws.

Why use this Telegram scraper?

  • No account at risk. It never asks for your Telegram login, phone number or session. Nothing to get logged out of, nothing to get banned.
  • Indicators as rows, not as a blob. CVE IDs, IPv4 addresses, MD5/SHA-1/SHA-256 hashes, BTC/ETH/TRON wallet addresses, emails, phone numbers, domains, @mentions and t.me channel links are pulled out of the post text, deduplicated per channel and delivered one per row with how often and where each was seen.
  • Keyword answers, including "nothing found". For every channel × keyword you get one row: posts scanned, matches, first and last match, top posts by views.
  • Related channels with one-hop details. Channels a channel forwards from, links to or mentions — each looked up once for its name, subscriber count, verified badge and whether its preview is open.
  • It tells you why a channel is empty. Every channel gets a channel row: is the preview open, how far back did the run reach, was the history cut.
  • Correct dates. The post time is read from the post's date link only — never from a video's duration.

Built for threat-intelligence and OSINT monitoring, brand and crypto watch, and newsroom research. Runs on the Apify platform with API access, scheduling, integrations and monitoring built in.

How to use it

  1. Enter one or more channels (telegram, @telegram or https://t.me/telegram).
  2. Optionally add keywords, a time window (lastNDays / sinceDate) and the indicator kinds you care about.
  3. Click Start and read the rows in the Output tab. Filter by recordType.
  4. For daily monitoring turn on New posts only with a monitor key and put the Actor on a schedule.

Input

FieldTypeDescription
channelsarrayPublic channels: name, @name or t.me link. 1–500 per run
postUrlsarraySingle posts by address, e.g. https://t.me/telegram/100 (paid plans)
maxPostsPerChannelintegerPosts to read per channel, newest first, 1–50,000. When omitted: 40
lastNDays / sinceDate / beforeinteger / date / integerTime window and starting post number (paid plans)
keywordsarrayCase-insensitive partial match against post text
onlyMatchingbooleanDeliver only posts that match a keyword (paid plans). A channel is stopped after 100 pages (about 2,000 posts) in a row without a matching post (stopReason: noRowsInWindow)
minViewsintegerSkip posts with fewer views. Same 100-page stop as onlyMatching
indicatorKindsarraycve, ipv4, hash, wallet, email, phone, domain, mention, channel_link (all when omitted)
includeRelatedChannelsbooleanRelated channel rows with one-hop lookup (on when omitted)
incremental + monitorKeyboolean + stringOnly posts newer than the last run with the same key (paid plans)
outputFormatstringfull, minimal or rag (formats other than full: paid plans)
proxyConfigurationobjectApify Proxy; the default datacenter proxy is enough
{
"channels": ["thehackernews", "cveNotify"],
"maxPostsPerChannel": 500,
"lastNDays": 30,
"keywords": ["ransomware", "zero-day"],
"indicatorKinds": ["cve", "ipv4", "hash", "domain"]
}

maxPostsPerChannel counts the posts the run reads; keywords with onlyMatching, minViews and the time window then decide which of those become rows.

Output

Every row has a recordType: post, channel, indicator, relatedChannel or keywordSummary. Rows that carry a noticeCode instead are messages, not results (free-plan-preview, free-plan-locked, charge-limit-reached, charge-unavailable, ledger-unreadable, ledger-unwritable, delivery-unconfirmed, restart-unconfirmed, lookup-failed, post-not-found, post-already-delivered, upstream-blocked, run-summary, invalid-input, no-input) — they are never charged; skip them when parsing. Channel rows and notice rows carry a short disclaimer (unofficial, you are responsible for your use of the data). A paid run that has neither (only postUrls) ends with one run-summary row that carries it.

{
"recordType": "post",
"platform": "telegram",
"channelUsername": "thehackernews",
"channelTitle": "The Hacker News",
"postId": 10244,
"dataPost": "thehackernews/10244",
"postUrl": "https://t.me/thehackernews/10244",
"postedAt": "2026-10-05T16:23:18.000Z",
"text": "GitGuardian found 28.65 million new hardcoded secrets in public GitHub commits in 2025…",
"textTruncated": false,
"markdown": "GitGuardian found 28.65 million new hardcoded secrets…",
"views": 5560,
"viewsText": "5.56K",
"reactions": [{ "emoji": "🔥", "count": 3 }],
"reactionCount": 3,
"hasMedia": true,
"mediaTypes": ["document"],
"photos": [],
"videoUrl": null,
"documentUrl": "https://t.me/thehackernews/10244",
"documentName": "main.gif",
"documentSize": "24 MB",
"hasVoice": false,
"poll": null,
"linkPreview": null,
"forwardedFrom": null,
"isForwarded": false,
"replyToUrl": null,
"hasReply": false,
"links": ["https://thehackernews.com/2026/10/the-credential-layer-is-expanding.html"],
"hashtags": [],
"mentions": [],
"linkCount": 1,
"matchedKeywords": [],
"scrapedAt": "2026-10-07T03:00:00.000Z",
"sourceUrl": "https://t.me/s/thehackernews"
}
{
"recordType": "indicator",
"channelUsername": "thehackernews",
"indicatorKind": "cve",
"value": "CVE-2026-88772",
"occurrences": 3,
"firstSeenAt": "2026-09-29T10:02:11.000Z",
"lastSeenAt": "2026-10-04T08:40:27.000Z",
"postUrls": ["https://t.me/thehackernews/10231", "https://t.me/thehackernews/10198"]
}
{
"recordType": "channel",
"channelUsername": "thehackernews",
"channelTitle": "The Hacker News",
"subscriberCount": 163000,
"isVerified": true,
"previewStatus": "open",
"postsDelivered": 279,
"postsScanned": 279,
"newestPostId": 10257,
"oldestReachedAt": "2026-09-07T05:11:42.000Z",
"historyTruncated": false,
"stopReason": "dateCutoff"
}

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

Data table

RowKey fields
postpostId, dataPost, postUrl, postedAt, text, textTruncated, markdown, views, viewsText, reactions, reactionCount, hasMedia, mediaTypes, photos, videoUrl, videoDurationText, documentUrl, documentName, documentSize, hasVoice, voiceUrl, poll, linkPreview (url, headline, summary, siteName, imageUrl), forwardedFrom, forwardedFromUrl, isForwarded, replyToUrl, hasReply, links, hashtags, mentions, linkCount, isEdited, authorSignature, matchedKeywords
channelchannelUsername, channelTitle, channelBio, channelUrl, subscriberCount, photoCount, videoCount, fileCount, linkCount, isVerified, avatarUrl, previewStatus (open, no_preview, truncated, not_found), previewNote, postsDelivered, postsScanned, pagesFetched, newestPostId, newestPostAt, oldestReachedPostId, oldestReachedAt, historyTruncated, historyGap, stopReason, collectionError, serviceMessagesSkipped, incrementalMode, incrementalNote, savedLastPostId
indicatorindicatorKind, value, occurrences, firstSeenAt, lastSeenAt, postUrls (up to 5), hashAlgorithm (hashes), walletNetwork (wallets)
relatedChannelrelatedUsername, relatedUrl, relatedTitle, relatedBio, relatedSubscriberCount, relatedIsVerified, relatedPreviewStatus, relationKinds (forward, link, mention), occurrences, firstSeenAt, lastSeenAt, postUrls
keywordSummarykeyword, postsScanned, matchCount, firstMatchAt, lastMatchAt, topPosts (up to 5 by views)

All rows also carry recordType, platform, channelUsername, scrapedAt and sourceUrl. Hashtags and full URLs stay in the post row's arrays; they are not sold as indicator rows. Service messages ("Channel created", "Channel photo updated") are counted in the channel row and not delivered as posts.

Pricing

Pay per event. The compute, proxy and storage a run uses are included in the event prices; Apify platform usage is not billed on top of them.

EventCharged whenPrice (list)Not charged
apify-actor-startthe run starts — once per run (memory is limited to 1 GB)$0.003—
channel-checkedone channel's lookup is completed — including the answer that its preview is turned off or the name does not exist$0.005our errors (timeouts, failed retries, parser errors, an answer that is not a Telegram page); the same channel entered twice
postone post row is written$0.004duplicate posts (also a postUrls entry the same run already delivered with its channel); notice rows
indicatorone indicator row, deduplicated per channel, is written$0.004duplicates; notice rows
related-channelone related channel row whose one-hop lookup was completed is written$0.004lookups that failed on our side
keyword-checkedone channel × keyword result row is written, including zero matches$0.004checks that failed on our side

apify-actor-start: Charged once when a run starts. This Actor's memory is limited to 1 GB, so that is one charge per run (the platform adds one more charge for every 1 GB of memory above 1 GB). It costs $0.003. The start fee is the same on every plan tier.

Paid plan tiers pay less per event: BRONZE −20%, SILVER −35%, GOLD and up −48%.

Example: one channel with 279 post rows + 43 indicator rows + 1 channel check + start (once) = $1.30 at list price. Set a maximum charge per run in Run options to cap spending; when the cap is reached the run stops normally and says so in a charge-limit-reached row.

Restarts and repeat charges. When Apify moves a run to another server, or you abort a run and resurrect it, the run gets a signal, saves its own ledger and continues from it: rows already delivered are neither written nor charged again, and finished channels are not read again. A run that dies without such a signal (for example killed at its timeout) and is then resurrected continues from its ledger as well: a row is charged only after the ledger holding it was saved, so no row is charged a second time. A row that was being handed over at the very moment the run died (charged or not, written or not — the run cannot tell) is neither charged nor written again; a restart-unconfirmed row gives the count. If the ledger cannot be saved (3 tries), the run charges no further row and says so in a ledger-unwritable row. If a resurrected run cannot read its ledger and the dataset already has rows, it stops before requesting or charging anything and says so in a ledger-unreadable row: run again with the same input. If the dataset refuses a row that was already charged, the run reads the end of the dataset back and writes the row once more only when it is not there; if that fails too, the row is missing from the dataset, is not charged again, and the count is reported in the channel row's collectionError (or in a run-summary row). When the dataset cannot be read back, the row is not written a second time (to avoid a duplicate) and a delivery-unconfirmed row gives the count. From such a row on the run charges no further row: channels and post URLs it had not read yet are not requested. If the platform's charge call itself fails, that row is still delivered and the call is not made again; when it fails 3 times in a row (or 10 times in a run) the run stops charging and requesting and says so in a charge-unavailable row with the count of charges that could not be confirmed.

Interrupted monitoring runs. With incremental: true, a run that is reading new posts saves the last post it delivered when it gets a migration or abort signal and with every periodic state save of the platform. The next scheduled run starts after that post, so it does not charge those posts again even when the interrupted run is never resurrected. Two things remain. A run that dies without a signal (for example killed at its timeout) leaves the posts it delivered after its last periodic save to be delivered and charged again by the next run. And the indicator, related-channel and keyword rows of a channel are written after its posts, so for a channel that was interrupted they come only if that run is resurrected (it then reads the channel again from where it started, without charging delivered rows twice); a later run does not produce them for posts an earlier run delivered. A first run (baseline) that is interrupted by a signal saves no position: resurrect it to finish the baseline, otherwise the next run takes the baseline again and charges its posts again.

Request discipline. Requests to one channel are at least 1.1 s apart. Only 429, 5xx and dropped connections are retried (after 1, 2 and 4 s, or after the server's Retry-After when that is longer, up to 30 s). When 3 channels in a row cannot be read, the run stops asking, says so in an upstream-blocked row and ends normally; nothing is charged for what was not read.

What it does not do

  • No member lists. Members are only visible to logged-in accounts.
  • No private channels or groups, no invite links. Only channels with a public preview. A channel whose preview is turned off is reported as previewStatus: "no_preview".
  • No login of any kind. It never asks for a phone number, code, session or cookies.
  • No full history guarantee. Some channels expose only part of their history in the public preview. The channel row says how far the run reached (oldestReachedAt, historyTruncated).
  • No media downloads, translation or summaries. Photo, video and file addresses are delivered as links.
  • No in-channel search. Keywords are matched against the posts the run reads.

Tips

  • Start with maxPostsPerChannel: 40 to see the shape of the data, then widen the window with lastNDays.
  • For monitoring, schedule the Actor with incremental: true and one monitorKey per schedule — each run returns only the posts that appeared since the previous one (for runs that were interrupted, see "Interrupted monitoring runs" under Pricing). Deleted posts never cause a repeat: when the channel's newest post number is below the saved one the run reports "nothing new". A new baseline is taken only when it is more than 200 below the saved number (a different history). A run cut short by an error or by your charge limit saves the last post it delivered, and the next run continues from there. If part of the history between the saved post and the newest post cannot be read, the channel row says so in historyGap and incrementalNote.
  • indicatorKinds: ["cve", "ipv4", "hash", "domain"] keeps a security feed focused; ["wallet", "domain", "channel_link"] suits crypto watch.
  • mention indicator values are plain handles — you can pass them to a username-lookup Actor in the Apify Store (for example Sherlock or Maigret based ones) as a next step.
  • A truncated or no_preview channel is a fact about that channel, not a failed run.

FAQ, disclaimers, and support

Is this legal? The Actor reads pages that Telegram serves publicly to anyone without an account. Whether a particular use of that data is lawful depends on what you collect and what you do with it. You are responsible for how you use the results. Do not use this tool for harassment, stalking, or any unlawful purpose.

Privacy & data responsibility. You are the data controller for every lookup you run with this Actor; ntriqpro is a data processor acting solely on your instructions. You are responsible for having a lawful basis and a legitimate, purpose-limited reason for each lookup, and for complying with GDPR, PIPA, CCPA, and other applicable privacy laws. We do not store your results — output is written only to your own run's dataset. With incremental, the last post number per channel is kept in a key-value store in your own account.

Why did a channel return nothing? Look at its channel row: no_preview means the owner turned the public preview off; not_found means there is no public channel with that name; truncated means the preview skips part of the history.

Trademarks. Telegram is a trademark of its owner. This Actor is an independent tool and uses the name only to describe what it reads.

Found a bug or want a feature? Use the Issues tab.

Used this Actor? A sentence in the Reviews tab about what you used it for helps the next person decide.