# Telegram Channel Posts Scraper (`xtracto/telegram-channel-posts-scraper`) Actor

Scrape latest posts from any public Telegram channel — text, photos, videos, views, reactions, forwards, replies. No login, no API key, no Bot API.

- **URL**: https://apify.com/xtracto/telegram-channel-posts-scraper.md
- **Developed by:** [Farhan Febrian Nauval](https://apify.com/xtracto) (community)
- **Categories:** Social media, Other
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 Posts Scraper

Bulk-extract recent posts from any public Telegram channel — text, media URLs, views, reactions, author, forwarded-from, and reply context — in clean structured JSON. No Bot API token, no login, no phone number.

### Why use this actor

- **No Bot API / no login required** — works against the public `t.me` web preview; no `BOT_TOKEN`, no phone number, no TDLib build to maintain.
- **Rich detail per post** — message text, media URLs (photos & videos), view counts, reactions, author label, forwarded-from channel, and reply-to context all in one flat record.
- **Bulk + paginated** — pass a list of channels in one run; the actor paginates back through each channel's history up to your `maxPosts` cap.
- **Per-channel isolation** — one bad channel won't fail the run. Missing or restricted channels surface as `_error: "not_found"` so you can triage failures.
- **Stable JSON output** suitable for pipelines, spreadsheets, and databases — every row carries `_input`, `_source`, `_scrapedAt` envelope fields so you can join results back to your input list.
- **Captures CDN media URLs** — direct links to photos and videos so you can mirror or analyze attachments downstream.

### How it works

1. You provide a list of public Telegram channels (e.g. `durov`, `@telegram`, or `https://t.me/durov`).
2. The actor opens each channel's public web preview and walks back through the post history, reading the same data the `t.me/s/<channel>` page shows.
3. Each post is normalized into a flat JSON record and streamed into your dataset, ready to download as JSON, CSV, or Excel.

You do not need to manage scrapers, browsers, or rotating IPs — all handled internally.

### Input

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "maxPosts": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["DATACENTER"]
  }
}
```

| Field | Type | Description |
|---|---|---|
| `channels` | array | List of public Telegram channels to scrape. Accepts bare usernames (`durov`), `@username`, or full `https://t.me/<username>` URLs. |
| `maxPosts` | integer | Maximum posts per channel. Default `100`. Set to `0` for unlimited (capped by what the public preview exposes). |
| `proxyConfiguration` | object | Apify Proxy settings. Datacenter proxy works for most public channels. |

### Output

Input: `durov`, `maxPosts: 2`

```json
{
  "id": 497,
  "channel": "durov",
  "url": "https://t.me/durov/497",
  "text": "France's \"Agency for Secure Documents\" was hacked. Names, addresses, emails, and phone numbers of\n19 million people\n— leaked from a single government database.\nFuture leaks will be even more catastrophic if the French government gets what it wants:\naccess to encrypted chats\nand\nDigital IDs\nof social media users.",
  "datetime": "2026-04-20T22:55:10+00:00",
  "views": "4.54M",
  "author": "Pavel Durov",
  "forwardedFrom": null,
  "replyTo": null,
  "photos": [],
  "videos": [],
  "links": [],
  "reactions": [],
  "_input": "durov",
  "_source": "S1-html",
  "_scrapedAt": "2026-05-18T10:39:43.899042+00:00"
}
```

```json
{
  "id": 498,
  "channel": "durov",
  "url": "https://t.me/durov/498",
  "text": "The apartment in Russia where I lived 20 years ago received a summons for “Suspect P. V. Durov”.\nThey must be suspecting me of defending Articles 29 and 23 of the Russian Constitution — which guarantee freedom of speech and the right to private correspondence.\nProud to be guilty!\n😏",
  "datetime": "2026-04-22T17:06:37+00:00",
  "views": "5.19M",
  "author": "Pavel Durov",
  "forwardedFrom": null,
  "replyTo": null,
  "photos": [
    "https://cdn4.telesco.pe/file/CVRdtGiETwFzCQ5efOdFbAKVLiQAxSv8bpnypVOdE6Bn-zenXs0NtKC3UVwF6LMr...jpg"
  ],
  "videos": [],
  "links": [],
  "reactions": [],
  "_input": "durov",
  "_source": "S1-html",
  "_scrapedAt": "2026-05-18T10:39:43.899042+00:00"
}
```

