# Telegram Scraper - Channel Posts, Keywords & IOCs, No Login (`ntriqpro/telegram-channel-osint`) Actor

Telegram scraper for public channels: posts, views, reactions and media, plus keyword hits, extracted indicators (CVE, IP, hash, wallet, domain, handle) and related channels. No login, no phone number.

- **URL**: https://apify.com/ntriqpro/telegram-channel-osint.md
- **Developed by:** [daehwan kim](https://apify.com/ntriqpro) (community)
- **Categories:** Social media, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Telegram Scraper - Channel Posts, Keywords & IOCs, No Login

**Give it public Telegram channel names and get back their posts — plus the indicators inside those posts (CVE, IP, hash, wallet, domain, handle), keyword hit counts and the related channels they forward from, link to and mention.** No login, no phone number, no cookies: it reads only the public channel preview.

| | Free plan (preview) | Paid Apify plan (Starter and up) |
|---|---|---|
| Channels per run | 2 | 500 |
| Posts per channel | the 10 newest (fewer when `maxPostsPerChannel` is below 10) | up to 50,000 (as far as the public preview reaches) |
| Post text | first 280 characters (`textTruncated: true`) | full text |
| Links, hashtags and mentions lists | from the shown text only (`linkCount` shows the real total) | complete |
| History (`lastNDays`, `sinceDate`, `before`) | locked | yes |
| New posts only (`incremental`) | locked | yes |
| Fetch posts by URL (`postUrls`) | locked | yes |
| Indicator rows (CVE, IP, hash, wallet, domain, handle…) | 1 of each kind + how many more were found | all |
| Related channel rows | 3 + how many more candidates were found | all (with name and subscriber count) |
| Keywords | 1 | all, including `onlyMatching` |
| Output format | full | full, minimal, rag |
| Channel check result (`channel` row) | both channels | all |
| Price per event | list price | BRONZE −20%, SILVER −35%, GOLD and up −48% |

**How big is the difference?** Measured on 2026-10-07: for `@thehackernews` a free run returns 10 posts and at most 3 indicators, while a paid run covers about 279 posts for the last 30 days (28×, projected from the channel's posting rate) and found 43 indicators in the 200 newest posts (14×). For `@rybar`: about 586 posts in 30 days (59×) and 36 related channels in 166 posts (12×, against 3). For `@cveNotify`: 247 indicators in 200 posts, 202 of them CVE IDs (41×, against 6).

A free run never fails because of a limit: it ends normally, tells you in the dataset what was shown and what stayed locked (`1 of 36 shown`), and ignores paid-only inputs with a notice instead of an error.

> **Unofficial.** This Actor is **not affiliated with, endorsed by, or sponsored by** Telegram. It reads **public** channel preview pages only. You are responsible for how you use the input and the results, for complying with Telegram's Terms of Service, and with GDPR, PIPA, CCPA and other applicable laws.

### Why use this Telegram scraper?

- **No account at risk.** It never asks for your Telegram login, phone number or session. Nothing to get logged out of, nothing to get banned.
- **Indicators as rows, not as a blob.** CVE IDs, IPv4 addresses, MD5/SHA-1/SHA-256 hashes, BTC/ETH/TRON wallet addresses, emails, phone numbers, domains, @mentions and t.me channel links are pulled out of the post text, deduplicated per channel and delivered one per row with how often and where each was seen.
- **Keyword answers, including "nothing found".** For every channel × keyword you get one row: posts scanned, matches, first and last match, top posts by views.
- **Related channels with one-hop details.** Channels a channel forwards from, links to or mentions — each looked up once for its name, subscriber count, verified badge and whether its preview is open.
- **It tells you why a channel is empty.** Every channel gets a `channel` row: is the preview open, how far back did the run reach, was the history cut.
- **Correct dates.** The post time is read from the post's date link only — never from a video's duration.

Built for threat-intelligence and OSINT monitoring, brand and crypto watch, and newsroom research. Runs on the Apify platform with API access, scheduling, integrations and monitoring built in.

### How to use it

1. Enter one or more **channels** (`telegram`, `@telegram` or `https://t.me/telegram`).
2. Optionally add **keywords**, a time window (`lastNDays` / `sinceDate`) and the indicator kinds you care about.
3. Click **Start** and read the rows in the **Output** tab. Filter by `recordType`.
4. For daily monitoring turn on **New posts only** with a **monitor key** and put the Actor on a schedule.

### Input

| Field | Type | Description |
|---|---|---|
| `channels` | array | Public channels: name, `@name` or t.me link. 1–500 per run |
| `postUrls` | array | Single posts by address, e.g. `https://t.me/telegram/100` (paid plans) |
| `maxPostsPerChannel` | integer | Posts to read per channel, newest first, 1–50,000. When omitted: 40 |
| `lastNDays` / `sinceDate` / `before` | integer / date / integer | Time window and starting post number (paid plans) |
| `keywords` | array | Case-insensitive partial match against post text |
| `onlyMatching` | boolean | Deliver only posts that match a keyword (paid plans). A channel is stopped after 100 pages (about 2,000 posts) in a row without a matching post (`stopReason: noRowsInWindow`) |
| `minViews` | integer | Skip posts with fewer views. Same 100-page stop as `onlyMatching` |
| `indicatorKinds` | array | `cve`, `ipv4`, `hash`, `wallet`, `email`, `phone`, `domain`, `mention`, `channel_link` (all when omitted) |
| `includeRelatedChannels` | boolean | Related channel rows with one-hop lookup (on when omitted) |
| `incremental` + `monitorKey` | boolean + string | Only posts newer than the last run with the same key (paid plans) |
| `outputFormat` | string | `full`, `minimal` or `rag` (formats other than `full`: paid plans) |
| `proxyConfiguration` | object | Apify Proxy; the default datacenter proxy is enough |

```json
{
  "channels": ["thehackernews", "cveNotify"],
  "maxPostsPerChannel": 500,
  "lastNDays": 30,
  "keywords": ["ransomware", "zero-day"],
  "indicatorKinds": ["cve", "ipv4", "hash", "domain"]
}
```

`maxPostsPerChannel` counts the posts the run reads; `keywords` with `onlyMatching`, `minViews` and the time window then decide which of those become rows.

### Output

Every row has a `recordType`: `post`, `channel`, `indicator`, `relatedChannel` or `keywordSummary`. Rows that carry a `noticeCode` instead are **messages, not results** (`free-plan-preview`, `free-plan-locked`, `charge-limit-reached`, `charge-unavailable`, `ledger-unreadable`, `ledger-unwritable`, `delivery-unconfirmed`, `restart-unconfirmed`, `lookup-failed`, `post-not-found`, `post-already-delivered`, `upstream-blocked`, `run-summary`, `invalid-input`, `no-input`) — they are never charged; skip them when parsing. Channel rows and notice rows carry a short `disclaimer` (unofficial, you are responsible for your use of the data). A paid run that has neither (only `postUrls`) ends with one `run-summary` row that carries it.

```json
{
  "recordType": "post",
  "platform": "telegram",
  "channelUsername": "thehackernews",
  "channelTitle": "The Hacker News",
  "postId": 10244,
  "dataPost": "thehackernews/10244",
  "postUrl": "https://t.me/thehackernews/10244",
  "postedAt": "2026-10-05T16:23:18.000Z",
  "text": "GitGuardian found 28.65 million new hardcoded secrets in public GitHub commits in 2025…",
  "textTruncated": false,
  "markdown": "GitGuardian found 28.65 million new hardcoded secrets…",
  "views": 5560,
  "viewsText": "5.56K",
  "reactions": [{ "emoji": "🔥", "count": 3 }],
  "reactionCount": 3,
  "hasMedia": true,
  "mediaTypes": ["document"],
  "photos": [],
  "videoUrl": null,
  "documentUrl": "https://t.me/thehackernews/10244",
  "documentName": "main.gif",
  "documentSize": "24 MB",
  "hasVoice": false,
  "poll": null,
  "linkPreview": null,
  "forwardedFrom": null,
  "isForwarded": false,
  "replyToUrl": null,
  "hasReply": false,
  "links": ["https://thehackernews.com/2026/10/the-credential-layer-is-expanding.html"],
  "hashtags": [],
  "mentions": [],
  "linkCount": 1,
  "matchedKeywords": [],
  "scrapedAt": "2026-10-07T03:00:00.000Z",
  "sourceUrl": "https://t.me/s/thehackernews"
}
```

```json
{
  "recordType": "indicator",
  "channelUsername": "thehackernews",
  "indicatorKind": "cve",
  "value": "CVE-2026-88772",
  "occurrences": 3,
  "firstSeenAt": "2026-09-29T10:02:11.000Z",
  "lastSeenAt": "2026-10-04T08:40:27.000Z",
  "postUrls": ["https://t.me/thehackernews/10231", "https://t.me/thehackernews/10198"]
}
```

```json
{
  "recordType": "channel",
  "channelUsername": "thehackernews",
  "channelTitle": "The Hacker News",
  "subscriberCount": 163000,
  "isVerified": true,
  "previewStatus": "open",
  "postsDelivered": 279,
  "postsScanned": 279,
  "newestPostId": 10257,
  "oldestReachedAt": "2026-09-07T05:11:42.000Z",
  "historyTruncated": false,
  "stopReason": "dateCutoff"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

### Data table

| Row | Key fields |
|---|---|
| `post` | `postId`, `dataPost`, `postUrl`, `postedAt`, `text`, `textTruncated`, `markdown`, `views`, `viewsText`, `reactions`, `reactionCount`, `hasMedia`, `mediaTypes`, `photos`, `videoUrl`, `videoDurationText`, `documentUrl`, `documentName`, `documentSize`, `hasVoice`, `voiceUrl`, `poll`, `linkPreview` (`url`, `headline`, `summary`, `siteName`, `imageUrl`), `forwardedFrom`, `forwardedFromUrl`, `isForwarded`, `replyToUrl`, `hasReply`, `links`, `hashtags`, `mentions`, `linkCount`, `isEdited`, `authorSignature`, `matchedKeywords` |
| `channel` | `channelUsername`, `channelTitle`, `channelBio`, `channelUrl`, `subscriberCount`, `photoCount`, `videoCount`, `fileCount`, `linkCount`, `isVerified`, `avatarUrl`, `previewStatus` (`open`, `no_preview`, `truncated`, `not_found`), `previewNote`, `postsDelivered`, `postsScanned`, `pagesFetched`, `newestPostId`, `newestPostAt`, `oldestReachedPostId`, `oldestReachedAt`, `historyTruncated`, `historyGap`, `stopReason`, `collectionError`, `serviceMessagesSkipped`, `incrementalMode`, `incrementalNote`, `savedLastPostId` |
| `indicator` | `indicatorKind`, `value`, `occurrences`, `firstSeenAt`, `lastSeenAt`, `postUrls` (up to 5), `hashAlgorithm` (hashes), `walletNetwork` (wallets) |
| `relatedChannel` | `relatedUsername`, `relatedUrl`, `relatedTitle`, `relatedBio`, `relatedSubscriberCount`, `relatedIsVerified`, `relatedPreviewStatus`, `relationKinds` (`forward`, `link`, `mention`), `occurrences`, `firstSeenAt`, `lastSeenAt`, `postUrls` |
| `keywordSummary` | `keyword`, `postsScanned`, `matchCount`, `firstMatchAt`, `lastMatchAt`, `topPosts` (up to 5 by views) |

All rows also carry `recordType`, `platform`, `channelUsername`, `scrapedAt` and `sourceUrl`. Hashtags and full URLs stay in the post row's arrays; they are not sold as indicator rows. Service messages ("Channel created", "Channel photo updated") are counted in the `channel` row and not delivered as posts.

### Pricing

Pay per event. The compute, proxy and storage a run uses are included in the event prices; Apify platform usage is not billed on top of them.

| Event | Charged when | Price (list) | Not charged |
|---|---|---|---|
| `apify-actor-start` | the run starts — once per run (memory is limited to 1 GB) | $0.003 | — |
| `channel-checked` | one channel's lookup is completed — including the answer that its preview is turned off or the name does not exist | $0.005 | our errors (timeouts, failed retries, parser errors, an answer that is not a Telegram page); the same channel entered twice |
| `post` | one post row is written | $0.004 | duplicate posts (also a `postUrls` entry the same run already delivered with its channel); notice rows |
| `indicator` | one indicator row, deduplicated per channel, is written | $0.004 | duplicates; notice rows |
| `related-channel` | one related channel row whose one-hop lookup was completed is written | $0.004 | lookups that failed on our side |
| `keyword-checked` | one channel × keyword result row is written, including zero matches | $0.004 | checks that failed on our side |

`apify-actor-start`: Charged once when a run starts. This Actor's memory is limited to 1 GB, so that is one charge per run (the platform adds one more charge for every 1 GB of memory above 1 GB). It costs $0.003. The start fee is the same on every plan tier.

Paid plan tiers pay less per event: BRONZE −20%, SILVER −35%, GOLD and up −48%.

Example: one channel with 279 post rows + 43 indicator rows + 1 channel check + start (once) = $1.30 at list price. Set a maximum charge per run in Run options to cap spending; when the cap is reached the run stops normally and says so in a `charge-limit-reached` row.

**Restarts and repeat charges.** When Apify moves a run to another server, or you abort a run and resurrect it, the run gets a signal, saves its own ledger and continues from it: rows already delivered are neither written nor charged again, and finished channels are not read again. A run that dies without such a signal (for example killed at its timeout) and is then resurrected continues from its ledger as well: a row is charged only after the ledger holding it was saved, so no row is charged a second time. A row that was being handed over at the very moment the run died (charged or not, written or not — the run cannot tell) is neither charged nor written again; a `restart-unconfirmed` row gives the count. If the ledger cannot be saved (3 tries), the run charges no further row and says so in a `ledger-unwritable` row. If a resurrected run cannot read its ledger and the dataset already has rows, it stops before requesting or charging anything and says so in a `ledger-unreadable` row: run again with the same input. If the dataset refuses a row that was already charged, the run reads the end of the dataset back and writes the row once more only when it is not there; if that fails too, the row is missing from the dataset, is not charged again, and the count is reported in the channel row's `collectionError` (or in a `run-summary` row). When the dataset cannot be read back, the row is not written a second time (to avoid a duplicate) and a `delivery-unconfirmed` row gives the count. From such a row on the run charges no further row: channels and post URLs it had not read yet are not requested. If the platform's charge call itself fails, that row is still delivered and the call is not made again; when it fails 3 times in a row (or 10 times in a run) the run stops charging and requesting and says so in a `charge-unavailable` row with the count of charges that could not be confirmed.

**Interrupted monitoring runs.** With `incremental: true`, a run that is reading new posts saves the last post it delivered when it gets a migration or abort signal and with every periodic state save of the platform. The next scheduled run starts after that post, so it does not charge those posts again even when the interrupted run is never resurrected. Two things remain. A run that dies without a signal (for example killed at its timeout) leaves the posts it delivered after its last periodic save to be delivered and charged again by the next run. And the indicator, related-channel and keyword rows of a channel are written after its posts, so for a channel that was interrupted they come only if that run is resurrected (it then reads the channel again from where it started, without charging delivered rows twice); a later run does not produce them for posts an earlier run delivered. A first run (baseline) that is interrupted by a signal saves no position: resurrect it to finish the baseline, otherwise the next run takes the baseline again and charges its posts again.

**Request discipline.** Requests to one channel are at least 1.1 s apart. Only 429, 5xx and dropped connections are retried (after 1, 2 and 4 s, or after the server's `Retry-After` when that is longer, up to 30 s). When 3 channels in a row cannot be read, the run stops asking, says so in an `upstream-blocked` row and ends normally; nothing is charged for what was not read.

### What it does not do

- **No member lists.** Members are only visible to logged-in accounts.
- **No private channels or groups, no invite links.** Only channels with a public preview. A channel whose preview is turned off is reported as `previewStatus: "no_preview"`.
- **No login of any kind.** It never asks for a phone number, code, session or cookies.
- **No full history guarantee.** Some channels expose only part of their history in the public preview. The `channel` row says how far the run reached (`oldestReachedAt`, `historyTruncated`).
- **No media downloads, translation or summaries.** Photo, video and file addresses are delivered as links.
- **No in-channel search.** Keywords are matched against the posts the run reads.

### Tips

- Start with `maxPostsPerChannel: 40` to see the shape of the data, then widen the window with `lastNDays`.
- For monitoring, schedule the Actor with `incremental: true` and one `monitorKey` per schedule — each run returns only the posts that appeared since the previous one (for runs that were interrupted, see "Interrupted monitoring runs" under Pricing). Deleted posts never cause a repeat: when the channel's newest post number is below the saved one the run reports "nothing new". A new baseline is taken only when it is more than 200 below the saved number (a different history). A run cut short by an error or by your charge limit saves the last post it delivered, and the next run continues from there. If part of the history between the saved post and the newest post cannot be read, the channel row says so in `historyGap` and `incrementalNote`.
- `indicatorKinds: ["cve", "ipv4", "hash", "domain"]` keeps a security feed focused; `["wallet", "domain", "channel_link"]` suits crypto watch.
- `mention` indicator values are plain handles — you can pass them to a username-lookup Actor in the Apify Store (for example Sherlock or Maigret based ones) as a next step.
- A `truncated` or `no_preview` channel is a fact about that channel, not a failed run.

### FAQ, disclaimers, and support

**Is this legal?** The Actor reads pages that Telegram serves publicly to anyone without an account. Whether a particular use of that data is lawful depends on what you collect and what you do with it. You are responsible for how you use the results. Do not use this tool for harassment, stalking, or any unlawful purpose.

**Privacy & data responsibility.** **You are the data controller** for every lookup you run with this Actor; ntriqpro is a **data processor** acting solely on your instructions. You are responsible for having a lawful basis and a legitimate, purpose-limited reason for each lookup, and for complying with GDPR, PIPA, CCPA, and other applicable privacy laws. **We do not store your results** — output is written only to your own run's dataset. With `incremental`, the last post number per channel is kept in a key-value store in your own account.

**Why did a channel return nothing?** Look at its `channel` row: `no_preview` means the owner turned the public preview off; `not_found` means there is no public channel with that name; `truncated` means the preview skips part of the history.

**Trademarks.** Telegram is a trademark of its owner. This Actor is an independent tool and uses the name only to describe what it reads.

**Found a bug or want a feature?** Use the **Issues** tab.

*Used this Actor? A sentence in the **Reviews** tab about what you used it for helps the next person decide.*

# Actor input Schema

## `channels` (type: `array`):

Public Telegram channels to read: a name, @name or a t.me link. Paid Apify plans read up to 500 channels per run with full history. Free plan runs are a preview: 2 channels, the 10 newest posts of each (text cut at 280 characters), 1 indicator of each kind, 3 related channels and 1 keyword. Charged per channel checked ($0.005) and per row delivered ($0.004).

## `postUrls` (type: `array`):

Single posts to fetch by address, for example https://t.me/telegram/100. Each post found is one post row. A post the same run already delivered with its channel is not charged a second time; a notice row says so. (paid Apify plans only — free plan runs ignore this and say so)

## `maxPostsPerChannel` (type: `integer`):

How many posts to read per channel, newest first (1 to 50,000, as far as the public preview reaches). Filters below apply to the posts read. Free plan runs return up to 10 newest posts per channel (fewer when this is below 10).

## `lastNDays` (type: `integer`):

Stop at posts older than this many days. (paid Apify plans only — free plan runs ignore this and say so)

## `sinceDate` (type: `string`):

Stop at posts older than this date (YYYY-MM-DD). (paid Apify plans only — free plan runs ignore this and say so)

## `before` (type: `integer`):

Start reading backwards from this post number instead of the newest post. (paid Apify plans only — free plan runs ignore this and say so)

## `keywords` (type: `array`):

Words or phrases to look for in the posts read (case-insensitive, partial match). Each channel × keyword gives one keyword result row with the match count, first and last match and the top posts, even when nothing matched. Free plan runs check the first keyword only.

## `onlyMatching` (type: `boolean`):

Deliver post rows only for posts that contain at least one keyword. A channel is stopped after 100 pages in a row without a matching post. (paid Apify plans only — free plan runs ignore this and say so)

## `minViews` (type: `integer`):

Skip posts with fewer views than this. A channel is stopped after 100 pages in a row without a matching post.

## `indicatorKinds` (type: `array`):

Which indicators to extract from post text as their own rows, deduplicated per channel: CVE IDs, IPv4 addresses, file hashes (MD5, SHA-1, SHA-256), wallet addresses (BTC, ETH, TRON), emails, phone numbers, domains, @mentions and t.me channel links. Free plan runs show 1 indicator of each kind and say how many more were found.

## `includeRelatedChannels` (type: `boolean`):

Collect the channels this channel forwards from, links to or mentions, and look each one up once for its name, subscriber count, verified badge and whether its preview is open. One row per related channel. Free plan runs show 3.

## `incremental` (type: `boolean`):

Remember the last post number per channel and return only newer posts on the next run with the same monitor key. Deleted posts are never delivered again; a new baseline is taken only when the channel's newest post number is more than 200 below the saved one. (paid Apify plans only — free plan runs ignore this and say so)

## `monitorKey` (type: `string`):

Name of the saved position used by "New posts only". Use a different key for each schedule that should keep its own position. (paid Apify plans only — free plan runs ignore this and say so)

## `outputFormat` (type: `string`):

Shape of the post rows: full (every field), minimal (address, date, text, views) or rag (Markdown text and links for LLM pipelines). Formats other than full work on paid plans. (paid Apify plans only — free plan runs ignore this and say so)

## `proxyConfiguration` (type: `object`):

Apify Proxy settings. The default datacenter proxy is enough for public channel previews.

## Actor input object example

```json
{
  "channels": [
    "telegram"
  ],
  "postUrls": [],
  "maxPostsPerChannel": 40,
  "keywords": [],
  "indicatorKinds": [
    "cve",
    "ipv4",
    "hash",
    "wallet",
    "email",
    "phone",
    "domain",
    "mention",
    "channel_link"
  ],
  "includeRelatedChannels": true,
  "outputFormat": "full",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "channels": [
        "telegram"
    ],
    "postUrls": [],
    "maxPostsPerChannel": 40,
    "keywords": [],
    "minViews": 0,
    "indicatorKinds": [
        "cve",
        "ipv4",
        "hash",
        "wallet",
        "email",
        "phone",
        "domain",
        "mention",
        "channel_link"
    ],
    "includeRelatedChannels": true,
    "outputFormat": "full",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ntriqpro/telegram-channel-osint").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "channels": ["telegram"],
    "postUrls": [],
    "maxPostsPerChannel": 40,
    "keywords": [],
    "minViews": 0,
    "indicatorKinds": [
        "cve",
        "ipv4",
        "hash",
        "wallet",
        "email",
        "phone",
        "domain",
        "mention",
        "channel_link",
    ],
    "includeRelatedChannels": True,
    "outputFormat": "full",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("ntriqpro/telegram-channel-osint").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "channels": [
    "telegram"
  ],
  "postUrls": [],
  "maxPostsPerChannel": 40,
  "keywords": [],
  "minViews": 0,
  "indicatorKinds": [
    "cve",
    "ipv4",
    "hash",
    "wallet",
    "email",
    "phone",
    "domain",
    "mention",
    "channel_link"
  ],
  "includeRelatedChannels": true,
  "outputFormat": "full",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call ntriqpro/telegram-channel-osint --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ntriqpro/telegram-channel-osint"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/I1wlglAiLTxOjDNra/builds/Wfc9BTsldNoqYYm4M/openapi.json
