Telegram channel scraper API: posts, views and subscriber counts as JSON

Telegram channel scraper API with no login: subscriber counts, the latest 100 posts with views and media, or one message by link, from 1 credit a call.

By Scrape Everything team · · 11 min read

Last updated: 2026-10-21

TL;DR: A Telegram channel scraper API reads a public channel's card, its latest messages and single posts over HTTP, with no Telegram account, api_id or bot token. With Scrape Everything, POST /v1/telegram/posts returns up to 100 recent messages with text, media and approximate view counts for 2 credits, and /v1/telegram/profile returns the exact subscriber count for 1 credit.

A Telegram channel scraper API is a web service that reads the public t.me page of a channel and returns structured JSON instead of HTML. Telegram already publishes a web preview for most public channels at t.me/s/<channel>. We fetch it, parse it and normalize it, so you get subscriber_count, view_count and posted_at instead of a pile of divs. The Telegram scraper page lists all three endpoints.

Official Telegram API vs public channel preview: what does each need?

Telegram gives you two official doors, and both ask for more setup than most monitoring jobs deserve.

The Telegram API (MTProto) is the full client protocol. You register an app at my.telegram.org to get an api_id and api_hash, then log in as a real user with a phone number and keep that session alive. Telethon and Pyrogram wrap it. It's powerful, and it's also your personal account doing the scraping.

The Telegram Bot API is simpler: get a token from @BotFather and call HTTPS methods. The catch is membership. Per the Telegram Bots FAQ, a bot receives messages from channels where it is a member, and only the channel's owner or admins can add it. You can't point a bot at someone else's channel.

The public preview needs nothing. To check a channel, open t.me/s/<name> in a browser. If you land on the feed, it's on. If you get bounced to t.me/<name>, the owner turned it off.

RouteWhat you needReads other people's public channelsPrivate chatsOutput
Telegram API (MTProto)api_id, api_hash, logged-in user sessionYes, as your userYes, if your account is a memberMTProto objects via a client library
Telegram Bot APIBot token from @BotFatherOnly channels the bot was added toOnly chats the bot is inJSON updates
Public t.me preview (Scrape Everything)An API key, no Telegram accountYes, if the web preview is onNoJSON, same envelope as our other 8 platforms

My take: if you need private groups or full history, use MTProto and accept the account management. If the question is "what did these 20 public channels post today, and how many people saw it", a session file sitting on a server is more liability than it's worth.

How do I get a Telegram subscriber count from the API?

Use telegram.profile. It costs 1 credit and takes one parameter, username, which accepts a bare username, an @handle or a full t.me link.

bash
curl -sS https://api.scrapeeverything.com/v1/telegram/profile \
  -H "Authorization: Bearer $SCRAPEEVERYTHING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "telegram"}'

The request hits Telegram's official channel, so you can run it as is. The sample below uses placeholder values, since real counters go stale the day you publish them. The field names are exact.

json
{
  "success": true,
  "data": {
    "username": "example_channel",
    "name": "Example Channel",
    "type": "channel",
    "is_verified": false,
    "description": "Daily updates from the example team.",
    "photo_url": "https://cdn.example/photo.jpg",
    "subscriber_count": 48213,
    "member_count": null,
    "online_count": null,
    "monthly_users": null,
    "link": "https://t.me/example_channel",
    "preview_url": "https://t.me/s/example_channel"
  },
  "meta": { "cached": false, "credits_charged": 1, "credits_remaining": 99 }
}
  • The same endpoint works for groups, bots and users. type is inferred from the counters on the card. It's usually right, but if you're filtering on it, check which counter is filled before you trust the label.
  • Channels fill subscriber_count. Groups fill member_count and online_count. Bots and mini apps fill monthly_users.
  • Here subscriber_count is exact. The posts endpoint reads a compact "11M"-style counter, so the two won't always match.
  • preview_url is only set for channels. If it's null, the posts endpoint has nothing to read.

For a daily growth check, this is the call. 100 channels once a day is 3,000 credits a month, which works out to about $4.68 at the Builder rate.

How do I scrape Telegram channel posts with views and media?

