# Telegram Email Scraper: Channel Admin & Ad Contact Finder (`datagleaner/telegram-channel-contacts`) Actor

Find the admin and advertising contact of any public Telegram channel, with subscribers and average views. $0.002 per channel with a contact. Admin @usernames, ad bots, emails, booking links, posting rate, and similar channels by snowball. No login.

- **URL**: https://apify.com/datagleaner/telegram-channel-contacts.md
- **Developed by:** [Data Gleaner](https://apify.com/datagleaner) (community)
- **Categories:** Lead generation, Integrations, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 channel with contacts

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 Channel Contacts: Admin & Ad Contact Finder

Find the admin and advertising contact of any public Telegram channel, with subscribers and average views. $0.002 per channel with a contact; channels without one are free, and there is no start fee.

It answers "telegram channel contacts", "telegram channel admin contact", "telegram advertising contacts" and "telegram channel owner": give it channel usernames or t.me links and get, per channel, the **admin @usernames, the ad-booking contact or bot, emails, websites and ad-exchange links**, plus the numbers an ad buyer prices on: **subscribers, average views per post, views per subscriber, posts per week and the last post date**. Turn on `findSimilar` and it snowballs through the channels each one forwards from or mentions, so one seed channel becomes a list of placements in the same niche. No login, no Telegram account, no API key.

**To try it:** paste a few channels into `channels` and click Start, or run it empty to see 5 example channels.

### What it does

For each channel it reads two public pages:

1. **`t.me/s/<channel>`**, the channel's web preview: the description and the most recent posts (`postsToScan`, default 20), with their views and dates.
2. **`t.me/<channel>`**: the exact subscriber count (the preview rounds it to 3.11M), and the description for channels that have the preview turned off.

Contacts are read line by line. A handle, email or link counts as a contact when its line, or the label line just above it, carries a contact word: ads, advertising, promo, PR, booking, collab, cooperation, business, contact, DM, admin, manager; реклама, сотрудничество, по вопросам, для связи, админ; 广告, 商务, 合作, 联系; تبلیغات, ارتباط; reklam, werbung, publicidad and more. "По вопросам рекламы @marina_mousse" and "For business inquiries DM: @airdropinspect" both land in `adContacts`, with the line itself as `context`.

Each handle is then classified from its own t.me page (at most 6 lookups per channel, cached across the run):

- a **personal account** goes to `adminUsernames`;
- a **bot** (Telegram bot names end in "bot") goes to `bots`, and to `adContacts` when its line is a contact line, such as an ad-booking bot;
- **another channel or group** goes to `mentionedChannels`, never to the contacts. The channel's own handle is skipped.

Posts marked as someone else's ad (Russia's `erid` label, an advertiser's ИНН, `#реклама`, `#ad`) are not read for contacts, since a handle there belongs to the advertiser. In posts, only short contact lines count, and only ad-exchange links (telega.in, collaborator.pro and others), so a news story about advertising does not produce a contact.

### Use cases

- **Ad buyers and media buyers**: get the person or bot to book a post with, next to the reach you are paying for.
- **Crypto, Web3 and app marketers**: build a list of placements in one niche from a few seed channels with `findSimilar`, then filter by `minSubscribers`, `avgViews` and `language`.
- **Telegram ad agencies**: refresh the admin contact of every channel in a catalogue; admins change handles often.
- **AI agents**: "who do I contact to advertise on these Telegram channels, and what reach do they have?" in one call, paying only for channels that have an answer.

### Input

| Field | Meaning |
|---|---|
| `channels` | Usernames, `@usernames` or t.me links (`tproger`, `@mash`, `https://t.me/s/airdropinspector`). Public groups work too. |
| `findSimilar` | Also process the channels forwarded from or @mentioned in the scanned posts and descriptions (default false). |
| `postsToScan` | Recent posts read per channel (default 20, max 100). About 20 posts per request. |
| `maxChannels` | With `findSimilar`, stop adding similar channels once this many channel rows are out (default 50). Your own channels are always all processed. |
| `minSubscribers` | Skip smaller channels. They are not output and not charged. |
| `requestDelaySecs` | Pause between requests to t.me (default 1). |
| `proxyConfiguration` | Optional; no proxy by default. |

```json
{
  "channels": ["mash", "tproger", "airdropinspector"],
  "findSimilar": true,
  "maxChannels": 100,
  "minSubscribers": 10000
}
```

With no channels at all, it runs 5 example channels and says so in the status message.

### Output

One row per channel. Real row for `t.me/mash` (description shortened):

```json
{
  "username": "mash",
  "url": "https://t.me/mash",
  "title": "Mash",
  "description": "Прислать новость, фото, видео: @inmash1_bot\n\nПо вопросам рекламы @marina_mousse\n...",
  "subscribers": 3108503,
  "verified": true,
  "isChannel": true,
  "type": "channel",
  "hasWebPreview": true,
  "avgViews": 219020,
  "viewsPerSubscriber": 0.07,
  "postsPerWeek": 189.0,
  "lastPostAt": "2026-10-09T14:51:32Z",
  "postsScanned": 20,
  "language": "ru",
  "hasContact": true,
  "adminUsernames": ["marina_mousse"],
  "adContacts": [
    {
      "handle": "marina_mousse",
      "url": "https://t.me/marina_mousse",
      "type": "user",
      "isAdContact": true,
      "context": "По вопросам рекламы @marina_mousse",
      "source": "description",
      "postUrl": null
    }
  ],
  "bots": ["inmash1_bot"],
  "emails": [],
  "websites": [],
  "adBookingLinks": [],
  "directMessagesLink": null,
  "adOfferPosts": [],
  "contactSource": "description",
  "mentionedChannels": ["lab_mash", "mashnasporte", "masx3_0"],
  "avatarUrl": "https://cdn4.telesco.pe/file/...",
  "foundVia": null,
  "status": "ok",
  "scrapedAt": "2026-10-09T15:39:10Z",
  "input": "mash"
}
```

Field notes:

- `adContacts[].type` is `user`, `bot` or `unknown` (not classified within the lookup cap); `isAdContact` is true when the line is about ads specifically, false for a general contact line ("DM", "по всем вопросам").
- `emails` come from the description and from contact lines in posts (`ads@varlamov.me`, `advertise@cointelegraph.com`).
- `adBookingLinks` are ad-exchange links and the channel's own advertising pages (`https://forklog.com/advertisement/`). `websites` are its other links.
- `directMessagesLink` is set when the channel advertises Telegram's channel direct messages (`t.me/<channel>?direct`).
- `adOfferPosts` lists recent posts that look like an ad price list (a "прайс" or "rate card" with a price in it).
- `postsPerWeek` is the scanned posts divided by the time from the oldest of them to now. `language` is an ISO 639-1 guess from the text.
- A channel with its web preview off has `hasWebPreview: false`: its description and exact subscribers are read, the post stats are empty.
- An input that is a personal account, a bot or no such username gets a free row with `status: "not a channel"`.

The dataset has three views: "Channel contacts", "All contact lines" (one row per handle with its line) and "Similar channels".

### Pricing

Pay per answer, one event:

- **`channel-with-contact`: $0.002** for each channel where an admin or ad contact handle, an email or an ad-booking link was found.
- A channel with **no contact is free**. It is still returned with its subscribers, views and mentioned channels.
- Rows for inputs that are not channels are free.
- No start fee and no per-post fee: the recent posts read for contacts and views are included.

**Worked example.** You check 500 channels in a niche and 60% show a contact: 300 x $0.002 = **$0.60**. The other 200 cost nothing. With `findSimilar` on and `maxChannels: 1000`, a run that finds 600 channels with a contact costs $1.20.

Set a maximum total charge on the run and the Actor stops cleanly when it is reached.

### What to expect

Measured on a local run of 20 real channels (9 crypto, 7 news, 4 marketing and tech), 20 posts each: **13 of 20 (65%) had a contact**, in 67 requests and about 1 minute 45 seconds. 8 named an admin account or contact bot, 4 an email (`ads@varlamov.me`, `advertise@cointelegraph.com`), 1 an advertising page. The 7 without one were mostly official channels of large brands (Pavel Durov, the New York Times, Whale Alert, RIA) that publish no ad contact, plus one near-empty channel. A `findSimilar` run seeded with 2 channels returned 7, 6 of them with a contact.

### Limits

- Only what the channel publishes: its description and its recent posts. No private channels, no members list, no phone numbers.
- At most 6 handle lookups per channel. A handle on a contact line that was not looked up is still returned, as `type: "unknown"`.
- Discovery is by snowball only (forwards and @mentions); there is no keyword search of all of Telegram.
- Channels with the web preview turned off give their description and subscribers but no post stats.
- `language` is a guess from the scripts and common words in the text.

### Run it after a Telegram scraper

Open the source Actor's **Integrations** tab, add this Actor (`datagleaner/telegram-channel-contacts`) and set its input to:

```json
{"datasetId": "{{resource.defaultDatasetId}}"}
```

Every time the source run succeeds, this Actor reads the channels from its results and looks up the admin and ad contact of each. The channel is taken from the first of these fields that holds a usable value: `channel`, `channelLink`, `channelUrl`, `chat_username`, `username`, or the t.me link in `url`, `postUrl`, `source_url` or `link` (also inside a nested `message`); `datasetField` names another field and is tried first. Post links such as `t.me/<channel>/123` give the channel, sender fields (`fromUsername`, `sender_username`) are ignored because they are people, and invite links, private `t.me/c/...` links and items with no channel are skipped. Duplicates are merged and at most `maxDatasetItems` (default 1000) channels are read. The run log and status line say how many items were read and how many were skipped. Channels from the dataset combine with `channels`, and billing is the same: one event per channel with a contact. Compatible with the output of the Telegram scrapers on the Store that return a channel or post link per row (for example `webfinity/telegram-channel-content-media-scraper-v2`, `truefetch/telegram-channel-message`, `tri_angle/telegram-scraper`) and with the `datagleaner/telegram-channels` Actor.

The same works from code, for one finished run:

````python
run = client.actor("datagleaner/telegram-channel-contacts").call(run_input={"datasetId": "SOURCE_DATASET_ID"})
``` It also works as an integration: open the source Actor's Integrations tab, choose Add integration and pick this Actor, and the dataset is passed automatically.

### Use with Python

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("datagleaner/telegram-channel-contacts").call(run_input={
    "channels": ["mash", "tproger", "airdropinspector"],
    "findSimilar": True,
    "maxChannels": 20,  # at most 20 x $0.002 = $0.04
})
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item["url"], item.get("subscribers"), item.get("avgViews"), item.get("adminUsernames"), item.get("emails"))
````

### Use with JavaScript / Node.js

```js
// npm install apify-client
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datagleaner/telegram-channel-contacts').call({
    channels: ['mash', 'tproger'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const item of items) {
    console.log(item.url, item.subscribers, item.adContacts.map((c) => `${c.handle}: ${c.context}`));
}
```

### Use it from n8n, Make, Zapier or an AI agent

Actor ID: `datagleaner/telegram-channel-contacts`

Minimal input:

```
{"channels": ["mash", "tproger"]}
```

Each tool below runs this Actor with your own Apify API token.

- **n8n:** add the **Apify** node (`@apify/n8n-nodes-apify`). On n8n Cloud you install it from the community node registry. Choose **Run an Actor and get dataset**, set Actor to `datagleaner/telegram-channel-contacts` and paste the input above.
- **Make:** use the Apify app's **Run an Actor** module, then **Get Dataset Items** to read the results. **Watch Actor Runs** can trigger a scenario when a run finishes.
- **Zapier:** use the Apify action **Run Actor**, then the search **Fetch dataset items**. The trigger **Finished Actor run** starts a Zap when a run ends.
- **AI agents (MCP):** connect to `https://mcp.apify.com/?tools=datagleaner/telegram-channel-contacts`. Without a token, an agent can still find Actors with the `search-actors` tool, but running this one needs your Apify account. In Claude Code:

  ```
    claude mcp add --transport http apify "https://mcp.apify.com/?tools=datagleaner/telegram-channel-contacts"
  ```

  Then run `/mcp` to sign in to Apify in your browser. Then ask in plain language, for example: "Find who to contact to buy an ad on these Telegram channels: t.me/mash, t.me/tproger, t.me/airdropinspector. Give me a table with subscribers, average views, the admin or ad contact and the line it was found on."
- **LangChain (Python):**

```python
## pip install langchain-apify, then set APIFY_TOKEN in your environment
import json
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("datagleaner/telegram-channel-contacts")
result = tool.invoke({"run_input": json.loads('{"channels": ["mash", "tproger"]}')})
```

### FAQ

**How do I find the admin of a Telegram channel?** Put the channel in `channels`. The Actor reads the description and recent posts for lines such as "Admin: @name", "For ads: @name" or "По вопросам рекламы @name", checks that the handle is a personal account and returns it in `adminUsernames`, with the exact line in `adContacts`.

**How do I find who to contact to advertise on a Telegram channel?** Same run. `adContacts` with `isAdContact: true` is the ad contact the channel names; ad-booking bots are included, and `adBookingLinks` holds telega.in, collaborator.pro or the channel's own advertising page.

**Can it find similar Telegram channels to advertise on?** Turn on `findSimilar`. Channels are added from the forwards and @mentions in each channel's posts, which in practice are its sister channels and the channels it cross-promotes with. Filter with `minSubscribers`.

**Does it give the real reach, not just subscribers?** Yes: `avgViews` over the scanned posts and `viewsPerSubscriber`, the numbers ad prices are set on.

**Does it need a Telegram account or API key?** No. It reads Telegram's public web pages.

**Does it find phone numbers?** No. It returns only the public contact routes the channel itself publishes.

**I need the posts themselves.** Use [Telegram Channel Scraper](https://apify.com/datagleaner/telegram-channel-scraper) for messages with views, reactions and media.

### Responsible use

This Actor reads only public pages that Telegram serves to anyone without logging in, and returns only the contact routes a channel publishes for people to reach it. Usernames are personal data in many jurisdictions: you are responsible for having a lawful basis to store and use them, for respecting each channel's stated contact preferences, and for complying with anti-spam laws and Telegram's terms when you reach out.

# Actor input Schema

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

Public channels as @usernames, usernames or t.me links (`@tproger`, `t.me/mash`, `https://t.me/s/airdropinspector`). Public groups work too. Leave empty to run 5 example channels.

## `datasetId` (type: `string`):

Optional. Read channels from this dataset, for example the results of a Telegram channel or message scraper, and add them to the channels above. Each item's channel (`channel`, `channelLink`, `chat_username`, `username`, or the t.me link in `url`, `postUrl`, `source_url`, `link`) is used; items without one are skipped. In an integration use `{{resource.defaultDatasetId}}`.

## `datasetField` (type: `string`):

Optional. Name of the field that holds the channel username or t.me link when it is not one of the usual ones. Tried first.

## `maxDatasetItems` (type: `integer`):

Stop after this many unique channels from the dataset.

## `findSimilar` (type: `boolean`):

Also process the channels that your channels forward from or @mention in their recent posts, snowballing outward until the output holds `maxChannels` rows. A cheap way to build a list of ad placements in one niche.

## `postsToScan` (type: `integer`):

How many recent posts are read per channel for ad contacts, price-list posts, average views and posting rate. About 20 posts per request.

## `maxChannels` (type: `integer`):

With Find similar on, the run stops adding similar channels once this many channel rows are out. Your own channels are always all processed.

## `minSubscribers` (type: `integer`):

Skip channels with fewer subscribers (or group members). Skipped channels are not output and not charged.

## `requestDelaySecs` (type: `number`):

Pause between requests to t.me, with a little random jitter. Keep it at 1 or more on large runs.

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

Optional. Defaults to no proxy: t.me serves Apify's own IPs fine. Add a proxy only if a very large run starts seeing errors.

## Actor input object example

```json
{
  "channels": [
    "tproger",
    "airdropinspector",
    "mash"
  ],
  "maxDatasetItems": 1000,
  "findSimilar": false,
  "postsToScan": 20,
  "maxChannels": 50,
  "requestDelaySecs": 1
}
```

# Actor output Schema

## `results` (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": [
        "tproger",
        "airdropinspector",
        "mash"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datagleaner/telegram-channel-contacts").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": [
        "tproger",
        "airdropinspector",
        "mash",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("datagleaner/telegram-channel-contacts").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": [
    "tproger",
    "airdropinspector",
    "mash"
  ]
}' |
apify call datagleaner/telegram-channel-contacts --silent --output-dataset

```

## MCP server setup

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

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/4WgynuaR4PkFvdB6l/builds/orG08UDmg9UM29LUI/openapi.json
