# Telegram Channel Scraper — Posts, Views, Video Transcripts (`steadyfetch/telegram-channel-scraper`) Actor

Telegram channel scraper for public channels: each post as a row — date, views, text, links, media, forwards and replies — newest first, with only new posts since your last run and opt-in transcripts of the voice notes and short videos Telegram shows publicly. No login, no account.

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

## Pricing

from $0.50 / 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.
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 — Posts, Views, Video Transcripts

Name a public Telegram channel and get its posts as rows, newest first: when each post was published, how many views it has, the full text, the links in it, what media it carries, who it was forwarded from and which post it replies to. No login, no Telegram account, no phone number. **Run it on a schedule and it returns only the posts that are new since your last run** — posts your account already has are skipped and never charged twice. Turn on transcripts and every voice note, round video and short video clip Telegram shows publicly comes back as text too. Click Start with nothing set and you get a sample: 5 posts from Telegram's own public channel, read live, and nothing is charged for it.

**Using an AI agent?** Pin this actor in Apify's MCP server with one link: `https://mcp.apify.com?tools=steadyfetch/telegram-channel-scraper`

- **Actor id:** `steadyfetch/telegram-channel-scraper`
- **Input:** `{ "channels": ["https://t.me/telegram"] }` — the one field you have to set: public channel links, `@names` or plain names. Add `"maxPosts": 100` for the per-channel limit and `"transcribe": true` for transcripts.
- **Public channels only:** a private chat, an invite link, or a channel whose owner turned the web preview off has no public page to read — each comes back as one row that costs nothing and says which it is.
- **Cap the bill:** set `maxTotalChargeUsd` on the run (a run option, not Actor input), e.g. `1.00` — the run stops when it reaches it.
- **Price:** from **$0.50 per 1,000 posts** on Gold and above, $0.80 per 1,000 on the Apify free plan; transcripts **$0.007 per started minute**; a channel read with nothing new is one **$0.002 channel check**. Full table below.
- **How often it changes:** news channels post every few minutes, most channels a few times a day, and view counts keep climbing for a day or two. An hourly or daily run is the shape that fits.

*Unofficial, not affiliated with Telegram — not endorsed by or sponsored by Telegram, nor by any channel whose public posts appear in the sample.*

### What you get

One row per post (`type: post`).

