Telegram Channel Scraper — New, Edited & Deleted Posts avatar

Telegram Channel Scraper — New, Edited & Deleted Posts

Pricing

from $1.00 / 1,000 post events

Go to Apify Store
Telegram Channel Scraper — New, Edited & Deleted Posts

Telegram Channel Scraper — New, Edited & Deleted Posts

Scrape public Telegram channels without an account and get only the changes: new posts, edited posts, deleted posts. Keyless. JSON output with text, media URLs and timestamps for feeds, alerts and OSINT pipelines.

Pricing

from $1.00 / 1,000 post events

Rating

0.0

(0)

Developer

Heim AI

Heim AI

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

19 days ago

Last modified

Share

Scrape public Telegram channels and get only the changes: new posts, edited posts, and deleted posts. Keyless — reads https://t.me/s/<channel> public preview HTML. No Telegram account, no user-level PII (no members, commenters, phones).

Built for feeds, alerts, OSINT pipelines, and agent workflows that need a stable delta stream instead of re-scraping full timelines.

What it does

Each run polls the channels you list, diffs them against a persistent snapshot keyed by monitorId, and emits:

  • new — post id appeared since last poll (or all visible posts on first seen, if emitOnFirstSeen is true)
  • edited — same id, content hash changed (text / media type / media count; view ticks and CDN URL churn ignored)
  • deleted — id was inside the visible window and has been missing for two consecutive polls that could observe its position (a poll whose window does not reach that far neither confirms nor counts; a transient Telegram page glitch never produces a false deletion; the row carries last-known text). The poll row's pendingDeletionCount shows first-miss candidates awaiting confirmation.

Plus one poll summary row per successfully checked channel.

Quick start (Console)

  1. Open the Actor → Input
  2. Keep the prefill (telegram, durov) or paste your public handles
  3. Set a stable monitorId (e.g. prod-alerts)
  4. Run → first run emits baseline new posts (unless emitOnFirstSeen: false)
  5. Schedule every 15 minutes with the same monitorId to receive only real deltas

Input

FieldTypeDefaultDescription
channelsstring[]— (required)Handles: telegram, @durov, t.me/x, https://t.me/s/x. Max 50/run.
monitorIdstringdefaultSnapshot namespace. ^[A-Za-z0-9_-]{1,64}$
scanDepthinteger20Posts fetched per channel (20–100). Deletion detection covers this window only.
emitOnFirstSeenbooleantrueFirst poll: emit all visible as new, or silently baseline.

Output row types

Every row includes type, monitorId, and detectedAt (ISO).

type: "post" (charged post-event)

{
"type": "post",
"event": "new",
"monitorId": "prod-alerts",
"channel": "telegram",
"postId": 28421,
"postUrl": "https://t.me/telegram/28421",
"text": "Hello from Telegram",
"mediaType": "photo",
"mediaUrls": ["https://cdn4.telegram-cdn.org/..."],
"views": 1200000,
"viewsRaw": "1.2M",
"postedAt": "2026-07-30T12:00:00+00:00",
"firstSeen": true,
"detectedAt": "2026-08-01T05:00:00.000Z"
}

Edited (adds previousText):

{
"type": "post",
"event": "edited",
"monitorId": "prod-alerts",
"channel": "telegram",
"postId": 28421,
"postUrl": "https://t.me/telegram/28421",
"text": "Updated caption",
"previousText": "Hello from Telegram",
"mediaType": "photo",
"mediaUrls": ["https://cdn4.telegram-cdn.org/..."],
"views": 1200000,
"viewsRaw": "1.2M",
"postedAt": "2026-07-30T12:00:00+00:00",
"firstSeen": false,
"detectedAt": "2026-08-01T05:15:00.000Z"
}

Deleted (last-known content from snapshot):

{
"type": "post",
"event": "deleted",
"monitorId": "prod-alerts",
"channel": "telegram",
"postId": 28410,
"postUrl": "https://t.me/telegram/28410",
"text": "fake for delete test",
"mediaType": null,
"mediaUrls": [],
"views": null,
"viewsRaw": null,
"postedAt": "2026-07-29T09:00:00+00:00",
"firstSeen": false,
"detectedAt": "2026-08-01T05:15:00.000Z"
}

type: "poll" (charged channel-poll)

