Telegram channels are where a lot of news, crypto, product and community announcements land first. If you want to archive a channel, track keywords across several channels, or feed posts into an LLM, the official route (MTProto via Telethon) needs an API ID, a phone number and a logged-in session. For public channels there is a simpler way.
The trick: the public web preview
Every public channel has a read-only web preview at https://t.me/s/<channel>. It is plain HTML, works without login, and shows the latest ~20 posts with dates, view counts and text.
# pip install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup
def latest_posts(channel):
html = requests.get(f"https://t.me/s/{channel}", timeout=20).text
soup = BeautifulSoup(html, "html.parser")
for msg in soup.select(".tgme_widget_message"):
text = msg.select_one(".tgme_widget_message_text")
views = msg.select_one(".tgme_widget_message_views")
yield {
"url": "https://t.me/" + msg["data-post"],
"date": msg.select_one(".tgme_widget_message_date time")["datetime"],
"views": views.text if views else None,
"text": text.get_text(" ", strip=True) if text else "",
}
for p in latest_posts("telegram"):
print(p["date"], p["views"], p["url"], p["text"][:60])
Output:
2026-08-26T19:12:24+00:00 1.87M https://t.me/telegram/459 Signed Gifts. You can now add your signature and a comment t
2026-08-26T19:12:34+00:00 2.12M https://t.me/telegram/460 For all the info from today's update and a look back at 13 y
Gotchas if you extend this yourself:
- Only ~20 posts per page. Older posts need
?before=<postId>pagination, looping until you pass your start date. - Views come as
1.87M/12.3Kstrings; convert them before sorting. - Use the
timeinside.tgme_widget_message_date— videos also contain<time>elements (durations) without adatetimeattribute, which crashes a naiveselect_one("time"). - Forwards, replies, reactions, media URLs and edited flags each live in different elements.
- Channels with the preview disabled return no messages at all.
If you don't want to maintain that
Disclosure: I built a small Apify Actor that does the pagination, date range, keyword filter and field parsing above: Telegram Channel Posts Scraper. It costs $1.50 per 1,000 posts (Apify's free monthly credit covers a few thousand) and you can call it with plain requests:
# pip install requests
import os, requests
URL = ("https://api.apify.com/v2/acts/bachiembmt~telegram-channel-posts-scraper"
"/run-sync-get-dataset-items")
run_input = {
"channels": ["durov", "https://t.me/telegram"],
"maxPostsPerChannel": 60,
"fromDate": "2026-01-01",
"keywords": ["gift", "update"],
"includeChannelInfo": False,
}
items = requests.post(URL, json=run_input, timeout=300,
headers={"Authorization": f"Bearer {os.environ['APIFY_TOKEN']}"}).json()
for post in items:
print(post.get("date"), post.get("views"), post.get("url"), (post.get("text") or "")[:60])
2026-02-10T17:44:05+00:00 1940000 https://t.me/telegram/426 Gift Crafting. Collectible gifts can now be combined to crea
2026-01-07T18:17:53+00:00 2730000 https://t.me/telegram/423 Happy New Year! Telegram released 13 major updates in 2025 –
Each post has channel, postId, url, date, text, views (as an integer), forwardedFrom, replyTo, hasMedia, mediaType, mediaUrls, links, reactions, edited. The dataset exports to CSV/Excel/JSON from the Apify console, and you can schedule the run daily to build a keyword alert.
Limits, honestly
- Public channels with the web preview enabled only. No private channels, groups or comments.
- It reads the same public HTML as the snippet above, so if Telegram changes the markup both break until fixed.
- Respect each channel's content rights when you republish anything.
If the DIY snippet is enough for you, use it — it is genuinely 20 lines. The Actor is for when you need many channels, history and a schedule without babysitting a parser.
Top comments (0)