| Column | What it holds |
|---|---|
| `channel` · `channelTitle` | the channel's name and its display title |
| `postId` · `url` | the post's number in the channel and its permanent link |
| `date` | the publish time, ISO 8601 in UTC |
| `views` | the view count as a number (Telegram's "1.59M" read as 1590000) |
| `text` | the post's text, line breaks kept |
| `links[]` | every link in the text, and the link preview's address |
| `mediaKind` · `mediaCount` · `mediaDurationSeconds` | `video`, `voice`, `round_video`, `photo`, `document`, `poll`, `sticker` — and how long the clips run |
| `forwardedFrom` | the channel a post was forwarded from (its name and link when Telegram shows them) |
| `replyTo` | the post it replies to, with its link |
| `edited` · `authorSignature` | whether Telegram marks the post as edited, and the author's signature when the channel signs posts |
| `charged` · `chargedEvents` · `status` · `statusReason` | on every row, so the invoice reconciles from the dataset |

With **Transcribe voice and video** on, one more row per clip (`type: transcript`): `postId`, `url`, `mediaKind`, `durationSeconds`, `transcript`, `language` and timed `segments`.

### Voice notes and short videos as text

Telegram's public channel page serves voice notes and the shorter video clips; it does not serve big ones. Measured on 30 video posts across busy channels, about half the video seconds were served — the longest served clip ran about 3½ minutes, while a clip Telegram shows as **"Media is too big"** (that can happen even at 30 seconds) cannot be fetched by anyone without the app. So this actor transcribes what Telegram serves, and a withheld clip comes back as a transcript row saying so — never charged. A clip that is only music or silence is never charged either. Voice notes are rare on public channels; most clips are videos and round videos.

### Only new posts, on a schedule

**Only new posts since my last run** is on by default. Each channel is read newest first, down to the newest post your account got from it on an earlier run, and stops there — so an hourly run delivers, and charges, only what was posted in that hour. The memory lives in your own Apify account (a key-value store named `telegram-channel-scraper-memory`). If a run stops early (your maximum charge, the run clock), the posts it delivered are remembered too, and the next run collects the older new posts below them without charging anything twice. Turn the setting off to read the newest posts again whatever you have.

A read that finds nothing new is billed one $0.002 channel check, because the page was still read for you. A read that delivers even one post never pays it.

### What it costs

| Event | Apify free plan | Bronze | Silver | Gold and above |
|---|---|---|---|---|
| Post | $0.0008 | $0.0007 | $0.0006 | $0.0005 |
| Transcript minute (opt-in, per started minute of audio) | $0.007 | $0.007 | $0.007 | $0.007 |
| Channel check (a read with nothing new) | $0.002 | $0.002 | $0.002 | $0.002 |

No start fee. Never charged: a private, missing or preview-off channel, a link that is not Telegram, a page Telegram did not answer for, a clip Telegram does not serve, a clip with no speech, posts your account already has, posts cut by your own limits or the run clock, and the sample a bare Start returns. Platform usage is included in these prices.

### Input

| Field | What it does |
|---|---|
| `channels` | public channel links or names, one per line (`https://t.me/telegram`, `t.me/s/telegram`, `@telegram`, `telegram`; a post link is read as its channel) |
| `maxPosts` | newest posts to read from each channel — 20 by default, up to 2,000 |
| `since` | only posts on or after a date (`2026-10-01`) or a span back from now (`7 days`); a value that is not a date is refused on an uncharged row, never guessed |
| `onlyNewPosts` | only posts newer than your last run — on by default |
| `transcribe` | transcripts of voice notes, round videos and video clips — off by default |
| `maxRunSeconds` | stop collecting after this many seconds; what was collected is still delivered |

### As an HTTP endpoint

The same actor answers HTTP requests (Apify Standby): `GET https://steadyfetch--telegram-channel-scraper.apify.actor?channel=telegram&maxPosts=20` with your Apify token returns the rows as JSON in the same response, charged exactly as a run. One channel per request, up to 100 posts (20 with transcripts). The endpoint keeps no account memory, so "only new posts" is a run feature; Apify free plan callers get posts without transcripts.

### When something goes wrong

Every channel that delivers less than you asked ends on one uncharged row naming why: the channel has no more posts, every older post is one you already have, the posts are older than your date, Telegram has no channel by that name, it shows no public page for it, it is a private chat, it does not display the channel, or it did not answer (please re-run). A time limit ends the collecting, never the delivering: posts already collected are always delivered. This actor may fail when Telegram changes its public pages — failed items are never charged. Posts this actor's safety screen withholds come back as an address-only row, uncharged.

### Questions

Open an issue on the Issues tab — we usually reply within a couple of hours.

If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.

# Changelog

This Actor's version history is a separate document: https://apify.com/steadyfetch/telegram-channel-scraper/changelog.md

# Actor input Schema

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

Public Telegram channels, one per line: https://t.me/telegram, https://t.me/s/telegram, @telegram or just telegram (a post link is read as its channel). A private invite link (t.me/+…, joinchat), a name with no public preview, or a name that does not exist comes back as one uncharged row saying which.

## `maxPosts` (type: `integer`):

How many of the newest posts to read from each channel (newest first). 20 if you leave it out; up to 2,000.

## `since` (type: `string`):

Leave empty for no date limit. A date (2026-10-01, or a full time like 2026-10-01T12:00:00Z) or a span back from now (7 days, 12 hours, 2 weeks). A value that is not a date is refused on an uncharged row, never guessed.

## `onlyNewPosts` (type: `boolean`):

On by default: a channel is read down to the newest post your account got from it on an earlier run, so a scheduled run delivers — and charges — only what is new. A read that finds nothing new pays one $0.002 channel check. Turn it off to read the newest posts again whatever you already have.

## `transcribe` (type: `boolean`):

Off by default. On: each voice note, round video and video clip Telegram serves on the channel's public page is transcribed into its own row (text, language, timed segments), charged $0.007 per started minute of its audio. Telegram's public page serves the shorter clips only — a clip it shows as "Media is too big" comes back as an uncharged row saying so — and a clip with no speech is never charged.

## `maxRunSeconds` (type: `integer`):

Stop collecting after this many seconds (the posts already collected are still delivered). Leave empty to use the run timeout.

## Actor input object example

```json
{
  "channels": [
    "https://t.me/telegram"
  ],
  "maxPosts": 20,
  "onlyNewPosts": true,
  "transcribe": false
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per post (`type: post`): the channel, post id and link, the publish time, views as a number, the text, the links in it, the media kind and length, who it was forwarded from, the post it replies to and an edited flag — charged once as a `post`. With "Transcribe voice and video" on, one row per voice note, round video or video clip Telegram serves (`type: transcript`): the text, language and timed segments, charged as `transcript-minute` per started minute of its audio. A channel read that had nothing new to deliver is billed one `check` on its own row. Every row carries `charged`, `chargedEvents` and `status`, so the invoice reconciles from the dataset itself.

## `misses` (type: `string`):

One uncharged row for each channel that delivered less than you asked, saying why: `no_data` (the channel has no more posts), `skipped.all_repeat` (every older post is one your account already has), `skipped_date_window` (older than your date), `channel_not_found`, `page_private` (no public web page for the name), `private_or_members_only` (a private invite link), `removed_or_unavailable` (Telegram does not display the channel), `source_unavailable` (Telegram did not answer — please re-run), `skipped_budget` / `skipped_deadline` (your maximum charge or the run clock), `skipped.content_screen` (a post this actor's safety screen withheld) and `input_error`. A transcript row that is not delivered says why with its own status (`transcript_too_long.withheld` — Telegram shows the clip as "Media is too big" — `transcript_no_speech`, `transcript_unavailable` …) and is never charged.

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

The run's receipt: posts and transcripts delivered, what was asked for, the charged events and what stopped the run. Uncharged.

# 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": [
        "https://t.me/telegram"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadyfetch/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": ["https://t.me/telegram"] }

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

```

## MCP server setup

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