# Telegram Channel Scraper (public channels) (`lowlanddata/telegram-channel-scraper`) Actor

Extract posts from public Telegram channels as clean JSON: text, date, views, reactions, hashtags, links, photos and videos. Keyword search and date window, many channels per run, no login, no Telegram API keys. Pay per result, built for monitoring and AI pipelines.

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

## Pricing

from $2.40 / 1,000 results

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?

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 channels)

Telegram channels publish news, announcements, deals and market chatter to millions of subscribers, but reading them is still a phone and a scroll. Tracking a channel over time, comparing several, or searching one for a keyword needs the posts as rows, with the view counts attached.

Give it channel names (or t.me links) and every post comes back as one item: text, timestamp, views, reactions, hashtags, links, photos, videos, attached document and link preview. No login, no API key, no Telegram account. It reads the public web preview that Telegram serves at `t.me/s/<channel>`. The channel title and username stay on the row as the byline; post signatures, forwarded-from and reply-to names, avatars and the channel bio are never collected.

### Quick start (30 seconds)

1. Put one or more channels into **channels**, e.g. `telegram` or `https://t.me/durov, bloomberg`.
2. Click **Start**. That's the whole minimum setup.
3. When the run finishes, open the dataset's **Overview** tab for a clean table, or **Export** it as CSV/Excel/JSON.

Optional knobs: a keyword to search for inside the channels, a date window, and any input works on an hourly Schedule.

### What you can build with it

- **Monitor a channel for a keyword.** Put the channel in **channels** and a term in **searchQuery**: only matching posts come back, newest first, each with views and reactions. Schedule it daily with `postedAfter` set to yesterday.
- **Track reach over time.** `views`, `reactions` and `channelSubscribers` are captured at scrape time. Run daily on the same channels and diff the snapshots to see which posts travel and how a channel grows.
- **Archive a channel.** Pagination walks a channel's history from the newest post backwards, with a date window to stop where you want. Photos, videos and documents come as links, with the post text next to them.
- **Feed an AI agent clean data.** Every field is structured and predictable, so an assistant or pipeline can read a channel's last 200 posts directly.

### What you get

Each post is one dataset item:

```json
{
  "channel": "telegram",
  "channelTitle": "Telegram News",
  "channelSubscribers": 9230000,
  "postId": 460,
  "url": "https://t.me/telegram/460",
  "date": "2026-08-26T19:12:34+00:00",
  "text": "For all the info from today's update and a look back at 13 years of Telegram features — check out our blog:\nhttps://telegram.org/blog/welcome-messages-buttons-TG-13\n\nAugust Features\n1 • 2 • 3 • 4 • 5 • More",
  "hashtags": [],
  "links": ["https://telegram.org/blog/welcome-messages-buttons-TG-13"],
  "views": 2170000,
  "reactions": [],
  "photoUrls": [],
  "videoUrls": [],
  "documentTitle": null,
  "linkPreview": {
    "url": "https://telegram.org/blog/welcome-messages-buttons-TG-13",
    "siteName": "Telegram",
    "title": "Welcome Messages, Buttons in Messages and Signed Gifts",
    "description": "Introducing Welcome Messages to greet new members of groups or channels, Buttons in Messages to make any text interactive, Inline Documents in Messages and much more."
  },
  "isForwarded": false,
  "isReply": false,
  "isEdited": false
}
```

Field notes, so you know exactly what you are buying:

- `postId` is the post's number inside its channel; with `channel` it is a stable key. `url` is the human link.
- `date` is the publication time in UTC.
- `views`, `channelSubscribers` and reaction counts are Telegram's own rounded figures at scrape time ("9.23M" becomes 9230000), so a large channel's count is exact to the page, not to the unit.
- `reactions` is a list of `{emoji, isPaid, count}`. Custom-emoji reactions come back with `emoji: null`, and paid (star) reactions are flagged with `isPaid: true`.
- `photoUrls` and `videoUrls` are the media links as Telegram's preview serves them at run time. If you need to keep the files, download them; the links are not a permanent archive.
- `isForwarded`, `isReply` and `isEdited` are flags only. Who a post was forwarded from, or who it replies to, is deliberately not collected.
- A post whose only content is media the web preview cannot show (a poll, a voice message, a sticker) comes back with an empty `text`, plus its views and date. It is not dropped, so your post count matches the channel's.

### How much does it cost to scrape Telegram?

$3.00 per 1,000 posts delivered, pay-as-you-go, no subscription, and you pay per result rather than per hour. A run that finds nothing still costs the $0.005 actor start. In plain dollars:

