DEV Community

ApiVault Labs
ApiVault Labs

Posted on Originally published at apivault.mataroa.blog

How to Export Public Telegram Channel Posts to CSV Without a Telegram Login

Public Telegram channels can be useful sources for editorial research, industry updates and link discovery. Reading them one at a time is manageable for a few posts. Comparing several channels over a period is easier with a dataset containing the post text, timestamp and original link.

Here is a practical workflow for collecting a small public-channel sample, exporting it to CSV, and preparing repeat snapshots. We build the Telegram Channel Scraper used in this tutorial; it runs on Apify and reads the public previews Telegram exposes at t.me/s.

Decide what you want to measure

Good starting questions include which links your own channel shared this week, which visible posts mention a product, or what topics a set of industry channels discussed. Write down the channels and period before collecting data.

This workflow collects public channel posts. It does not collect private groups, member lists, phone numbers or complete comment threads. Channels without accessible public previews may be unavailable. No Telegram account, bot token or Telegram API ID is required; an Apify account is required to run the Actor.

Collect five posts first

Open Telegram Channel Scraper, switch to JSON input, and use:

{
  "channels": ["telegram"],
  "maxPostsPerChannel": 5,
  "maxResults": 5
}
Enter fullscreen mode Exit fullscreen mode

The example targets Telegram's public channel. Replace it with a channel relevant to your research. Set a small spending limit and run it. Inspect the Post Dataset before increasing the limits.

Useful fields include channel, postId, postUrl, publishedAt, text, links, viewsLabel, viewsApprox and available reactions. Media fields contain exposed references and metadata; the Actor does not download the media files. Some references can expire. A media-only post can have no text.

Download the Dataset as CSV, Excel or JSON. Keep the original postUrl next to any derived notes so someone can check the context.

Expand the scope deliberately

For a larger snapshot, add the channel usernames you have selected and increase maxPostsPerChannel and maxResults. The first is a per-channel delivery ceiling; the second limits the entire run. Both are upper bounds, not promises of available posts.

The Actor follows older public preview pages. Its input also supports keyword and UTC date filters. A date filter may require scanning several pages to find matches, so a small result count can still involve significant collection work.

Read SUMMARY, COVERAGE, ERRORS and CHANNELS in the run's key-value store. These explain which pages were scanned, where collection stopped, and whether a channel failed. Reaching a page limit is different from reaching the end of the exposed source. Even a source-end status does not establish a complete historical archive.

When continuing older collection, COVERAGE may contain a per-channel nextBefore cursor. Use the documented continuation input in a separate single-channel run. This continues a history traversal; it is different from detecting new posts on tomorrow's run.

Make a compact research CSV

If you exported JSON as telegram-posts.json, this Python script keeps the text, source link and date together. No extra packages are needed:

import csv
import json

with open("telegram-posts.json", encoding="utf-8") as source:
    posts = json.load(source)

columns = ["channel", "postId", "publishedAt", "postUrl", "text", "viewsLabel"]
seen = set()
with open("channel-research.csv", "w", newline="", encoding="utf-8-sig") as output:
    writer = csv.DictWriter(output, fieldnames=columns)
    writer.writeheader()
    for post in posts:
        identity = (post.get("channel"), post.get("postId"))
        if None in identity or identity in seen:
            continue
        seen.add(identity)
        writer.writerow({column: post.get(column) for column in columns})
print(f"Wrote {len(seen)} unique posts")
Enter fullscreen mode Exit fullscreen mode

Import text columns as text if you open untrusted post content in a spreadsheet. Keep your raw JSON alongside the CSV if you need the original link lists, reactions or media metadata later.

Build repeat monitoring with stable identities

For a daily collection, schedule a bounded run in Apify and save the results in your own database or spreadsheet. Identify a post with the pair (channel, postId); numeric post IDs alone are not unique across channels. Compare the new snapshot with saved records before creating any alerts.

Treat the first collection as a baseline. Later runs can insert new posts and update previously observed text or metrics. A missing post in a later limited sample does not prove deletion. The Actor produces snapshots; persistent state, comparisons and notifications belong in your workflow.

Views and reactions are observations, not an audience census. Telegram's public labels can be rounded, so preserve viewsLabel and describe viewsApprox as approximate. Do not infer exact unique readers or exact engagement rates from rounded counters.

Understand the cost before increasing limits

As checked on 23 October 2026, the listed post-event fee is $0.05 per 1,000 useful delivered posts, plus Apify platform resources. For 100 delivered posts, the post-event component is $0.005. This excludes scanning and other resources; selective filters, retries and larger pages can change the final cost. Check the current Pricing tab and inspect the cost of your sample run.

Start with five public posts. Once the source and output look useful, expand the channels and date window and keep the source links attached to every research finding.

Top comments (0)