# Telegram Channel Scraper & Keyword Monitor (No Login) (`ryumoon-data/telegram-channel-keyword-scraper`) Actor

Scrape public Telegram channels without login or API keys. Filter by keywords and date range; get text, views, reactions, links and media as JSON, CSV or Excel.

- **URL**: https://apify.com/ryumoon-data/telegram-channel-keyword-scraper.md
- **Developed by:** [Ryu Moon](https://apify.com/ryumoon-data) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 message scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Telegram Scraper: Channel Messages & Keyword Monitor (No Login, No API Key)

**Scrape messages from any public Telegram channel: text, views, reactions, links, photos, videos and forward info, with keyword search and date filters.** No Telegram account, phone number, bot token or API key needed. Export to JSON, CSV, Excel, or push straight into Google Sheets, Slack, Zapier or Make.

### Quick start recipes

Copy one of these into the JSON input tab and click Start.

**Crypto keyword alerts (schedule hourly, then add a Slack or email integration):**

```json
{"channels": ["cointelegraph", "whale_alert_io", "binance_announcements"], "keywords": ["listing", "airdrop", "ETF"], "dateFrom": "7 days", "onlyNewMessages": true}
```

After the first run, each hourly run only returns new matching posts, often for about a cent.

**Brand or competitor mentions:**

```json
{"channels": ["cointelegraph", "binance_announcements"], "keywords": ["YourBrand", "CompetitorName"], "dateFrom": "7 days"}
```

**Export a channel's history to Excel:**

```json
{"channels": ["durov"], "maxMessagesPerChannel": 0, "dateFrom": "2024-01-01"}
```

Then open the Output tab and choose Export > Excel.

**Remote job posts:**

```json
{"channels": ["remotejobss"], "keywords": ["python", "react"], "wholeWordMatch": true, "onlyNewMessages": true}
```

**Why not use the Telegram API directly?** There's no app registration, phone number or session file, and no risk of a ban to your own Telegram account. It runs in the cloud on a schedule and exports straight to CSV, Excel or JSON.

### Use with AI agents (MCP, Claude, ChatGPT, Cursor)

**Purpose:** Get posts (text, date, views, reactions, links, media URLs) from public Telegram channels, optionally filtered by keywords and a date range. No Telegram login or API key.

**When to use it**

- The user names one or more public Telegram channels and wants their posts, or posts mentioning a keyword.
- Monitoring: run on a schedule with `onlyNewMessages: true` to get only posts published since the last run.
- Research a date window, e.g. "what did @cointelegraph post about ETFs last week".

**When not to use it**

- To **find** channels about a topic, call [Telegram Channel Finder](https://apify.com/ryumoon-data/telegram-channel-finder) first, then pass its `username` values here as `channels`.
- Private channels, groups that need joining, or member lists: not supported.

**Example input**

```json
{
  "channels": [
    "cointelegraph",
    "durov"
  ],
  "keywords": [
    "bitcoin",
    "ETF"
  ],
  "dateFrom": "7 days",
  "maxMessagesPerChannel": 50
}
```

**Output fields (one dataset item per row):** `channel`, `messageId`, `url`, `date` (ISO), `text`, `views`, `reactionsTotal`, `reactions`, `links`, `media` (photo/video URLs), `linkPreview`, `author`, `edited`, `isForwarded`, `forwardedFrom`, `matchedKeywords`, `channelTitle`, `channelSubscribers`.

**Cost:** $0.005 per run + $0.0007 per message (100 messages ≈ $0.08). Cap spend with `maxMessagesTotal`.

**Connect it to your agent:** add this remote MCP server (Apify's official MCP server, loaded with just this tool):

```json
{"mcpServers": {"telegram-channel-keyword-scraper": {"url": "https://mcp.apify.com?tools=ryumoon-data/telegram-channel-keyword-scraper"}}}
```

Or call it from any agent with the Apify API: `POST https://api.apify.com/v2/acts/ryumoon-data~telegram-channel-keyword-scraper/run-sync-get-dataset-items` with the input above as the JSON body. Agents without an Apify account can pay per run with [agentic payments](https://docs.apify.com/agents/payments) (x402, MPP or Skyfire).

### What can this Telegram scraper do?

- 📥 **Scrape any public channel** (`durov`, `@telegram`, `https://t.me/s/cointelegraph`): text, date, views, reactions, links, link previews, photo/video URLs, forwarded-from, author signature, edited flag.
- 🔎 **Keyword search across channels**: uses Telegram's own in-channel search, so finding "bitcoin ETF" in a 60,000-post channel takes a few requests instead of thousands. Any/all match and whole-word mode.
- 📅 **Date range that jumps**: set *From* / *To* and the actor binary-searches straight to that period instead of paging through every newer post.
- 🔔 **Monitoring mode**: *Only new messages since last run* + an Apify schedule = a cheap Telegram keyword alert feed (crypto calls, brand mentions, job posts, deals, news).
- 📊 **Channel stats**: title, description and subscriber count added to every row.
- 🛡️ **Robust**: automatic retries with back-off, polite rate limiting, dedupe across keywords, clear warnings for private/missing channels.

### Use cases

- **Crypto and trading research**: collect posts from signal, project and news channels and filter for tickers or project names.
- **Brand and PR monitoring**: get every public post mentioning your brand, product or competitors.
- **OSINT and journalism**: archive what public channels posted within a date range.
- **Marketing**: track competitor and ad channels, see which posts get the most views and reactions.
- **Data science**: build text datasets from public channels for NLP and sentiment analysis.
- **Alerts**: schedule it with monitoring mode and send new matches to Slack, email or a webhook.

### How much does it cost?

This Actor uses pay-per-event pricing. You pay only for these events:

| Event | Price | When it is charged |
|---|---|---|
| Run started | **$0.005** | Once per run |
| Message scraped | **$0.0007** | Each Telegram post saved to the dataset ($0.70 per 1,000) |

Examples:

- 100 messages: $0.005 + 100 × $0.0007 = **$0.075**
- 1,000 messages: $0.005 + 1,000 × $0.0007 = **$0.705**
- An hourly monitoring run that finds 5 new posts: **$0.0085** per run

Messages filtered out by keywords or dates are not charged. Set a *maximum cost per run* in Apify Console and the Actor stops cleanly when it is reached. Each page request returns up to about 20 posts, so 1,000 posts take roughly 50 requests; with the default polite rate limit that is a couple of minutes.

### Limits

- **Public channels only.** The channel must have Telegram's web preview (`t.me/s/<channel>`) enabled. Private channels, groups, DMs and member lists are not supported.
- **No login.** It never uses a Telegram account, so it cannot see anything a logged-out visitor can't see.
- **No forward counts or comments.** The public preview doesn't show them.
- **Media links expire.** Photo and video URLs come from Telegram's CDN with temporary tokens; download media soon after the run if you need it.
- **Reaction and view counts** are as Telegram shows them at scrape time (Telegram rounds large numbers, so 1.2K is returned as 1200).

### Input

| Field | What it does | Example |
|---|---|---|
| `channels` (required) | Usernames or t.me links | `["durov", "https://t.me/s/telegram"]` |
| `keywords` | Only posts containing these | `["bitcoin", "ETF"]` |
| `keywordMode` | `any` / `all` | `any` |
| `wholeWordMatch` | `eth` won't match `ethics` | `false` |
| `useTelegramSearch` | Use Telegram's search for keywords (fast) | `true` |
| `dateFrom` / `dateTo` | `YYYY-MM-DD`, ISO, `7 days`, `7d` or `last week` | `2025-03-01` |
| `maxMessagesPerChannel` | 0 = no limit (default 20) | `20` |
| `maxMessagesTotal` | Hard cap across all channels | `0` |
| `onlyNewMessages` | Monitoring mode | `false` |
| `includeChannelInfo` | Add title + subscribers | `true` |
| `skipMessagesWithoutText` | Drop media-only posts | `false` |
| `maxRequestsPerMinute`, `requestDelaySecs`, `maxRequestRetries`, `proxyConfiguration` | Advanced | `30`, `1`, `5`, none |

```json
{ "channels": ["durov", "telegram", "cointelegraph"], "keywords": ["bitcoin", "TON", "ETF"], "dateFrom": "2025-01-01", "maxMessagesPerChannel": 10 }
```

### Output (one row per message)

Real row from `sample-output.json` (long values trimmed):

```json
{
  "channel": "cointelegraph",
  "messageId": 72722,
  "url": "https://t.me/cointelegraph/72722",
  "date": "2026-10-18T10:34:18.000Z",
  "edited": false,
  "author": null,
  "text": "🇸🇻 HUGE: El Salvador's Bitcoin holdings now surpass 7,800 $BTC.\n\nNews | Markets | YouTube…",
  "views": 2660,
  "reactions": [
    {
      "emoji": "❤",
      "count": 28
    },
    {
      "emoji": "🔥",
      "count": 12
    }
  ],
  "reactionsTotal": 42,
  "isForwarded": false,
  "forwardedFrom": null,
  "replyTo": null,
  "links": [
    "https://cointelegraph.com/",
    "https://cointelegraph.com/category/markets",
    "https://www.youtube.com/@cointelegraph"
  ],
  "linkPreview": null,
  "media": [
    {
      "type": "photo",
      "url": "https://cdn5.telesco.pe/file/t_3VrnYDK66Ayqyxws-zNZ_lKnYFq-1…",
      "thumbnailUrl": null
    }
  ],
  "hasMedia": true,
  "isServiceMessage": false,
  "channelTitle": "Cointelegraph",
  "channelSubscribers": 330000,
  "matchedKeywords": [
    "bitcoin"
  ],
  "searchQuery": "bitcoin",
  "scrapedAt": "2026-10-18T12:37:35.248Z"
}
```

See `sample-output.json` for a real run (22 posts from 3 channels).

### FAQ

**Telegram scraper vs Telegram API vs Telethon?** Telethon and the Telegram API need an app ID, a phone number and a logged-in session, and heavy scraping can get that account limited. This actor reads only the public web preview, so there's nothing to log in to and nothing to get banned. The trade-off is that only public channels with web preview enabled are supported.

**How do I scrape a channel?** Enter one or more usernames or links in *Channels*, optionally add keywords or dates, and click *Start*. Results appear in the *Output* tab, ready to export.

**Do I need a Telegram account or API key?** No. The actor reads the public web preview at `t.me/s/<channel>`, the same page anyone can open in a browser.

**Can it scrape private channels or groups?** No, by design. Only public channels with web preview enabled. Private chats, groups and members are never touched.

**Does it give forward counts?** Telegram's public preview shows *whether* a post was forwarded and from whom (when public), but not how many times a post was forwarded, so `forwardedFrom` is returned and forward counts are not.

**Why do video URLs stop working later?** Telegram's CDN links carry a temporary token. Download media soon after the run if you need it.

**Why did I get fewer messages than I asked for?** The channel may have fewer posts in your date range or matching your keywords, or it may have web preview disabled. Check the `SUMMARY` record in the run's key-value store for per-channel counts and any problems.

**Is it legal?** It collects publicly available posts only. You are responsible for how you use the data (GDPR applies to personal data such as author names). Use reasonable rates; defaults are deliberately polite.

### Integrations

Schedule it hourly with `onlyNewMessages: true`, then use Apify integrations to send new matches to Slack, email, Google Sheets, a webhook, Zapier or Make.

### Changelog

- 0.2 – runs no longer fail on bad input: invalid channel names, unreadable dates and string inputs are handled with a clear status message. Fuzzy Telegram searches stop after 5 pages with no match, and the run finishes cleanly before its timeout. Default is now 20 messages per channel, and 256 MB of memory is enough.
- 0.1 – first release: keyword search, date seek, monitoring mode, channel stats.

# Actor input Schema

## `channels` (type: `array`):

Public Telegram channel usernames or links to scrape, e.g. \["durov", "@telegram", "https://t.me/s/bbcbreaking"]. Only channels with web preview enabled can be scraped. Don't know the channels? Run Telegram Channel Finder (ryumoon-data/telegram-channel-finder) first. If left empty, the example channels durov and telegram are used.

## `keywords` (type: `array`):

Only return messages containing these words or phrases (case-insensitive). Leave empty to return every message.

## `keywordMode` (type: `string`):

Return messages matching ANY keyword or ALL keywords.

## `wholeWordMatch` (type: `boolean`):

If on, 'eth' will not match 'ethics'.

## `useTelegramSearch` (type: `boolean`):

Much faster for keyword searches across long histories: asks Telegram for matching posts instead of reading every post. Results are still filtered locally for accuracy. Turn off to scan the full timeline.

## `dateFrom` (type: `string`):

Oldest post to include. YYYY-MM-DD, an ISO timestamp, or relative like '7 days', '24 hours', '7d' or 'last week'.

## `dateTo` (type: `string`):

Newest post to include (inclusive). Same formats as From date. The scraper jumps straight to this date instead of paging through newer posts.

## `maxMessagesPerChannel` (type: `integer`):

Maximum posts to return per channel (newest first). 0 = no limit (bounded by the date range). Keep this low to control cost.

## `maxMessagesTotal` (type: `integer`):

Hard cap across all channels. 0 = no limit.

## `onlyNewMessages` (type: `boolean`):

Remembers the newest post seen per channel and only returns newer posts next time. Ideal with a schedule + webhook/Slack/email integration.

## `includeChannelInfo` (type: `boolean`):

Add the channel title and subscriber count to every row.

## `skipMessagesWithoutText` (type: `boolean`):

Drop posts that have no text (media-only posts).

## `maxRequestsPerMinute` (type: `integer`):

Polite rate limit towards t.me. Each request returns up to ~20 posts.

## `requestDelaySecs` (type: `integer`):

Pause between page requests to stay polite towards t.me.

## `maxRequestRetries` (type: `integer`):

How many times to retry a page that fails before giving up.

## `proxyConfiguration` (type: `object`):

Usually not needed. Use Apify Proxy if you scrape many channels on a tight schedule.

## Actor input object example

```json
{
  "channels": [
    "durov"
  ],
  "keywords": [
    "telegram"
  ],
  "keywordMode": "any",
  "wholeWordMatch": false,
  "useTelegramSearch": true,
  "dateFrom": "30 days",
  "maxMessagesPerChannel": 10,
  "maxMessagesTotal": 0,
  "onlyNewMessages": false,
  "includeChannelInfo": true,
  "skipMessagesWithoutText": false,
  "maxRequestsPerMinute": 30,
  "requestDelaySecs": 1,
  "maxRequestRetries": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped messages stored in the run's default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "channels": [
        "durov"
    ],
    "keywords": [
        "telegram"
    ],
    "dateFrom": "30 days",
    "maxMessagesPerChannel": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("ryumoon-data/telegram-channel-keyword-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "channels": ["durov"],
    "keywords": ["telegram"],
    "dateFrom": "30 days",
    "maxMessagesPerChannel": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("ryumoon-data/telegram-channel-keyword-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "channels": [
    "durov"
  ],
  "keywords": [
    "telegram"
  ],
  "dateFrom": "30 days",
  "maxMessagesPerChannel": 10
}' |
apify call ryumoon-data/telegram-channel-keyword-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ryumoon-data/telegram-channel-keyword-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/13M3CQcJxS3Fo9cKR/builds/maEddPITxcVMcZ5ke/openapi.json
