# Telegram Channel Scraper - Public Posts, Views, Media (`scrapefy/telegram-channel-scraper`) Actor

Posts from public Telegram channels in one schema: text, date, views, reactions, links, media URLs, forwarded-from, channel info. Reads the t.me/s web preview. No login, no API key, no proxy.

- **URL**: https://apify.com/scrapefy/telegram-channel-scraper.md
- **Developed by:** [Scrapify](https://apify.com/scrapefy) (community)
- **Categories:** Social media, News, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 Channel Scraper: public posts, views, reactions and media from t.me channels

Scrape **posts from public Telegram channels** without a login, API key, phone number or proxy. Give channel usernames or t.me links and every post comes back as one clean row: channel, post ID and URL, date (ISO), text, views, author signature, forwarded-from, links, media type and media URLs, reactions and an edited flag. Optionally add one channel-info row per channel with title, description and subscriber count. Export to CSV, JSON or Excel, or call it from the API.

Use it for **news and crypto channel monitoring, brand and keyword tracking, market research, OSINT on public announcements and building datasets for AI agents**. Schedule it with a keyword or date window to get a daily feed. It reads only the **public web preview** Telegram publishes at `t.me/s/<channel>` - the same page anyone can open in a browser - so there is no Telegram API, no MTProto session and no account. Works with AI agents through the Apify MCP server.

### Why this one

- **No login, no keys, no proxy.** Only public channels with a web preview. Nothing to configure.
- **Pagination past the first page.** Older posts are read page by page (about 20 per page) until your limit, date or keyword condition is met.
- **Filters:** max posts per channel, optional overall cap, date from / date to (UTC), keywords (text or links). Posts removed by a filter are never charged.
- **One normalised schema** for text, photo, video, link-preview, forwarded and reply posts.
- **Polite by design:** 1-3 seconds between page requests, retries with backoff on 429 and 5xx, and a channel is stopped (the run continues) if Telegram keeps answering 429.
- **Clear errors per channel.** A channel without a public preview (private, a user or bot, a group, deleted, or preview switched off) is reported in the log and in the run summary with the reason; the other channels still run. The run fails only if every channel fails.
- **Hard spending cap.** Max posts per channel (default 20) is exact; pay per post only.

### Input (main fields)

| Field | Meaning |
|---|---|
| Channels | Usernames, @handles or t.me / t.me/s links, one per line |
| Max posts per channel | Newest first; default 20 |
| Max posts (all channels) | Optional overall cap |
| Posts from date / until date | UTC, `YYYY-MM-DD` or full ISO timestamp; a plain end date includes the whole day |
| Keywords | Keep only posts whose text or links contain one of these (case-insensitive) |
| Include channel info item | One extra row per channel (separate event) |
| Max pages scanned per channel | Safety limit (default 60 pages) so a filter that matches nothing cannot read a whole history |

### Output (one row per post)

`type` ("post"), `channel, channelTitle, postId, postUrl, date, text, views, viewsText, author, isForwarded, forwardedFrom, forwardedFromUrl, replyToUrl, links, mediaType, mediaUrls, linkPreview, reactions, edited, scrapedAt`. Channel-info rows (`type` = "channel") have `channel, channelUrl, channelTitle, verified, description, subscribers, subscribersText, photos, videos, links, photoUrl`. A `RUN_SUMMARY` record in the key-value store lists, per channel, pages read, posts scanned and pushed, why scanning stopped, and any errors.

Notes on the data: `views` and `subscribers` are the rounded figures Telegram shows (`5.84M`), converted to numbers; `viewsText` keeps the original text. Media URLs are the thumbnails or files exactly as the preview shows them (photo URLs, video thumbnail and, when shown, the video file URL); media Telegram does not render on the web preview is flagged `not_shown_in_preview`. Reaction emojis are the ones the preview shows; custom emoji are given as `custom:<id>`. Author signature, forwarded-from and reply link only exist where the channel uses them. See `sample-output.json` for real rows.

### Output example

Real rows from a test run (columns trimmed):

| Channel | Post | Date (UTC) | Text | Views | Media |
|---|---|---|---|---|---|
| telegram | 460 | 2026-08-26 19:12 | For all the info from today's update and a look back at 13 years of Te… | 2060000 | link |
| telegram | 459 | 2026-08-26 19:12 | Signed Gifts. You can now add your signature and a comment to gifts yo… | 1820000 | video |
| wublockchainenglish | 26536 | 2026-10-24 05:24 | Highlight Clip: Arthur Hayes: High-FDV Shitcoins Can't Even Pump on Bi… | 337 | none |
| wublockchainenglish | 26535 | 2026-10-24 04:56 | Bitcoin Price Bets Help Push Weekly Crypto Volume on Kalshi and Polyma… | 471 | none |
| binance_announcements | 9082 | 2026-10-23 09:31 | Binance Alpha Will Remove PTB, AA, OWL, DORA, ELIZAOS, MEMES, 哭哭马  (20… | 22200 | photo |
| binance_announcements | 9081 | 2026-10-23 05:01 | Binance Will Support the ASML Holding N.V. (ASML) and Nokia Corporatio… | 26100 | photo |

### Pricing

Pay per event: **$0.0015 per post** ($1.50 per 1,000 posts), **$0.001 per channel-info row** (only if switched on) and a $0.00005 start event per run. The first 20 posts cost about three cents. See PRICING.md for the benchmark.

### Public data only / personal data

The Actor reads only what Telegram shows publicly on a channel's web preview. It does **not** log in, does **not** read private channels, groups or chats, member lists or phone numbers, and does not bypass anything. Posts can still contain personal data (names, @usernames, photos, forwarded messages). You are responsible for using the data in line with Telegram's terms, GDPR and other applicable data-protection law: have a lawful basis, keep only what you need, and honour deletion requests.

### Notes and limits

- Only channels with a public preview work. `t.me/s/<name>` must show posts; if it redirects to `t.me/<name>` the preview is off.
- Telegram shows roughly the last posts of a channel in pages of about 20; very old history may be missing or removed by the channel.
- Counts (views, subscribers, reactions) are as shown at scrape time and rounded by Telegram.
- Telegram can rate-limit heavy use; keep runs modest and spread large jobs over time.

### FAQ

**How much does it cost?** $0.0015 per post plus a $0.00005 start event per run. 1,000 posts = $1.50. You are not charged for posts removed by filters, channels without a preview, or anything past your limits.

**Do I need a Telegram account, API ID or phone number?** No. Nothing is needed.

**Can it scrape private channels, groups or members?** No, by design. Only public channel previews.

**How do I know a channel works?** Open `https://t.me/s/<name>` in a browser. If you see posts, it works.

**Do I need proxies?** No. The preview answers normal requests; no proxy is used or offered.

**Can I search by keyword across all of Telegram?** No. This Actor reads the channels you list; the keyword filter applies within them.

**Can I schedule it?** Yes, with Apify Schedules or the API. Use "Posts from date" for a daily new-posts feed.

**Which export formats are available?** JSON, CSV, Excel, XML, HTML or RSS from the Console or API, or Apify integrations such as Make, Zapier, n8n and Google Sheets.

**Something broke or a field is missing?** Open an issue on the Actor's Issues tab with the run link.

### Use with AI agents / MCP

Agents can call this Actor through the [Apify MCP server](https://mcp.apify.com) (`https://mcp.apify.com`). Add the Actor to the server, or let the agent find it with the `search-actors` tool, then call it by name:

- Actor: `santis_hermes/telegram-channel-scraper`
- Example prompt: "Get the last 10 posts from the Telegram channels bloomberg and wublockchainenglish that mention bitcoin."
- Minimal input:

```json
{
  "channels": ["bloomberg", "wublockchainenglish"],
  "keywords": ["bitcoin"],
  "maxPostsPerChannel": 10
}
```

# Actor input Schema

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

Public Telegram channels to scrape, one per line. Accepts a username (durov), an @handle (@durov) or a link (https://t.me/durov, https://t.me/s/durov). Private channels, groups, users, bots and invite links have no public preview and are reported as errors in the run summary.

## `maxPostsPerChannel` (type: `integer`):

Maximum number of posts returned for each channel, newest first. The scraper pages back through older posts (about 20 per page) until this many matching posts are found. You are charged only for posts returned.

## `maxItems` (type: `integer`):

Optional hard stop for the whole run across all channels. Leave empty for no overall cap (the per-channel limit still applies).

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

Keep only posts published on or after this date and time (UTC). Format YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ, for example 2026-10-01. Scanning of a channel stops once it reaches older posts. Leave empty for no start date.

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

Keep only posts published on or before this date (UTC). A plain date such as 2026-10-20 includes the whole day. Use together with Posts from date to get one time window. Leave empty for no end date.

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

Keep only posts whose text or links contain at least one of these words or phrases, for example bitcoin, airdrop, breaking. Not case sensitive. Leave empty to keep all posts. Posts that do not match are skipped and not charged.

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

Adds one extra row per channel (type = channel) with the title, description, subscriber count as shown on the preview, photo, video and link counters, verified flag and profile picture URL. Billed as a separate channel-info event.

## `maxPagesPerChannel` (type: `integer`):

Safety limit on how many preview pages (about 20 posts each) are read per channel, so a keyword or date filter that matches nothing cannot scan a whole channel history. Raise it to search deeper into old posts.

## Actor input object example

```json
{
  "channels": [
    "telegram",
    "durov",
    "bloomberg"
  ],
  "maxPostsPerChannel": 20,
  "includeChannelInfo": false,
  "maxPagesPerChannel": 60
}
```

# Actor output Schema

## `posts` (type: `string`):

All posts as a table with the key columns.

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

Every scraped post with all fields.

## `runSummary` (type: `string`):

Per-channel counts, errors per channel, charged events.

# 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": [
        "telegram",
        "durov",
        "bloomberg"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapefy/telegram-channel-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": [
        "telegram",
        "durov",
        "bloomberg",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("scrapefy/telegram-channel-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": [
    "telegram",
    "durov",
    "bloomberg"
  ]
}' |
apify call scrapefy/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapefy/telegram-channel-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/jTtRfgDfwhAfmpRtb/builds/ClmJ4Lbk5UkxSaBNp/openapi.json
