# Telegram Channel Scraper API - Messages, Reactions & Search (`vonsensey/telegram-channel-scraper-api`) Actor

Scrape public Telegram channels without a Telegram account. Complete post history back to the first message, keyword search across many channels at once, view counts, emoji reactions, forward attribution and reply threads. No phone number, no session string. Pay per post, failures are free.

- **URL**: https://apify.com/vonsensey/telegram-channel-scraper-api.md
- **Developed by:** [Blackcube](https://apify.com/vonsensey) (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

from $2.00 / 1,000 messages

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Telegram Channel Scraper API - Messages, Reactions & Search

**More from this account:** [YouTube Transcript Suite](https://apify.com/vonsensey/youtube-transcript-scraper) · [Website Contact & Email Suite](https://apify.com/vonsensey/website-contact-email-extractor) · [Career Site & ATS Jobs Suite](https://apify.com/vonsensey/career-page-job-postings-scraper-api) · [Google News Suite](https://apify.com/vonsensey/google-news-scraper-api) · [Keyword Research Suite](https://apify.com/vonsensey/google-keyword-ideas-scraper) · [Shopify Store Intelligence Suite](https://apify.com/vonsensey/shopify-store-leads-scraper) · [eBay Data Suite](https://apify.com/vonsensey/ebay-scraper-api) · [Amazon Reviews Suite](https://apify.com/vonsensey/amazon-reviews-scraper-api) · [Reddit](https://apify.com/vonsensey/reddit-scraper-posts-comments-api) · [Meta Ad Library](https://apify.com/vonsensey/facebook-ads-library-scraper-meta-ad-api) · [Vinted](https://apify.com/vonsensey/vinted-scraper-api) · [Trustpilot Review Intelligence Suite](https://apify.com/vonsensey/trustpilot-reviews-scraper) · [App Store & Google Play Reviews Suite](https://apify.com/vonsensey/app-store-google-play-reviews-scraper) · [Business Reviews Suite](https://apify.com/vonsensey/business-reviews-aggregator-scraper-api) · [Amazon Product Data Suite](https://apify.com/vonsensey/amazon-product-scraper-api)

Scrape public Telegram channels without a Telegram account. Complete post history back to the first message, keyword search across many channels at once, view counts, emoji reactions, forward attribution and reply threads. No phone number, no session string. Pay per post, failures are free.

**No run-start fee, and failures are free.** Private channels, dead handles, the coverage report
and every error row cost nothing. A channel that was read but had nothing new since your last run
is billed once as a channel checked ($0.004) — half the price of a channel profile, and a fiftieth
of what a busy channel costs.

### No Telegram account. That is the whole point.

The best-known Telegram scrapers on this Store drive a **logged-in Telegram session**. To use
them you hand over a phone number, an `api_id`, an `api_hash` and a session string — and
then Telegram's anti-abuse systems treat that session as a scraper. The largest one in the
category has **307 open issues and none closed**, with titles like *"My account got blocked"*
and *"It wants a session password"*.

**This Actor has no session to block.** It reads what Telegram publishes to the open web.

| | Session-based scrapers | This Actor |
|---|---|---|
| Phone number | required | **none** |
| `api_id` / `api_hash` | required | **none** |
| Session string to paste and keep alive | required | **none** |
| Your Telegram account at risk | yes | **nothing to ban** |
| Works the first time you press Run | after setup | **immediately** |

### What you get

| Field | What it is |
|---|---|
| `text` | The post itself, emoji intact, formatting flattened to plain text |
| `channel`, `messageId`, `url` | Where it came from and a permalink straight to it |
| `channelId` | Telegram's own numeric channel id — **survives a channel rename**, which the @handle does not |
| `datetime` | Exact ISO timestamp, straight from Telegram. Not "2 days ago" |
| `views` | View count as an integer |
| `viewsLabel` | What Telegram actually rendered, verbatim: `"1.06K"`, `"14.7M"` |
| `author`, `authorUrl` | The posting identity and a link to it |
| `reactions` | Every emoji reaction with its count, including paid Star reactions |
| `reactionsTotal` | All reaction counts summed, so posts sort by engagement in one field |
| `forwardedFrom`, `forwardedFromUrl` | The channel a post was forwarded from — the forward graph nobody else surfaces |
| `replyTo` | The post it answers: message id, author and the quoted excerpt |
| `media` | Photos, videos, voice notes, documents, stickers, polls and link previews, with CDN URLs |
| `hashtags`, `mentions`, `links` | Pulled out of the text, deduplicated, ready to filter on |
| `isService` | Pinned-message and channel-event notices, so you can keep or drop them |

Turn on `enrichMessages` and every post also carries:

| Field | What it is |
|---|---|
| `sentiment` | `positive`, `negative` or `neutral` |
| `sentimentScore` | Normalised `-1` to `1`, so posts sort by strength of feeling |
| `keywords` | Content words, stopwords stripped out |
| `tickers` | `$BTC`, `#TAO` style tokens — the dominant content type on Telegram |
| `addresses` | Wallet and contract-shaped strings found in the post |
| `hasQuestion` | The post asks something |
| `language` | Dominant writing system (`latin`, `cyrillic`, `arabic`, …), or `null` when too short to tell |
| `wordCount` | Words in the post |

#### Four kinds of row, and only two of them cost anything

| Row | `type` | Costs |
|---|---|---|
| One public channel post | `message` | $2.00 per 1,000 (+$4.00 per 1,000 if enriched) |
| A channel's profile card, with its subscriber count | `channel` | $8.00 per 1,000 |
| What each channel produced, and whether your cap stopped it | `coverage` | **free** |
| Anything that could not be served, with the reason | `error` | **free** |

### Pricing

| Your Apify plan | message / 1,000 | channel profile / 1,000 | enrichment / 1,000 |
|---|---:|---:|---:|
| Free | $4.00 | $16.00 | $8.00 |
| Bronze | $3.33 | $13.40 | $6.70 |
| Silver | $2.53 | $10.20 | $5.10 |
| Gold | $2.00 | $8.00 | $4.00 |
| Platinum | $2.00 | $7.40 | $3.70 |
| Diamond | $2.00 | $7.00 | $3.50 |

You are charged **per post delivered**, once. A post reached through two different inputs is
billed a single time, and on a scheduled re-run with `onlyNewSinceLastRun` the posts you
already have are neither delivered nor charged again.

`searchQuery`, `dateFrom` and `dateTo` narrow the run **before** billing, so they reduce the
bill rather than filtering something you already paid for.

#### What this listing's own defaults cost

Press Run without changing anything and the **most** this listing can charge on the Gold tier is **$2.00** — up to **1,000 posts** at $2.00 per 1,000 = $2.00.

A channel that returns nothing is billed one channel checked ($0.004) rather than a post, so the real bill is usually far lower. Lower
`maxItems` or set `maxCostUsd` to cap it outright.

Set `maxCostUsd` for a hard ceiling in dollars, or `maxItems` for one in rows.

### Try it

```json
{
  "channels": [
    "@durov",
    "@telegram"
  ],
  "maxMessagesPerChannel": 200,
  "maxItems": 1000
}
```

### Worked recipes

#### A channel's complete history, back to post one

Set the per-channel cap high and it walks all the way to the very first message ever
published, then stops on its own.

```json
{ "channels": ["@durov"], "maxMessagesPerChannel": 100000, "maxItems": 50000 }
```

#### Many channels in one run

Paste a list in any form — `@name`, `name`, or a `t.me` link. They are read in parallel
and land in one dataset, each row tagged with its channel.

```json
{ "channels": ["@durov", "telegram", "https://t.me/TelegramTips"], "maxMessagesPerChannel": 500 }
```

#### One keyword, watched across every channel you care about

Telegram's own in-channel search does the matching, so you are billed for the hits, not for
the history you filtered out.

```json
{ "channels": ["@durov", "@telegram", "@TelegramTips"], "searchQuery": "airdrop", "maxItems": 500 }
```

#### Watch daily and pay only for new posts

Put it on a schedule with `onlyNewSinceLastRun`. It remembers the last post it saw per
channel, so tomorrow's run delivers — and bills — only what is genuinely new.

```json
{ "channels": ["@bloomberg", "@BBCWorld"], "onlyNewSinceLastRun": true, "stateLabel": "news-watch" }
```

#### Just one date range

The walk stops as soon as it reaches older posts, so a narrow window is cheap rather than a
full history you then throw away.

```json
{ "channels": ["@telegram"], "dateFrom": "2026-01-01", "dateTo": "2026-03-31", "maxMessagesPerChannel": 5000 }
```

#### Subscriber counts for a list of channels

One row per channel — title, description, avatar, exact subscriber count and whether the
handle is a channel, a group or a bot. Bulk vetting in a single run.

```json
{ "channels": ["@durov", "@telegram", "@Bitcoin", "@TelegramTips"], "mode": "channels" }
```

#### Find what actually performs

Every post with its view count and its full reaction breakdown, plus the channel's
subscriber count on the same dataset, so reach is a division rather than a guess.

```json
{ "channels": ["@durov"], "mode": "both", "maxMessagesPerChannel": 300 }
```

#### Crypto channels as a structured feed

Tickers, wallet and contract addresses, sentiment and reactions extracted from every post —
for backtesting and comparing channels, not for following calls.

```json
{ "channels": ["@cryptosignals", "@whale_alert"], "enrichMessages": true, "maxItems": 2000 }
```

#### Score the lot

Sentiment, keywords, tickers and question intent on every post, so ten thousand rows sort
into piles worth reading first.

```json
{ "channels": ["@Bitcoin"], "enrichMessages": true, "maxMessagesPerChannel": 2000 }
```

#### Straight to a spreadsheet

Run it, then use the dataset's own Export button for CSV, JSON, Excel or XML. The columns
are flat and documented.

```json
{ "channels": ["@telegram"], "maxMessagesPerChannel": 5000, "includeHtml": false }
```

#### Cap the spend, not just the rows

A hard ceiling in dollars. The run stops the moment it would exceed it, and still writes its
coverage report.

```json
{ "channels": ["@durov", "@telegram"], "maxMessagesPerChannel": 100000, "maxCostUsd": 5 }
```

### Honest limits

These are measured, and stated because a listing that hides them wastes your first run.

- **View counts above 1,000 are rounded by Telegram itself.** Telegram publishes `"1.06K"`,
  not `1062`. Rather than pass a rounded number off as exact, every row carries both the
  expanded integer in `views` and Telegram's verbatim string in `viewsLabel`, so you always
  know which you are filtering on. Below 1,000 the count is exact.
- **Member and subscriber lists are not available, and nothing here pretends otherwise.**
  Telegram does not publish who is in a channel. The channel's *total* subscriber count is
  returned; the individual people are not, by any public route. Actors that claim to extract
  member lists are driving a logged-in account to do it.
- **Private channels, invite-only groups, user profiles and bots have no public page.** They
  come back as a free `error` row naming the reason, not as a failed run.
- **Some reactions are custom emoji with no glyph in the published page.** Those are reported
  as `custom:<id>` with their exact count, rather than a guessed emoji. Standard emoji and
  paid Star reactions come back as themselves.
- **No phone numbers, and no way to message anyone.** This reads published posts. It does not
  add members, send messages or resolve contacts, and it never will.

### Legal and privacy

Not affiliated with, endorsed by, or sponsored by Telegram. "Telegram" is a trademark of
Telegram Messenger Inc. and is used here only to describe what this Actor reads.

It collects **publicly published channel content only** — nothing behind a login, nothing
private, no member lists. Where posts contain personal data you are the controller for
whatever you collect under GDPR: you are responsible for having a lawful basis, honouring
deletion requests, and not using what you collect to contact people who have not agreed to
hear from you.

### Use it from n8n, MCP, the API or a schedule

Built to be called by a workflow, not only from the Store form. The Actor is `vonsensey/telegram-channel-scraper-api`; every snippet below sends `{}`, which runs the defaults shown on the form — replace it with your own input.

#### n8n

Install the **Apify** community node (`@apify/n8n-nodes-apify` under *Settings → Community Nodes*, or search "Apify" on n8n Cloud). Add **Apify → Run Actor** with Actor `vonsensey/telegram-channel-scraper-api` and your input JSON, then **Apify → Get Dataset Items** on the run's `defaultDatasetId` and pipe the rows anywhere. For scheduled runs, the **On new Apify Event** trigger fires when a run of this Actor finishes.

#### MCP (Claude, Cursor, VS Code, any MCP client)

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=vonsensey/telegram-channel-scraper-api",
      "headers": {
        "Authorization": "Bearer <YOUR_APIFY_TOKEN>"
      }
    }
  }
}
```

Your agent then calls `vonsensey/telegram-channel-scraper-api` as a tool with the same input the form takes and reads the dataset back.

#### REST API (one call, rows in the response)

```bash
curl -X POST "https://api.apify.com/v2/acts/vonsensey~telegram-channel-scraper-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" -d '{}'
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("vonsensey/telegram-channel-scraper-api").call(run_input={})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row)
```

#### JavaScript

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('vonsensey/telegram-channel-scraper-api').call({});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Make, Zapier, LangChain, CrewAI

The Apify app in **Make** and **Zapier** has a *Run an Actor* module: pick `vonsensey/telegram-channel-scraper-api`. In **LangChain** and **CrewAI** the Apify tool wrappers take the same Actor id. A daily **schedule** needs nothing but the Console: *Schedules → Create → this Actor → cron*, and the dataset fills on its own.

### Use cases

- **Archive a channel before it disappears.** Walk a public channel all the way back to its first post and keep the lot — text, timestamps, views, reactions and media links — as a dataset you own.
- **Watch a term across a whole watchlist.** One keyword, many channels, on a schedule. Each run returns the new matches and nothing else, so a daily watch costs what the day produced.
- **Compare channels on evidence.** Subscriber counts, posting cadence, views per post and the full reaction breakdown, side by side, so reach is a division rather than a guess.
- **Feed a model or a dashboard.** Flat, documented rows with an exact ISO timestamp on every post — ready to embed, chart or load into a warehouse without a cleaning pass.

### Run it on a schedule

A one-off pull answers a question; a schedule answers it every day without you. Open **Schedules** in the Apify Console, point a cron at this Actor, and the dataset keeps filling on its own — no server, no cron box, no babysitting. Everything here is built to be re-run: you are billed per post delivered, so a scheduled run that finds nothing new bills one channel checked ($0.004) per channel instead of a post — a fiftieth of what a busy channel costs, and nothing at all for a channel that could not be read.

### FAQ

#### Do I need a Telegram account or API key to use this?

No. No phone number, no api\_id, no api\_hash and no session string. It reads what Telegram publishes to the open web, so there is nothing to log in with and nothing that can get your account limited — which is the single most common complaint against the session-based scrapers in this category.

#### Can I scrape Telegram group members or subscriber lists?

No, and nothing in this suite claims to. Telegram does not publish who is in a channel on any public route; only the total subscriber count is public, and that is returned. Actors that offer member lists are driving a logged-in account to get them.

#### Can I export Telegram messages to CSV, JSON or Excel?

Yes. Every run writes a dataset you can export in one click from the Console, or pull straight from the API in JSON, CSV, XLSX or JSONL. There is a dedicated Actor in this suite for exactly that.

#### What happens with a private channel or a handle that no longer exists?

It comes back as a free row naming the reason, rather than failing the run. You are never charged for a channel that returned nothing, and there is no run-start fee.

***

Something wrong, or a field you need that is missing? Open an issue on the **Issues** tab — it is read and it gets fixed. If this saved you time, a rating on the Store page helps the next person find it.

# Actor input Schema

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

Public channels to scrape. Paste them in any form — <code>@durov</code>, <code>durov</code>, <code>https://t.me/durov</code> or <code>t.me/s/durov</code> — one per line. Private channels, user profiles and bots have no public page and are reported as free error rows instead of failing the run.

## `mode` (type: `string`):

<b>Messages</b> returns posts only. <b>Channel profiles</b> returns just the profile card for each channel (title, description, avatar, subscriber count) and no posts. <b>Both</b> returns each channel's profile plus its posts.

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

How far back to walk in each channel. Set it high to export a channel's complete history — the walk stops on its own at the first post ever published.

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

A hard ceiling on billable rows across every channel, so a run's cost is predictable before it starts. Free rows — errors, the coverage report and the summary — never count against it.

## `searchQuery` (type: `string`):

Return only posts matching this keyword or phrase, matched by Telegram's own in-channel search. Combine it with a list of channels to monitor a term across all of them in one run.

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

ISO date or date-time, for example <code>2026-01-01</code>. The walk stops as soon as it reaches older posts, so a narrow window is cheap.

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

ISO date or date-time, for example <code>2026-06-30</code>.

## `onlyNewSinceLastRun` (type: `boolean`):

For schedules. Remembers the last post id seen per channel and returns only what was published since — so a daily run bills for the new posts, not for history you already have.

## `stateLabel` (type: `string`):

Names the memory used by <b>Only new posts since the last run</b>. Leave it empty unless you run several different channel lists on separate schedules; then give each its own label. Re-enter the same label to keep tracking the same feed.

## `enrichMessages` (type: `boolean`):

Adds sentiment, keywords, ticker symbols such as $BTC, wallet/contract addresses, a question flag, writing system and word count to every post. Billed as its own event, only for posts actually scored. A lexicon signal for triage and sorting — not financial advice and not a moderation verdict.

## `includeHtml` (type: `boolean`):

Keeps the original post markup alongside the plain text. Useful when you need the exact formatting, links or embedded entities.

## `skipServiceMessages` (type: `boolean`):

Leaves out pinned-message and channel-event notices, keeping only real posts.

## `maxCostUsd` (type: `integer`):

Optional hard spend ceiling. The run stops delivering billable rows once it reaches this amount, and still writes its coverage report and summary.

## `concurrency` (type: `integer`):

How many channels to read at once. The default suits any list; raise it for very large batches.

## `useProxy` (type: `boolean`):

Off by default, and off is correct: this Actor reads Telegram's public pages directly and needs no proxy. Turn it on only if your own network blocks t.me.

## Actor input object example

```json
{
  "channels": [
    "@durov"
  ],
  "mode": "messages",
  "maxMessagesPerChannel": 200,
  "maxItems": 1000,
  "searchQuery": "airdrop",
  "onlyNewSinceLastRun": false,
  "enrichMessages": false,
  "includeHtml": false,
  "skipServiceMessages": true,
  "concurrency": 5,
  "useProxy": false
}
```

# Actor output Schema

## `messages` (type: `string`):

No description

# 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",
        "@telegram"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("vonsensey/telegram-channel-scraper-api").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",
        "@telegram",
    ] }

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

```

## MCP server setup

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

```

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/hlvZI9UL0sxe3Uyw7/builds/KtXv5sfa84CunkjMm/openapi.json