Use telegram.posts. It returns the channel header plus the latest messages, newest first, with text, media URLs, views, dates and forward info. It costs 2 credits per call, whatever limit you set.

ParameterTypeRequiredDefaultRange
usernamestringyesusername, @handle or t.me link
limitintegerno201 to 100

The price is flat, so ask for 100 unless you have a reason not to. Under the hood, the preview serves roughly 20 messages per page, and we walk back with ?before=<oldest id> until we hit your limit. A 100-message call is about five page fetches in a row, which is why it costs 2 credits and why it's slower than a profile call.

python
import os
import requests

resp = requests.post(
    "https://api.scrapeeverything.com/v1/telegram/posts",
    headers={"Authorization": f"Bearer {os.environ['SCRAPEEVERYTHING_API_KEY']}"},
    json={"username": "telegram", "limit": 100},
    timeout=60,
)
resp.raise_for_status()
data = resp.json()["data"]

print(data["name"], data["subscriber_count_text"], "subscribers")
for p in data["posts"][:5]:
    print(p["posted_at"], p["type"], p["view_count_text"], p["link"])

Trimmed data object, placeholder values (the success and meta envelope is the same as above):

json
{
  "username": "example_channel",
  "name": "Example Channel",
  "is_verified": false,
  "subscriber_count": 48000,
  "subscriber_count_text": "48K",
  "photo_count": 1200,
  "video_count": 310,
  "link": "https://t.me/example_channel",
  "posts_fetched": 100,
  "has_more": true,
  "posts": [
    {
      "id": 1042,
      "post": "example_channel/1042",
      "link": "https://t.me/example_channel/1042",
      "type": "photo",
      "text": "Release notes for v2.3 are out.",
      "photo_urls": ["https://cdn.example/1042.jpg"],
      "video_url": null,
      "view_count": 12400,
      "view_count_text": "12.4K",
      "posted_at": "2026-10-20T09:15:00+00:00",
      "is_forwarded": false,
      "forwarded_from": null,
      "link_preview": null
    }
  ]
}

type is one of text, photo, album, video, poll, document, voice, round_video or sticker. Videos also carry video_url, video_thumb_url and a video_duration string like 0:32.

