# Telegram Channel Scraper (`devilscrapes/telegram-channel-scraper`) Actor

Export post history from any public Telegram channel via its no-login web preview. We rotate browser fingerprints, retry on rate limits, and walk the pagination cursor so deep channel history lands clean — text, media links, view counts, and forwards. Pay only for posts that land.

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

## Pricing

from $2.00 / 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.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

<img src=".actor/icon.svg" width="160" alt="Telegram Channel Scraper" />

## Telegram Channel Scraper

*We do the dirty work so your dataset stays clean.* 😈

**$2.00 / 1,000 posts** — pay only for results that land. No credit card required to try.

Export post history from any public Telegram channel via its no-login web preview (`t.me/s/{channel}`) — no Telegram API key, no bot token, no MTProto session to babysit. We rotate browser fingerprints, retry on rate limits, and walk the pagination cursor backward through a channel's history so you get clean, structured rows: text, media links, view counts, and forwards.

### 🎯 What this scrapes

- Public Telegram channel post history via the server-rendered `/s/{channel}` preview surface.
- Channel-level metadata (title, subscriber count, description) captured once per channel.
- Post text, timestamps, view counts, media (photo/video/document), outbound link previews, and forward attribution.
- Every field lands typed and validated — no raw HTML fragments, no guessing at date formats.

### 🔥 Features

- **What we handle for you**: browser fingerprint rotation, exponential back-off on rate limits, pagination cursor walking, and Pydantic-validated rows.
- Works on any public channel — no login, no bot token, no MTProto setup.
- Detects private/nonexistent channels and skips them with a clear warning instead of failing the whole run.
- **Pay only for results that land** — no data means no `result` charges (only the small `actor-start` warm-up fee).
- Clean, typed dataset rows — Pydantic-validated on the way out, so downstream tools never choke on a malformed field.

### 💡 Use cases

- **OSINT analysts** tracking a watchlist of political/activist channels for narrative shifts, without opening a Telegram account.
- **Crypto researchers** archiving trading-signal channels to backtest signal accuracy after the fact.
- **News teams** backfilling a monitoring dashboard with a channel's full history on day one, instead of only capturing posts going forward.
- **Compliance / trust-and-safety teams** pulling a channel's public post history as evidence before it goes dark or gets banned.

### ⚙️ How to use it

1. Open the Actor input form.
2. Add one or more channel usernames to **Channel usernames** (with or without `@`/`t.me/` prefix).
3. Adjust **Max posts per channel** (default 100, maximum 5000).
4. Optionally set **Since date** to stop paginating once posts get older than a given date.
5. Click **Start** and watch the run log. Results stream into the default dataset in real time.

### 📥 Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `usernames` | array of string | yes | — | Public channel usernames, with or without `@`/`t.me/` prefix |
| `maxPostsPerChannel` | integer | no | `100` | Cap on posts fetched per channel (1–5000) |
| `sinceDate` | string | null | no | `null` | ISO-8601 date/datetime; stop paginating once older posts are reached |
| `proxyConfiguration` | object | no | `{"useApifyProxy": true}` | Apify Proxy configuration |

```json
{
  "usernames": ["durov"],
  "maxPostsPerChannel": 100,
  "sinceDate": null,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

### 📤 Output

One row per post (`row_type: "post"`), plus one summary row per channel (`row_type: "channel_meta"`), sharing a single dataset.

```json
{
  "row_type": "post",
  "channel": "durov",
  "post_id": 522,
  "datetime": "2026-05-20T09:12:03+00:00",
  "text": "New update for Telegram...",
  "view_count": 4510000,
  "media_type": "photo",
  "media_url": "https://cdn4.telesco.pe/file/....jpg",
  "outbound_links": ["https://telegram.org/blog/watch-apps-and-more"],
  "forwarded_from": null,
  "reply_count": null,
  "permalink": "https://t.me/durov/522"
}
```

### 🔌 Integrate it

Call it from any Python pipeline via `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("DevilScrapes/telegram-channel-scraper").call(
    run_input={"usernames": ["durov"], "maxPostsPerChannel": 500}
)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

No Python shop? Wire the same call into n8n or Make via their built-in Apify node — point it at `DevilScrapes/telegram-channel-scraper` with the same input JSON and the run drops straight into your workflow.

### 💰 Pricing

Pay-Per-Event (PPE) — you pay only for what lands in your dataset:

| Event | Price (USD) | When |
|---|---|---|
| `actor-start` | $0.01 | Once per run, at boot |
| `result` | $0.002 | Per post row written to the dataset (channel-meta rows are never charged) |

**Example**: scraping 1,000 posts across a handful of channels costs $0.01 (start) + 1,000 × $0.002 (results) = approximately $2.01 total.

### 🚧 Limitations

- Private / invite-only channels have no public preview and are out of reach by design.
- Comment threads / linked discussion groups are not scraped.
- Reply/comment counts are not exposed on the public preview surface and stay `null`.

### ❓ FAQ

**Do I need a Telegram account to use this Actor?**

No. The `/s/{channel}` preview is Telegram's own no-login, search-engine-facing surface. You run the Actor; we handle the rest.

**Can I scrape private or invite-only channels?**

No, the `/s/{channel}` preview only exists for public channels; private channels have no public preview to fetch, by design.

**How far back can I go in a channel's history?**

As far as the channel's public history goes, up to the **Max posts per channel** cap (up to 5000) or the **Since date** cutoff, whichever comes first.

**Will I get charged for channels that don't exist or are private?**

No. The Actor detects empty/nonexistent previews and skips them with a run-log warning — only the one-time `actor-start` fee plus `result` charges for post rows that actually land apply.

**Can I get comment/reply counts on posts?**

Not from this surface; `reply_count` is always `null` (see Limitations).

### 💬 Your feedback

Found a bug, hit a rate limit, or need a new field on the output row? Open an issue on the Actor's Apify Store page or contact the Devil Scrapes team at [apify.com/DevilScrapes](https://apify.com/DevilScrapes). We ship updates within days of validated reports.

# Actor input Schema

## `usernames` (type: `array`):

Public Telegram channel usernames, with or without an <code>@</code> or <code>t.me/</code> prefix (e.g. <code>durov</code>, <code>@durov</code>, <code>https://t.me/durov</code>). Normalized, deduped, and fetched one channel at a time.

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

Cap on posts fetched per channel. The Actor walks backward in time via the <code>?before=</code> pagination cursor until this cap, the cursor, or <strong>Since date</strong> (if set) is reached.

## `sinceDate` (type: `string`):

Optional ISO-8601 date or datetime (e.g. <code>2026-01-01</code>). The Actor stops paginating once it reaches a post older than this date. Leave empty to walk back as far as <strong>Max posts per channel</strong> allows.

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

Apify Proxy configuration. We route requests through rotating proxy sessions and swap exit IPs on any block, so the default datacenter pool covers this target — flip to residential here any time you want an extra layer of session isolation.

## Actor input object example

```json
{
  "usernames": [
    "durov"
  ],
  "maxPostsPerChannel": 100,
  "sinceDate": null,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# 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 = {
    "usernames": [
        "durov"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/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 = {
    "usernames": ["durov"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/telegram-channel-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "usernames": [
    "durov"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call devilscrapes/telegram-channel-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=devilscrapes/telegram-channel-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/NyDiQSa8HMT49UWd8/builds/pGvD23ZbZqBIGNWeA/openapi.json
