# Telegram Channel Scraper (`asgardlabs/telegram-channel-api`) Actor

Posts from any public Telegram channel, with views, media, links and polls, without a Telegram account or phone number. Only-new mode for schedules. Half a cent per channel, a tenth of a cent per post.

- **URL**: https://apify.com/asgardlabs/telegram-channel-api.md
- **Developed by:** [Asgard Labs](https://apify.com/asgardlabs) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 posts

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 – posts, views, media, links (no login)

Give it public Telegram channel names or links. Get the posts: text, date, view count, photos, videos, documents, link previews, polls, forwards, replies, links, mentions and hashtags, plus the channel's title, description and subscriber count. Walk back as far as you like, 20 posts per request.

Run it again with **only new** on and you get only posts newer than last time. Schedule it hourly or daily and you have a channel monitor. Telegram's own in-channel search is available too.

No phone number, no API key, no bot token, no login, no proxies. It reads the public web preview every public channel has.

### Telegram channel data per post

- **id, channel, channelTitle, subscribers, url, date, views, author, edited, isService**
- **text** (plain) and, with `includeHtml`, the original HTML
- **media** – photos, videos, videoThumbs, documents, hasVideo, hasRoundVideo, hasVoice, hasSticker
- **linkPreview** – url, siteName, title, description, image
- **poll** – question, options with percentages, voters
- **forwardedFrom, replyTo, links, mentions, hashtags**
- **isNew** – newer than the last run's marker

The run's key-value store also gets `CHANNELS` (title, description, photo, subscribers, counters per channel) and `SUMMARY`.

### Use cases

- News and market monitoring.
- Brand and crypto mentions.
- Competitor channels.
- Research datasets.
- Feeding posts into Slack or a spreadsheet.

### Input

| Field | Default | Meaning |
|---|---|---|
| `channels` | – | Usernames, `@handles` or t.me links. Case does not matter; the same channel written several ways counts once. Private invite links (`t.me/+…`, `joinchat`) are rejected with a clear reason. |
| `maxPostsPerChannel` | 100 | How far back to walk. Up to 20,000. |
| `onlyNew` | false | Only posts newer than the last run. One marker per channel in a key-value store. |
| `minDate` | – | Stop at this date, e.g. `2026-09-01`. |
| `query` | – | Telegram's own search inside the channel. |
| `keywords` | – | Keep only posts whose text contains any of these. |
| `includeHtml` | false | Keep the message HTML. |
| `timeoutSecs` | 300 | Time budget per channel. |
| `concurrency` | 4 | Channels read in parallel. |
| `stateStoreName` | telegram-channel-state | Where the markers live. Use a different name for a separate watchlist. |

### Output example

```json
{ "id": 3205, "channel": "bloomberg", "channelTitle": "Bloomberg", "subscribers": 172000, "url": "https://t.me/bloomberg/3205",
  "date": "2025-12-10T08:49:12+00:00", "views": 79600, "text": "…", "author": null,
  "media": { "photos": ["https://cdn5.telesco.pe/file/…jpg"], "videos": [], "videoThumbs": [], "documents": [], "hasVideo": false, "hasRoundVideo": false, "hasVoice": false, "hasSticker": false, "isUnsupported": false },
  "linkPreview": { "url": "https://www.bloomberg.com/…", "siteName": "Bloomberg", "title": "…", "description": "…", "image": "…" },
  "poll": null, "forwardedFrom": null, "replyTo": null, "links": ["https://…"], "mentions": ["@bloomberg"], "hashtags": ["#markets"], "edited": false, "isService": false, "isNew": true }
```

### How to use

1. Paste your channel names or t.me links, one per line into the input form. The example input is ready to run as is.
2. Click **Start**. Rows appear in the **Output** tab as they are found.
3. Download the table as CSV, JSON or Excel, or read it through the API.
4. For monitoring, create a **Schedule** and switch on `onlyNew`: later runs return only what changed, and only that is charged.

### Pricing

Pay per event: **`channel` $0.005** per channel that answered, **`message` $0.001** per post in the output. 1,000 posts from 10 channels is about $1.05. Private, deleted or preview-disabled channels are reported and free.

### Good to know

- **Public channels only.** Groups and private channels have no web preview. Comments and reactions are not in the preview either.
- **Media links** point to Telegram's CDN and can expire; download promptly if you need the files.
- **Stopping at your spend limit** is safe: the "last seen" marker is not moved by a stopped run, so the next run picks up everything again rather than skipping posts.
- **Errors are in plain words:** `channel unavailable: private, deleted, or preview disabled`, `timeout`, `ENOTFOUND (domain not found)`.

### Use from code or an AI agent

Every run can be started and read through the Apify API, the JavaScript and Python clients, or an AI agent with tool access. Open the **API** tab on this page for ready-made snippets. Pricing is per event, so an agent pays only for what it receives.

### FAQ

**Does it work on private channels or groups?**
No. Public channels with the web preview enabled only. Invite links are reported as unsupported and are not charged.

**Are comments and reactions included?**
No. The web preview does not expose them. Views, media, links, polls, forwards and replies are.

**How far back can it go?**
As far as the channel's history and your `maxPostsPerChannel`, up to 20,000 posts per channel per run.

**Will Telegram block it?**
The preview is public and needs no account, so there is nothing to ban. Bursts are rate-limited; four channels in parallel is safe.

**Can I get only new posts each run?**
Yes. Switch on `onlyNew`; the last post id per channel is remembered in your account.

# Actor input Schema

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

Public channel usernames or links: durov, @telegram, https://t.me/bloomberg. Case does not matter. Private invite links (t.me/+…) are not supported.

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

Newest posts first. 20 posts per page are read, so 100 posts is about 5 requests.

## `onlyNew` (type: `boolean`):

Remembers the last post id per channel in a key-value store. Ideal for hourly/daily schedules.

## `minDate` (type: `string`):

e.g. 2026-09-01

## `query` (type: `string`):

Uses Telegram's own channel search; returns matching posts only.

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

Keep only posts whose text contains at least one of these words, case-insensitive. Filtered posts are not charged.

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

Add the post's formatted HTML next to the plain text. Rows get larger.

## `timeoutSecs` (type: `integer`):

A channel that takes longer is reported as timed out and not charged.

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

How many channels to read at the same time. Telegram rate-limits bursts; four is safe.

## `stateStoreName` (type: `string`):

Key-value store in your account that keeps the last post id per channel for "only new". Letters, digits and dashes.

## Actor input object example

```json
{
  "channels": [
    "telegram",
    "durov",
    "bloomberg"
  ],
  "maxPostsPerChannel": 100,
  "onlyNew": false,
  "keywords": [],
  "includeHtml": false,
  "timeoutSecs": 300,
  "concurrency": 4,
  "stateStoreName": "telegram-channel-state"
}
```

# Actor output Schema

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

One row per post: channel, id, date, text, views, media, links, forwards, replies, poll, isNew.

## `summary` (type: `string`):

Counts, skipped inputs with reasons, errors in plain words, charge-limit state.

# 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("asgardlabs/telegram-channel-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": [
        "telegram",
        "durov",
        "bloomberg",
    ] }

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

```

## MCP server setup

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