The fields I'd build on: id, which only goes up within a channel, so it's your "have I seen this?" key (deleted posts leave gaps, which doesn't break that). is_forwarded and forwarded_from, which give you a repost graph of who copies whom. And has_more, which says older messages exist beyond what came back.

A common mistake is passing a bot like BotFather to the posts endpoint. Bots have a profile card but no preview feed, t.me redirects, and you get this:

json
{
  "success": false,
  "message": "@BotFather is not a public channel with web preview",
  "code": "NOT_FOUND",
  "request_id": "req_example",
  "meta": { "credits_charged": 2, "credits_remaining": 97 }
}

NOT_FOUND is charged, per the error code reference. Call telegram.profile first if you're taking usernames from user input.

Does it return reactions or comment counts?

No. The response has views, media, dates and forward info, but no reaction counts and no reply or comment counts. If you need those, MTProto is the route today.

Are Telegram view counts from the API exact?

No. Telegram's web preview shows views in compact form, and that's all there is to read. view_count is our integer parse of that text, and view_count_text keeps the original string.

What the page shows (view_count_text)What you get (view_count)
987987
12.4K12400
3.76M3760000

Small numbers come through exact. Above a thousand, the true count is near the parsed value and the last digits are simply not on the page. Fine for ranking posts or a rough reach rate. Not fine for billing an advertiser per view. The same goes for subscriber_count on the posts endpoint. For the exact subscriber number, call telegram.profile.

Use telegram.post for 1 credit. Identify the message one of three ways:

  1. url: the message link, https://t.me/durov/342
  2. post: the short form, durov/342
  3. username plus id: {"username": "durov", "id": 342}
bash
curl -sS https://api.scrapeeverything.com/v1/telegram/post \
  -H "Authorization: Bearer $SCRAPEEVERYTHING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"post": "durov/342"}'

The response has the same message fields as an item in posts[], plus a channel object with username, name, photo_url and link. It's the cheapest way to refresh the views on one post, or to enrich a t.me link someone pasted into your app. Private t.me/c/... links aren't supported.

Can I get older Telegram messages than the latest 100?

Not through this endpoint. telegram.posts stops at the latest 100 messages, and there's no before parameter for you to page further back. For a full archive, use MTProto with your own account. With Telethon that's a few lines:

python
from telethon.sync import TelegramClient

API_ID, API_HASH = 12345, "your_api_hash"  # from my.telegram.org

with TelegramClient("archive", API_ID, API_HASH) as client:
    for msg in client.iter_messages("telegram", limit=None):
        print(msg.id, msg.date, msg.views, msg.text)

You trade the API key for a phone login and a session file, but you get the whole history. A sensible split: Telethon once for the backfill, our API for daily checks.

How do I get alerts when a Telegram channel posts a keyword?

This script runs from cron. It checks a list of public channels, remembers the highest message id per channel, prints new posts and flags any that mention your keywords.

  1. Set SCRAPEEVERYTHING_API_KEY in your environment.
  2. Edit CHANNELS and KEYWORDS.
  3. Run pip install requests, then python tg_alerts.py.
  4. Schedule it, for example 0 8 * * * for once a day.
python
import json
import os
import pathlib
import requests

API = "https://api.scrapeeverything.com/v1/telegram/posts"
KEY = os.environ["SCRAPEEVERYTHING_API_KEY"]
CHANNELS = ["durov", "telegram"]
KEYWORDS = ["launch", "update", "outage"]
STATE = pathlib.Path("tg_state.json")

seen = json.loads(STATE.read_text()) if STATE.exists() else {}

for channel in CHANNELS:
    r = requests.post(
        API,
        headers={"Authorization": f"Bearer {KEY}"},
        json={"username": channel, "limit": 100},
        timeout=60,
    )
    body = r.json()
    if not body.get("success"):
        print(f"[{channel}] {r.status_code} {body.get('code')}: {body.get('message')}")
        continue

    data = body["data"]
    last_id = seen.get(channel, 0)
    new = [p for p in data["posts"] if p["id"] and p["id"] > last_id]

    print(f"\n@{channel}: {data['subscriber_count_text']} subscribers, {len(new)} new posts")
    for p in reversed(new):
        haystack = " ".join(filter(None, [
            p["text"],
            (p.get("link_preview") or {}).get("title"),
            (p.get("link_preview") or {}).get("description"),
        ])).lower()
        hits = [k for k in KEYWORDS if k.lower() in haystack]
        flag = f"  ALERT {hits}" if hits else ""
        print(f"  {p['posted_at']}  {p['view_count_text'] or '-':>6}  {p['link']}{flag}")

    if data["posts"]:
        seen[channel] = max(p["id"] or 0 for p in data["posts"])

STATE.write_text(json.dumps(seen, indent=2))

Don't send cache_days on a monitoring job. Caching is great for profile cards you show on a page (see how the response cache works), but a monitor reading yesterday's cached feed will cheerfully report that nothing happened.

Busy channels can push more than 100 messages between runs. If has_more is true and your previous max id isn't in the batch, you missed some. Check more often instead of hoping for a bigger page.

5 channels once a day is 10 credits a day, or 300 a month. The 100 free credits you get on signup cover 50 posts calls, so you can run this on your own channels for 10 days before paying anything.

How do I export Telegram channel posts to CSV?

Add this after data = body["data"] in either script:

python
import csv

fields = ["id", "posted_at", "type", "view_count", "view_count_text",
          "is_forwarded", "forwarded_from", "link", "text"]
with open("posts.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
    w.writeheader()
    w.writerows(data["posts"])

If you'd rather land posts in Google Sheets without code, the n8n node calls the same endpoints.

Can an AI agent monitor Telegram channels for me?

Yes. We publish a telegram-channel-monitoring Agent Skill in github.com/scrapeeverything/skills. It snapshots channels with telegram.posts, diffs them against the previous run and writes a digest.

bash
npx skills add scrapeeverything/skills --skill telegram-channel-monitoring

Then ask: "Give me a daily digest of @durov and alert me on 'launch'." Snapshots go in a local folder, so diffs start on the second run. The digest covers keyword alerts, subscriber change per day, new posts, posts still gaining views per hour, and which sources are being forwarded. It calls the API through our MCP server, the CLI or plain HTTP. The Agent Skills integration page has install options.

It also ships with guardrails we agree with: it monitors channels, not people, and it treats post text as untrusted, so it won't follow instructions or open links it finds in a post.

What are the limits of scraping Telegram channels this way?

Being straight about this saves you a support ticket later.

LimitWhat it means for you
Public web preview onlytelegram.posts works on public channels whose t.me/s/ page is on. If the owner turned it off, there's nothing to read.
Latest 100 messagesOne call returns at most 100 recent messages. No history beyond that.
Approximate viewsview_count is parsed from compact text like "1.2K". The raw string is in view_count_text.
No reactions or repliesNot in the response.
No private groups or chatst.me/c/... links and private groups aren't supported, and we don't log in to anything. Public groups return a card (members, online) but no messages.
BillingFailures on our side or the platform's (500, 502, 503, 504) are refunded automatically. A 404 NOT_FOUND is charged.

Request and concurrency caps per account are on the rate limits page. The skill suggests checking each channel at most hourly, which is a sensible ceiling anyway.

Scrape Everything reads public data only and isn't affiliated with Telegram. We can't give you a legal conclusion for your case. Read Telegram's Terms of Service, and if posts end up in a dataset, think about the personal data in them. Our notes on the legal side of scraping public social media data cover robots.txt, terms and personal data, and the researchers use case shows how teams structure datasets.

How much does the Telegram channel scraper API cost?

Credits come in one-time packs and never expire. Payment is by crypto.

EndpointWhat you getCredits
telegram.profileChannel, group, bot or user card with exact counters1
telegram.postsChannel header plus up to 100 latest messages2
telegram.postOne message by link1

Since 100 posts cost 2 credits, 1,000 posts cost 20 credits. At list prices:

PackPrice per 1,000 creditsCost per 1,000 Telegram posts
Builder ($39, 25,000 credits)$1.56about $0.031
Growth ($119, 100,000 credits)$1.19about $0.024
Scale ($449, 500,000 credits)$0.90$0.018

50 channels checked hourly is 2,400 credits a day, about 72,000 a month, which one Growth pack covers. Cache hits cost 0 credits. Full numbers are on the pricing page, and social media scraping API pricing compared puts ours next to other providers. On Apify, Telegram scrapers are Actors, many built by third parties, with prices that vary by Actor (see our Apify comparison). Prices on all sides may change, so check them the day you decide.

Every field and parameter is in the Telegram endpoint reference.

FAQ

Do I need a Telegram account, api_id or bot token?

No. Data comes from public t.me pages. You only need a Scrape Everything API key sent as Authorization: Bearer ssk_live_....

How many messages can I get from a Telegram channel?

Up to 100 of the latest messages per telegram.posts call, newest first. Set limit between 1 and 100. The call costs 2 credits either way.

Why is the view count rounded?

Telegram's web preview only shows compact counts such as "3.76M". We parse that into view_count and keep the original in view_count_text. For exact subscriber numbers, use telegram.profile.

Can I scrape private Telegram groups or channels?

No. Private t.me/c/... links and private groups aren't supported. For chats your own account belongs to, Telegram's official MTProto API is the right tool.

Is there an MCP server or CLI for Telegram data?

Yes. The @scrapeeverything/mcp server, listed in the official MCP Registry, exposes the Telegram endpoints as tools (for example telegram_posts), and the CLI runs with npx scrapeeverything. Both use the same credits as the REST API. Setup is on the MCP integration page, and the OpenAPI spec at https://scrapeeverything.com/openapi.json loads into your own tooling.


Pick a channel you already follow and run the posts call on it. Get 100 free credits. No payment details needed, sign in with Google or GitHub.

More from the blog

Start with 100 free credits.

Continue with Google, create an API key and send your first request in under a minute.

Get 100 free credits