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.
| Route | What you need | Reads other people's public channels | Private chats | Output |
|---|---|---|---|---|
| Telegram API (MTProto) | api_id, api_hash, logged-in user session | Yes, as your user | Yes, if your account is a member | MTProto objects via a client library |
| Telegram Bot API | Bot token from @BotFather | Only channels the bot was added to | Only chats the bot is in | JSON updates |
| Public t.me preview (Scrape Everything) | An API key, no Telegram account | Yes, if the web preview is on | No | JSON, 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.
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.
{
"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.
typeis 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 fillmember_countandonline_count. Bots and mini apps fillmonthly_users. - Here
subscriber_countis exact. The posts endpoint reads a compact "11M"-style counter, so the two won't always match. preview_urlis 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.
| Parameter | Type | Required | Default | Range |
|---|---|---|---|---|
username | string | yes | username, @handle or t.me link | |
limit | integer | no | 20 | 1 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.
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):
{
"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:
{
"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) |
|---|---|
987 | 987 |
12.4K | 12400 |
3.76M | 3760000 |
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.
How do I get a single Telegram message by link?
Use telegram.post for 1 credit. Identify the message one of three ways:
url: the message link,https://t.me/durov/342post: the short form,durov/342usernameplusid:{"username": "durov", "id": 342}
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:
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.
- Set
SCRAPEEVERYTHING_API_KEYin your environment. - Edit
CHANNELSandKEYWORDS. - Run
pip install requests, thenpython tg_alerts.py. - Schedule it, for example
0 8 * * *for once a day.
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:
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.
npx skills add scrapeeverything/skills --skill telegram-channel-monitoringThen 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.
| Limit | What it means for you |
|---|---|
| Public web preview only | telegram.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 messages | One call returns at most 100 recent messages. No history beyond that. |
| Approximate views | view_count is parsed from compact text like "1.2K". The raw string is in view_count_text. |
| No reactions or replies | Not in the response. |
| No private groups or chats | t.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. |
| Billing | Failures 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.
Is scraping Telegram channels legal?
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.
| Endpoint | What you get | Credits |
|---|---|---|
telegram.profile | Channel, group, bot or user card with exact counters | 1 |
telegram.posts | Channel header plus up to 100 latest messages | 2 |
telegram.post | One message by link | 1 |
Since 100 posts cost 2 credits, 1,000 posts cost 20 credits. At list prices:
| Pack | Price per 1,000 credits | Cost per 1,000 Telegram posts |
|---|---|---|
| Builder ($39, 25,000 credits) | $1.56 | about $0.031 |
| Growth ($119, 100,000 credits) | $1.19 | about $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.