| Field | Type | Description |
|---|---|---|
| `_input` | string | The channel value exactly as you supplied it. Use this to join results back to your input list. |
| `_source` | string | Internal tag for the path used to fetch the record. `S1-html` means the post was parsed from the channel's public web preview. |
| `_scrapedAt` | string | ISO-8601 UTC timestamp when the record was scraped. |
| `id` | integer | Telegram's per-channel message ID. Stable and monotonic per channel. |
| `channel` | string | Normalized channel username (without `@` or URL prefix). |
| `url` | string | Canonical message URL (`https://t.me/<channel>/<id>`). |
| `text` | string | Message body. May contain newlines and emoji. `null` for media-only posts with no caption. |
| `datetime` | string | ISO-8601 UTC timestamp the message was posted. |
| `views` | string | View count as shown by Telegram (e.g. `"2.89M"`, `"153K"`, `"412"`). String because Telegram returns it pre-formatted. |
| `author` | string | Signed author label when the channel signs posts (e.g. `"Pavel Durov"`). `null` for unsigned channels. |
| `forwardedFrom` | string | Source channel name if this post is a forward. `null` for original posts. |
| `replyTo` | object | Reference to the message this post replies to (`{ "url": "...", "text": "..." }`). `null` if not a reply. |
| `photos` | array | Direct CDN URLs for photo attachments. URLs are time-bound — mirror them quickly if you need permanence. |
| `videos` | array | Direct CDN URLs for video attachments. Same caveats as `photos`. |
| `links` | array | External URLs found in the message body. |
| `reactions` | array | List of reactions on the post (`[{ "emoji": "👍", "count": 123 }, ...]`). Empty array if reactions are off or none recorded. |

#### Error envelope

Channels that don't exist, are private, or fail to fetch return a structured error instead of crashing the run:

```json
{
  "_input": "this-channel-does-not-exist-xyz",
  "_source": "S1-html",
  "_scrapedAt": "2026-05-18T10:24:10.012345+00:00",
  "_error": "not_found",
  "_errorDetail": "channel page returned 404"
}
```

Filter on `_error` to triage failed rows.

### Pricing

This actor is billed per result: **$2.50 per 1,000 results**. Each post = 1 result. Errors (not-found, restricted) are not billed.

### Other Sosmed Actors

| Platform | Actor | Best for |
|---|---|---|
| Telegram | [Telegram Post Detail Scraper](https://apify.com/xtracto/telegram-post-detail-scraper) | Full single-post detail by URL |
| Bluesky | [Bluesky Account Posts Scraper](https://apify.com/xtracto/bluesky-account-posts-scraper) | Recent posts from any Bluesky handle |
| Mastodon | [Mastodon Account Scraper](https://apify.com/xtracto/mastodon-account-scraper) | Toots + profile data from any instance |
| Substack | [Substack Publication Posts Scraper](https://apify.com/xtracto/substack-publication-posts-scraper) | Newsletter posts from any publication |
| YouTube | [YouTube Channel Scraper](https://apify.com/xtracto/youtube-channel-scraper) | Uploaded videos for a channel |
| Reddit | [Reddit Subreddit Posts Scraper](https://apify.com/xtracto/reddit-subreddit-posts-scraper) | Latest / top posts from any subreddit |
| X (Twitter) | [X Account Tweets Scraper](https://apify.com/xtracto/x-account-tweets-scraper) | Recent tweets from any handle |

Browse the full catalog at [apify.com/xtracto](https://apify.com/xtracto).

### Notes

- Telegram's public web preview shows only channels marked **public**. Private (invite-link-only) channels are not accessible via this actor.
- Some channels restrict the public preview (the page shows *"If you have Telegram, you can view this channel"*) — those return an `_error` envelope.
- The web preview caps at roughly the latest ~100 messages per pagination window; very large channels may need scheduled incremental runs to backfill history.
- View counts are updated lazily; for old messages they reflect the count at the time of scraping, not real-time.
- Photo and video URLs are CDN-signed and time-bound — mirror them downstream if you need permanence.
- For large jobs (>20 channels), enable the Apify Proxy to avoid per-IP rate limits.

# Actor input Schema

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

List of public Telegram channels to scrape. Accepts bare usernames (e.g. `durov`), `@username`, or full `https://t.me/<username>` URLs.

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

Maximum number of posts to return per channel. Set to 0 for unlimited (will fetch as far back as the public web preview allows).

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

Apify Proxy settings. Datacenter proxy works for most public channels.

## Actor input object example

```json
{
  "channels": [
    "durov",
    "telegram"
  ],
  "maxPosts": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "DATACENTER"
    ]
  }
}
```

# Actor output Schema

## `id` (type: `string`):

Unique identifier of the item at the source.

## `channel` (type: `string`):

Channel as reported by the source.

## `url` (type: `string`):

Direct link to the scraped item.

## `text` (type: `string`):

Text content of the item.

## `datetime` (type: `string`):

Datetime as reported by the source.

## `views` (type: `string`):

Views.

## `author` (type: `string`):

Author name.

## `photos` (type: `string`):

Photos.

## `videos` (type: `string`):

Videos.

## `_input` (type: `string`):

The input value this row was produced from.

## `_scrapedAt` (type: `string`):

UTC timestamp of the scrape, ISO 8601.

## `forwardedFrom` (type: `string`):

Forwarded From as reported by the source.

## `replyTo` (type: `string`):

Reply To as reported by the source.

## `links` (type: `string`):

Links.

## `reactions` (type: `string`):

Reactions.

## `_source` (type: `string`):

Which extraction strategy produced the row.

## `_error` (type: `string`):

Set only on diagnostic rows - why that target produced no data.

## `_errorDetail` (type: `string`):

Extra context for the error.

# 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"
    ],
    "maxPosts": 100
};

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

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

```

## MCP server setup

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