You want the posts from a public Telegram channel in a spreadsheet: what was posted, when, and how many people saw it. Telegram's official API needs a phone number, an app registration and a client library, which is a lot of setup for "give me the last 500 posts as CSV". Copying posts by hand stops working after the first dozen.
The Telegram Channel Scraper actor, published by Hay Equipos on the Apify Store, reads the public web preview that Telegram itself serves at t.me/s/<channel> (the same page anyone can open in a browser) and walks back through the channel history page by page until it reaches your date or post limit. No Telegram account, no phone number, no API key.
What you get back
One row per post. A trimmed example (values are illustrative):
{
"channel": "examplechannel",
"channelTitle": "Example Channel",
"channelSubscribers": 125000,
"channelVerified": false,
"postId": 4821,
"postUrl": "https://t.me/examplechannel/4821",
"date": "2026-09-14T08:15:00.000Z",
"edited": false,
"text": "Weekly market recap is out. #markets #recap",
"views": 48300,
"reactions": [{ "emoji": "👍", "count": 310 }, { "emoji": "🔥", "count": 95 }],
"reactionsTotal": 405,
"mediaTypes": ["photo"],
"photoUrls": ["https://cdn4.telesco.pe/file/..."],
"videoCount": 0,
"documentNames": [],
"poll": null,
"links": ["https://example.com/recap"],
"hashtags": ["#markets", "#recap"],
"linkPreview": null,
"forwardedFrom": null,
"isServiceMessage": false,
"scrapedAt": "2026-09-27T06:09:41.563Z"
}
Each row also carries videoThumbnailUrl and forwardedFromUrl. Polls come with their question and option percentages, and link previews come with site name, title and description. A RUN_SUMMARY record in the run's key value store lists every channel with its status, title, subscriber count, description, and how many posts were read and saved.
In table form, the columns most people keep:
| postUrl | date | views | reactionsTotal | text |
|---|---|---|---|---|
| https://t.me/examplechannel/4821 | 2026-09-14 |
48300 | 405 | Weekly market recap is out... |
| https://t.me/examplechannel/4820 | 2026-09-13 |
51200 | 388 | New listing announced... |
Step by step in the Apify Console
- Open the actor on the Apify Store (link at the end) and click Try for free. You need a free Apify account.
- In Public channels, add one channel per line. A username (
telegram),@username, or a link (https://t.me/durovorhttps://t.me/s/durov) all work. - Set Maximum posts per channel (default 100, newest posts first) and, if you like, Maximum posts in total (default 10,000).
- To limit the date range, fill Posted on or after and Posted before, for example
2026-09-01. Reading stops once the actor reaches posts older than your start date. - To keep only posts about a topic, add words to Text contains any of. Matching ignores case.
- Leave Include service messages off unless you want notices like a changed channel photo or a pinned message.
- Click Start. When the run finishes, open the Output tab and export as CSV, Excel, JSON or HTML.
Example input:
{
"channels": ["telegram", "https://t.me/durov"],
"maxPostsPerChannel": 200,
"postedAfter": "2026-06-01",
"keywords": []
}
Calling it from code
The actor id is pistachio_implementation/telegram-channel-posts. Keep your token in the APIFY_TOKEN environment variable.
With curl, this runs the actor and returns the dataset items in one call:
curl -X POST \
"https://api.apify.com/v2/acts/pistachio_implementation~telegram-channel-posts/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"channels": ["telegram"], "maxPostsPerChannel": 50}'
With Python and the apify-client package (pip install apify-client):
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("pistachio_implementation/telegram-channel-posts").call(
run_input={
"channels": ["telegram", "durov"],
"maxPostsPerChannel": 100,
"postedAfter": "2026-09-01",
"keywords": ["update"],
}
)
for post in client.dataset(run["defaultDatasetId"]).iterate_items():
print(post["postUrl"], post["date"], post["views"])
To monitor a channel, schedule the actor daily with postedAfter set to yesterday, or store the highest postId from the last run and compare.
Pricing
Pay per event, with no subscription and no platform usage charges on top:
- $0.30 per 1,000 posts saved ($0.0003 per post).
- Posts removed by your date or keyword filters, and channels that cannot be read, cost nothing.
Limits and what it does not do
-
Public channels only. Private channels, groups, chats and user accounts have no web preview. They are reported as "no public preview" in
RUN_SUMMARYinstead of failing the whole run. Some public channels also turn their preview off. - Views and reactions are the rounded numbers Telegram shows. "1.48M" becomes 1480000, so treat large counts as approximate.
- Custom emoji reactions come back as
custom:<id>, because Telegram's page shows them as images. - Media links point to Telegram's CDN and expire after a while. Download the files soon if you need them.
- The actor waits 1.5 seconds between pages to stay polite, which works out to roughly 800 posts a minute.
- It does not collect post signatures (the admin who posted), member lists, phone numbers or user profiles. It reads only what a public channel shows to any visitor.
- It depends on the markup of Telegram's preview page. If Telegram changes that page, the parser needs an update.
Please use the posts you collect in line with Telegram's terms and the rights of the people who wrote them.
Try it on the Apify Store: https://apify.com/pistachio_implementation/telegram-channel-posts
Top comments (0)