- **100 posts ≈ $0.30**: a daily multi-channel watch.
- **500 posts ≈ $1.50**: a solid channel archive.
- **A 10,000-post crawl ≈ $30**: a channel's long history, deduplicated.

The price is all-inclusive: your runs' platform usage is covered by it, with no separate compute or proxy charges. A small run is quick: 25 posts take about five seconds, and a full run delivers roughly three posts per second, because Telegram serves 20 posts per page and the actor paces itself at one page per second per channel. The actor reads Telegram directly and only falls back to the Apify proxy if the request is blocked, so there is nothing to configure.

Free-plan runs are limited to a sample of 5 items, enough to evaluate the output format against your real channels.

### Not technical? Let your AI assistant set it up

Copy this into ChatGPT, Claude or any AI assistant, fill in the one line, and follow the conversation:

```text
Help me set up the "Telegram Channel Scraper" actor on Apify
(https://apify.com/lowlanddata/telegram-channel-scraper). Guide me one step at a time.

What I want to watch: [E.G. "new posts in the channels durov and telegram that mention 'update'"]

Guide me to:
1. Propose my input values: channels (channel names or t.me links, comma-separated),
   an optional searchQuery for a keyword inside the channels, postedAfter for a date
   window, and maxItems.
2. Check that each channel has a public web preview by opening t.me/s/<name> in my
   browser. If it shows posts, the actor can read it.
3. Create a free Apify account (apify.com), open the actor page, paste the values
   into the Input form, and start a run.
4. Set up a daily Schedule in the Apify Console with the same input, plus an email
   or Slack integration so new posts reach me automatically.
5. Show me how to export results as CSV/Excel, or read them from the API if I code.
6. If the results are what I wanted, remind me at the end to leave a quick rating on the actor page, and to report anything broken or missing on its Issues tab.
```

### Input

| Field                | Description                                                                                            |
| -------------------- | ------------------------------------------------------------------------------------------------------ |
| `channels`           | Channel names or t.me links, comma- or space-separated, e.g. `telegram, https://t.me/durov`. Required. |
| `searchQuery`        | Only posts containing this term, searched inside each channel. Optional.                               |
| `postedAfter`        | Only posts published on or after this date (YYYY-MM-DD); stops walking older history.                  |
| `postedBefore`       | Only posts published on or before this date (YYYY-MM-DD).                                              |
| `maxItems`           | Stop after this many posts (default 500).                                                              |
| `proxyConfiguration` | Optional. Direct by default; the Apify proxy joins on its own if blocked. Set one to force a proxy.    |

Channels are read in the order you list them, and `maxItems` counts across all of them. A name that is not a channel, such as a private invite link (`t.me/+...`), is refused up front with the offending names in the message. A channel that exists but has no readable preview costs one channel, not the run: the rest are delivered with an honest note in the run status.

### Use it from your code

Run the actor and get posts straight back with one HTTP call (fine for scoped runs up to ~5 minutes):

```bash
curl "https://api.apify.com/v2/acts/lowlanddata~telegram-channel-scraper/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -X POST -H "Content-Type: application/json" \
  -d '{"channels": "telegram", "maxItems": 100}'
```