{
"type": "poll",
"monitorId": "prod-alerts",
"channel": "telegram",
"status": "ok",
"postsVisible": 20,
"windowOldestPostId": 28401,
"windowNewestPostId": 28421,
"newCount": 1,
"editedCount": 0,
"deletedCount": 0,
"pendingDeletionCount": 0,
"firstRun": false,
"polledAt": "2026-08-01T05:15:00.000Z",
"detectedAt": "2026-08-01T05:15:00.000Z"
}

type: "error" (uncharged; run still succeeds)

{
"type": "error",
"monitorId": "prod-alerts",
"channel": "https://t.me/+inviteHash",
"error": "Invalid channel input \"https://t.me/+inviteHash\" — use a public handle, @handle, or t.me link (not invite links).",
"errorCode": "invalid_channel",
"detectedAt": "2026-08-01T05:00:00.000Z"
}

errorCode values: invalid_channel, channel_not_found, channel_preview_disabled, fetch_failed, skipped_channel_limit, skipped_charge_limit, invalid_monitor_id, no_channels, input_overflow (more than 200 raw entries), monitor_busy (another live run holds this monitorId — see below), state_corrupt (snapshot state failed validation — polling stops, state untouched).

Concurrency guard (monitor_busy): each monitorId is protected by a server-side lock (Apify request-queue lock API — atomic and ownership-enforced, so exactly one of two overlapping runs can hold it, including takeover of a dead run's expired lock). The losing run records one uncharged monitor_busy error row and exits successfully — schedule ticks that overlap a still-running poll are skipped safely instead of double-billing the same delta. The lock is renewed with ownership verification during long runs and expires automatically (8 min) if a run dies; a run that can no longer verify ownership stops all new charges immediately. Each monitorId keeps one small named request queue (tcd-lock-…) as its lock.

Scheduling recipe

Use Apify Schedules with a fixed monitorId:

  • Cron: */15 * * * * (every 15 minutes)
  • Input: same channels + same monitorId every time
  • Idle cost ≈ start fee + one channel-poll per channel per tick; you only pay post-event when something actually changes

API curl recipe

curl "https://api.apify.com/v2/acts/kaz_kakyo~telegram-channel-delta/runs?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channels": ["telegram", "durov"],
"monitorId": "prod-alerts",
"scanDepth": 20,
"emitOnFirstSeen": true
}'

Then read the run's default dataset for post/poll/error rows.

MCP / agents

Call this Actor from Apify MCP or any agent that can start an Actor run and read a dataset. Keep monitorId stable across scheduled calls so the agent receives a clean delta stream (not full timelines). Error rows are uncharged and the run succeeds on bad input — safe for autonomous retries.

Honest limitations

  • Deletion window — deletions are detectable only while the post id is still inside the visible scanDepth window (~last N posts). Older history is truncated, not reported as deleted.
  • Deletion latency — a deletion is confirmed on the second consecutive poll where the post is missing (one extra poll interval of latency, by design, to avoid false positives).
  • Billing exactness — billing is exactly-once in normal operation, including Apify platform migrations (each billed row settles into the snapshot immediately). In the worst case of a hard crash between a charge and the next state write, the affected channel's current delta can be re-emitted (and re-billed) once on the next run.
  • Media-only edits — hash uses text + mediaType + media URL count. Replacing an image without changing type/count/text may be missed.
  • Markup drift — t.me public preview HTML can change; selectors may need updates.
  • Public preview only — private channels and channels with preview disabled cannot be polled (you get channel_preview_disabled / channel_not_found).
  • No user-level data — by design: channel posts only, never members/commenters/profiles/phones.

Pricing

EventPrice
Actor start (apify-actor-start)$0.005
Channel poll (channel-poll)$0.001
Post event (post-event)$0.001

Worked example: 5 channels every 15 minutes = 96 runs × ($0.005 start + 5 × $0.001 polls) ≈ $0.96/day idle, plus $0.001 per actual post event. Hourly polling of the same 5 channels ≈ $0.24/day.

FAQ

Why poll rows?
They prove a channel was checked even when nothing changed, and carry window bounds + counts for monitoring/health dashboards.

What happens on the first run?
With emitOnFirstSeen: true (default), every visible post is emitted as new (firstSeen: true) and a snapshot is saved. With false, only the poll row is emitted and the snapshot is baselined silently.

How does monitorId work?
Snapshots live in named KV store telegram-channel-delta-state under key monitor-<monitorId>. Different monitorIds never share state — use separate ids for separate jobs/customers.

Is this against Telegram ToS?
This Actor only fetches public t.me/s/ preview pages (the same HTML any browser can load). No account, no login, no private API, no member scraping.