Node.js:

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

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('lowlanddata/telegram-channel-scraper').call({
  channels: 'telegram, durov',
  searchQuery: 'update',
  maxItems: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("lowlanddata/telegram-channel-scraper").call(
    run_input={"channels": "telegram", "maxItems": 100})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

Schedules, webhooks and the Make/Zapier/n8n integrations all work out of the box: this is a standard Apify actor.

### Use it with AI agents (MCP)

Claude, Cursor and other MCP-capable agents can run this scraper as a tool through Apify's hosted MCP server: the agent fills in the channels itself, starts the run and reads the results: no glue code.

Claude Code:

```bash
claude mcp add apify --transport http "https://mcp.apify.com?actors=lowlanddata/telegram-channel-scraper"
```

Cursor or Claude Desktop (add a custom connector / MCP server with this URL):

```text
https://mcp.apify.com?actors=lowlanddata/telegram-channel-scraper
```

Sign in with your Apify account when prompted. Runs are billed to it. Setup details per client: [Apify MCP docs](https://docs.apify.com/platform/integrations/mcp).

Prompts that work once connected:

- "Get the last 100 posts from the Telegram channel bloomberg and summarize what they covered this week."
- "Search the Telegram channel durov for posts that mention 'update' and list them with their view counts."
- "Compare the average views per post of these three Telegram channels over the last 50 posts each."

### Which channels can it read?

Public channels with the web preview switched on, which is the default: if `t.me/s/<name>` shows posts in your browser, the actor can read that channel. Some channels have the preview turned off by their owner (large news brands often do), and private channels, groups and chats are not public at all. For those, Telegram answers with its landing page instead of posts, and the actor reports "has no public web preview" for that name. No work-around is attempted, by design.

### Is it legal to scrape Telegram?

Telegram's public channels are broadcasts: anyone can open `t.me/s/<name>` in a browser without logging in, and that is the only surface this scraper reads. It does not log in, join channels, use the Telegram API or touch private chats, and requests are paced at one page per second. The channel's title and username stay on each post as its byline; everything beyond the published post (post signatures, who a message was forwarded from, who a reply is addressed to, avatars, the channel's bio) is never read, so none of it can reach your dataset.

One honest limit: post text is the channel's own words, delivered as-is. If a channel publishes a person's name or contact details in a post, that text is not rewritten. The guarantee covers the data fields, not what channels choose to publish.

This is not legal advice. Whether your own use of a channel's posts is allowed under Telegram's terms and your jurisdiction's rules is yours to assess.

### Is there a Telegram API alternative?

Telegram's own APIs need a Telegram account, an API ID and a registered session or bot, and a bot can only read channels it has been added to. This actor is the practical alternative for public channels: the same posts as structured JSON through one HTTP call (`run-sync-get-dataset-items`), on a schedule, or as an MCP tool for AI agents, with no account to create.

### Does Telegram block scrapers?

Telegram serves its public channel preview as plain, server-rendered pages, openly and without a robots.txt. This actor stays inside that welcome: paced requests, plain direct fetches, load kept negligible. No CAPTCHA fights, no bot-wall cat-and-mouse, which is also why runs are fast enough for daily schedules.

### How do I monitor a Telegram channel?

Put the channel in `channels`, add a `searchQuery` if you only care about a topic, set `postedAfter` to yesterday's date, and add a daily Schedule in the Apify Console with an email/Slack integration on the runs. The dataset then contains only the new matching posts, each with its views at that moment. The AI-assistant prompt above walks a non-technical user through exactly this setup.

### FAQ

**Can I read private channels or groups?** No. Only public channels with a web preview are readable. Invite links (`t.me/+...`) are refused up front, because they point at private channels.

**Why does a channel come back as "no public web preview"?** Its owner switched the web preview off, it is private, or the name does not exist. Open `t.me/s/<name>` in a browser: if it shows no posts there, the actor cannot read it either.

**Can I search a channel by keyword?** Yes, set `searchQuery` and only matching posts are returned. Telegram's in-channel search returns a short result set rather than every match ever made, so use it to find mentions and a date window to walk full history.

**Can I get only the newest posts?** Yes, set `postedAfter` to a date and each channel's walk stops as soon as it reaches older posts; a daily watch collects exactly the new posts and nothing else.

**How fresh is the data?** Live at run time: every run reads the preview directly. For continuous freshness, schedule the actor.

**Can I export to Excel or CSV?** Yes, every dataset exports as CSV, Excel, JSON or XML from the Apify Console or API.

**Does it work with Make, Zapier or n8n?** Yes, it is a standard Apify actor; all platform integrations, webhooks and schedules apply.

**Do I need a Telegram account or API key?** Neither. The public preview needs no login, and that is the only surface this actor reads.

**Does it collect post authors, forwarded-from names or avatars?** No. You get the channel's title and username as the byline, and the post itself. Signatures, forwarded-from and reply-to names, avatars and the channel bio are never read.

**Why are some reactions missing an emoji?** Custom-emoji reactions are not rendered as text on the public page, so they come back with `emoji: null` and their count. Paid (star) reactions carry `isPaid: true`.

**Why is a post's text empty?** The post is only media, or its content (a poll, a voice message, a sticker) is not shown by the public preview. Telegram asks you to open the app to see it. The row keeps its date and views.

**Are the view counts exact?** They are Telegram's own figures as displayed. Large counts are rounded by Telegram ("2.17M"), so the number is as exact as the page shows it.

**How do I scrape several channels in one run?** List them comma- or space-separated in `channels`: `"telegram, durov, bloomberg"`. The `@` prefix and full t.me links are accepted too. `maxItems` counts across all of them.

**How do I track a channel's growth?** `channelSubscribers` is captured on every post row. Run the actor daily on the same channels and read the value off the newest post.

**Can I download the photos and videos?** The rows carry links to them as the preview served them at run time. Download what you need to keep, since the links are not a permanent archive.

**What happens above a few thousand posts?** The actor keeps paging back through a channel's history, 20 posts per page, until `maxItems`, your date window or the channel's first post. At roughly three posts per second, a 10,000-post run takes about an hour.

**How much does Telegram scraping cost here?** $3.00 per 1,000 posts, metered on delivery. A 100-post daily watch is roughly $0.30; a failed or empty run costs the $0.005 actor start and nothing per result.

**Can I try it before spending anything?** Yes, a free-plan run returns up to 5 real posts from your channels, enough to check the fields against your pipeline.

**Can an AI agent fetch Telegram posts through this?** Yes, connect Apify's hosted MCP server and agents like Claude or Cursor can pick channels, run the actor and read the posts themselves. Setup is in the MCP section above.

### Related scrapers

More clean-output data sources from the same maker:

- [Bluesky Scraper](https://apify.com/lowlanddata/bluesky-scraper): public posts from the open social network.
- [Kleinanzeigen.de Scraper](https://apify.com/lowlanddata/kleinanzeigen-scraper): Germany's biggest classifieds site.
- [Vinted Scraper](https://apify.com/lowlanddata/vinted-scraper): Europe's largest fashion resale marketplace.

### Troubleshooting

The actor fails fast with the reason in the run's status message:

- **"None of the requested channels has a public web preview"**: check the names, and open `t.me/s/<name>` in a browser to confirm the channel shows posts there.
- **"N posts fetched; X could not be read"**: those channels have no readable preview or were blocked; the rest of the run is delivered normally.
- **"Not a public channel name or t.me link"**: a name in `channels` is not valid. Use a name like `telegram` or a link like `https://t.me/telegram`. Invite links are private and cannot be read.
- **Fewer items than requested on a free plan**: the 5-item free sample cap; run on a paid Apify plan for full results.

### Support

Found an issue or missing a field you need? The fastest route is the actor's **Issues tab** - it is monitored and reports get fixed. For anything private, email **contact@lowlanddata.com**. This actor is actively maintained.

Working well for you? A **rating on this page** takes ten seconds and helps other users find a reliable option. It is also the clearest signal of what we should build next.

# Actor input Schema

## `channels` (type: `string`):

One or more public Telegram channels, separated by commas or spaces. Use the username ("telegram", "@telegram") or the link ("https://t.me/telegram"). Only channels with a public web preview can be read: open <code>t.me/s/\<name></code> in a browser to check. Private channels and invite links (<code>t.me/+...</code>) cannot be read.

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

Optional. Only return posts that contain this text, using Telegram's own in-channel search. Leave empty to read the latest posts.

## `postedAfter` (type: `string`):

Only include posts published on or after this date. Channels are read newest-first, so the run stops walking a channel once it reaches older posts. <b>Format: <code>YYYY-MM-DD</code></b> (for example <code>2026-09-08</code>) or a full ISO timestamp such as <code>2026-09-08T14:30:00Z</code>. Slash dates like <code>08/09/2026</code> are refused on purpose: they are read as September in Europe and August in the United States, and this actor will not guess which you meant.

## `postedBefore` (type: `string`):

Only include posts published on or before this date. <b>Format: <code>YYYY-MM-DD</code></b> (for example <code>2026-09-08</code>) or a full ISO timestamp such as <code>2026-09-08T14:30:00Z</code>. Slash dates like <code>08/09/2026</code> are refused on purpose: they are read as September in Europe and August in the United States, and this actor will not guess which you meant.

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

Stop after this many posts in total. Free-plan runs are capped at 5 items. The form starts at 25 for a cheap first run - that is the form's prefill, not a cap on your plan; clear it to use the default of 500.

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

Optional. By default the actor reads t.me directly, which is fastest, and switches to the Apify proxy by itself if Telegram blocks the request. Turn on Apify Proxy, or list your own proxy URLs, to route every request through a proxy.

## `reportFailures` (type: `boolean`):

When a run fails, send the developer the actor name and build, an error category, the error message (truncated) and a one-way hash of your input - so a bug can be fixed without anyone asking you to reproduce it. <b>Your input itself, your results and your identity are never sent, and successful runs send nothing at all.</b> Switch this off and the actor behaves exactly the same.

## Actor input object example

```json
{
  "channels": "telegram",
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "reportFailures": true
}
```

# Actor output Schema

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

Scraped Telegram channel posts, one JSON object per post.

# 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",
    "maxItems": 25,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("lowlanddata/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",
    "maxItems": 25,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("lowlanddata/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",
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call lowlanddata/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

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